Merge from emacs-23
[bpt/emacs.git] / lisp / eshell / esh-test.el
1 ;;; esh-test.el --- Eshell test suite
2
3 ;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
4 ;; 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
5
6 ;; Author: John Wiegley <johnw@gnu.org>
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;; The purpose of this module is to verify that Eshell works as
26 ;; expected. To run it on your system, use the command
27 ;; \\[eshell-test].
28
29 ;;; Code:
30
31 (eval-when-compile
32 (require 'eshell)
33 (require 'esh-util))
34 (require 'esh-mode)
35
36 (defgroup eshell-test nil
37 "This module is meant to ensure that Eshell is working correctly."
38 :tag "Eshell test suite"
39 :group 'eshell)
40
41 ;;; User Variables:
42
43 (defface eshell-test-ok
44 '((((class color) (background light)) (:foreground "Green" :bold t))
45 (((class color) (background dark)) (:foreground "Green" :bold t)))
46 "The face used to highlight OK result strings."
47 :group 'eshell-test)
48 (define-obsolete-face-alias 'eshell-test-ok-face 'eshell-test-ok "22.1")
49
50 (defface eshell-test-failed
51 '((((class color) (background light)) (:foreground "OrangeRed" :bold t))
52 (((class color) (background dark)) (:foreground "OrangeRed" :bold t))
53 (t (:bold t)))
54 "The face used to highlight FAILED result strings."
55 :group 'eshell-test)
56 (define-obsolete-face-alias 'eshell-test-failed-face 'eshell-test-failed "22.1")
57
58 (defcustom eshell-show-usage-metrics nil
59 "If non-nil, display different usage metrics for each Eshell command."
60 :set (lambda (symbol value)
61 (if value
62 (add-hook 'eshell-mode-hook 'eshell-show-usage-metrics)
63 (remove-hook 'eshell-mode-hook 'eshell-show-usage-metrics))
64 (set symbol value))
65 :type '(choice (const :tag "No metrics" nil)
66 (const :tag "Cons cells consumed" t)
67 (const :tag "Time elapsed" 0))
68 :group 'eshell-test)
69
70 ;;; Code:
71
72 (defvar test-buffer)
73
74 (defun eshell-insert-command (text &optional func)
75 "Insert a command at the end of the buffer."
76 (goto-char eshell-last-output-end)
77 (insert-and-inherit text)
78 (funcall (or func 'eshell-send-input)))
79
80 (defun eshell-match-result (regexp)
81 "Insert a command at the end of the buffer."
82 (goto-char eshell-last-input-end)
83 (looking-at regexp))
84
85 (defun eshell-command-result-p (text regexp &optional func)
86 "Insert a command at the end of the buffer."
87 (eshell-insert-command text func)
88 (eshell-match-result regexp))
89
90 (defvar eshell-test-failures nil)
91
92 (defun eshell-run-test (module funcsym label command)
93 "Test whether FORM evaluates to a non-nil value."
94 (when (let ((sym (intern-soft (concat "eshell-" (symbol-name module)))))
95 (or (memq sym (eshell-subgroups 'eshell))
96 (eshell-using-module sym)))
97 (with-current-buffer test-buffer
98 (insert-before-markers
99 (format "%-70s " (substring label 0 (min 70 (length label)))))
100 (insert-before-markers " ....")
101 (eshell-redisplay))
102 (let ((truth (eval command)))
103 (with-current-buffer test-buffer
104 (delete-char -6)
105 (insert-before-markers
106 "[" (let (str)
107 (if truth
108 (progn
109 (setq str " OK ")
110 (put-text-property 0 6 'face 'eshell-test-ok str))
111 (setq str "FAILED")
112 (setq eshell-test-failures (1+ eshell-test-failures))
113 (put-text-property 0 6 'face 'eshell-test-failed str))
114 str) "]")
115 (add-text-properties (line-beginning-position) (point)
116 (list 'test-func funcsym))
117 (eshell-redisplay)))))
118
119 (defun eshell-test-goto-func ()
120 "Jump to the function that defines a particular test."
121 (interactive)
122 (let ((fsym (get-text-property (point) 'test-func)))
123 (when fsym
124 (let* ((def (symbol-function fsym))
125 (library (locate-library (symbol-file fsym 'defun)))
126 (name (substring (symbol-name fsym)
127 (length "eshell-test--")))
128 (inhibit-redisplay t))
129 (find-file library)
130 (goto-char (point-min))
131 (re-search-forward (concat "^(eshell-deftest\\s-+\\w+\\s-+"
132 name))
133 (beginning-of-line)))))
134
135 (defun eshell-run-one-test (&optional arg)
136 "Jump to the function that defines a particular test."
137 (interactive "P")
138 (let ((fsym (get-text-property (point) 'test-func)))
139 (when fsym
140 (beginning-of-line)
141 (delete-region (point) (line-end-position))
142 (let ((test-buffer (current-buffer)))
143 (set-buffer (let ((inhibit-redisplay t))
144 (save-window-excursion (eshell t))))
145 (funcall fsym)
146 (unless arg
147 (kill-buffer (current-buffer)))))))
148
149 ;;;###autoload
150 (defun eshell-test (&optional arg)
151 "Test Eshell to verify that it works as expected."
152 (interactive "P")
153 (let* ((begin (float-time))
154 (test-buffer (get-buffer-create "*eshell test*")))
155 (set-buffer (let ((inhibit-redisplay t))
156 (save-window-excursion (eshell t))))
157 (with-current-buffer test-buffer
158 (erase-buffer)
159 (setq major-mode 'eshell-test-mode)
160 (setq mode-name "EShell Test")
161 (set (make-local-variable 'eshell-test-failures) 0)
162 (local-set-key [(control ?c) (control ?c)] 'eshell-test-goto-func)
163 (local-set-key [(control ?c) (control ?r)] 'eshell-run-one-test)
164 (local-set-key [(control ?m)] 'eshell-test-goto-func)
165 (local-set-key [return] 'eshell-test-goto-func)
166
167 (insert "Testing Eshell under " (emacs-version))
168 (switch-to-buffer test-buffer)
169 (delete-other-windows))
170 (eshell-for funcname (sort (all-completions "eshell-test--"
171 obarray 'functionp)
172 'string-lessp)
173 (with-current-buffer test-buffer
174 (insert "\n"))
175 (funcall (intern-soft funcname)))
176 (with-current-buffer test-buffer
177 (insert (format "\n\n--- %s --- (completed in %d seconds)\n"
178 (current-time-string)
179 (- (float-time) begin)))
180 (message "Eshell test suite completed: %s failure%s"
181 (if (> eshell-test-failures 0)
182 (number-to-string eshell-test-failures)
183 "No")
184 (if (= eshell-test-failures 1) "" "s"))))
185 (goto-char eshell-last-output-end)
186 (unless arg
187 (kill-buffer (current-buffer))))
188
189
190 (defvar eshell-metric-before-command 0)
191 (defvar eshell-metric-after-command 0)
192
193 (defun eshell-show-usage-metrics ()
194 "If run at Eshell mode startup, metrics are shown after each command."
195 (set (make-local-variable 'eshell-metric-before-command)
196 (if (eq eshell-show-usage-metrics t)
197 0
198 (current-time)))
199 (set (make-local-variable 'eshell-metric-after-command)
200 (if (eq eshell-show-usage-metrics t)
201 0
202 (current-time)))
203
204 (add-hook 'eshell-pre-command-hook
205 (function
206 (lambda ()
207 (setq eshell-metric-before-command
208 (if (eq eshell-show-usage-metrics t)
209 (car (memory-use-counts))
210 (current-time))))) nil t)
211
212 (add-hook 'eshell-post-command-hook
213 (function
214 (lambda ()
215 (setq eshell-metric-after-command
216 (if (eq eshell-show-usage-metrics t)
217 (car (memory-use-counts))
218 (current-time)))
219 (eshell-interactive-print
220 (concat
221 (int-to-string
222 (if (eq eshell-show-usage-metrics t)
223 (- eshell-metric-after-command
224 eshell-metric-before-command 7)
225 (- (float-time
226 eshell-metric-after-command)
227 (float-time
228 eshell-metric-before-command))))
229 "\n"))))
230 nil t))
231
232 (provide 'esh-test)
233
234 ;;; esh-test.el ends here