Commit | Line | Data |
---|---|---|
60370d40 | 1 | ;;; esh-test.el --- Eshell test suite |
affbf647 | 2 | |
acaf905b | 3 | ;; Copyright (C) 1999-2012 Free Software Foundation, Inc. |
affbf647 | 4 | |
7de5b421 GM |
5 | ;; Author: John Wiegley <johnw@gnu.org> |
6 | ||
affbf647 GM |
7 | ;; This file is part of GNU Emacs. |
8 | ||
4ee57b2a | 9 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
affbf647 | 10 | ;; it under the terms of the GNU General Public License as published by |
4ee57b2a GM |
11 | ;; the Free Software Foundation, either version 3 of the License, or |
12 | ;; (at your option) any later version. | |
affbf647 GM |
13 | |
14 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | ;; GNU General Public License for more details. | |
18 | ||
19 | ;; You should have received a copy of the GNU General Public License | |
4ee57b2a | 20 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
affbf647 | 21 | |
affbf647 GM |
22 | ;;; Commentary: |
23 | ||
24 | ;; The purpose of this module is to verify that Eshell works as | |
25 | ;; expected. To run it on your system, use the command | |
26 | ;; \\[eshell-test]. | |
27 | ||
28 | ;;; Code: | |
29 | ||
00c3ec76 | 30 | (eval-when-compile |
81fb60b2 | 31 | (require 'cl) ; assert |
00c3ec76 GM |
32 | (require 'eshell) |
33 | (require 'esh-util)) | |
affbf647 GM |
34 | (require 'esh-mode) |
35 | ||
00c3ec76 GM |
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 | ||
affbf647 GM |
41 | ;;; User Variables: |
42 | ||
958e6876 | 43 | (defface eshell-test-ok |
affbf647 GM |
44 | '((((class color) (background light)) (:foreground "Green" :bold t)) |
45 | (((class color) (background dark)) (:foreground "Green" :bold t))) | |
ec60da52 | 46 | "The face used to highlight OK result strings." |
affbf647 | 47 | :group 'eshell-test) |
2fb1ec93 | 48 | (define-obsolete-face-alias 'eshell-test-ok-face 'eshell-test-ok "22.1") |
affbf647 | 49 | |
958e6876 | 50 | (defface eshell-test-failed |
affbf647 GM |
51 | '((((class color) (background light)) (:foreground "OrangeRed" :bold t)) |
52 | (((class color) (background dark)) (:foreground "OrangeRed" :bold t)) | |
53 | (t (:bold t))) | |
ec60da52 | 54 | "The face used to highlight FAILED result strings." |
affbf647 | 55 | :group 'eshell-test) |
2fb1ec93 | 56 | (define-obsolete-face-alias 'eshell-test-failed-face 'eshell-test-failed "22.1") |
affbf647 GM |
57 | |
58 | (defcustom eshell-show-usage-metrics nil | |
ec60da52 | 59 | "If non-nil, display different usage metrics for each Eshell command." |
affbf647 GM |
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 | ||
1a32899d | 72 | (defvar test-buffer) |
affbf647 GM |
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 | |
d355a0b7 | 104 | (delete-char -6) |
affbf647 GM |
105 | (insert-before-markers |
106 | "[" (let (str) | |
107 | (if truth | |
108 | (progn | |
109 | (setq str " OK ") | |
958e6876 | 110 | (put-text-property 0 6 'face 'eshell-test-ok str)) |
affbf647 GM |
111 | (setq str "FAILED") |
112 | (setq eshell-test-failures (1+ eshell-test-failures)) | |
958e6876 | 113 | (put-text-property 0 6 'face 'eshell-test-failed str)) |
affbf647 GM |
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)) | |
c044263b | 125 | (library (locate-library (symbol-file fsym 'defun))) |
affbf647 GM |
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") | |
73171bd4 | 153 | (let* ((begin (float-time)) |
affbf647 GM |
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 | ||
97dad9d3 | 167 | (insert "Testing Eshell under " (emacs-version)) |
affbf647 GM |
168 | (switch-to-buffer test-buffer) |
169 | (delete-other-windows)) | |
a9eeff78 | 170 | (dolist (funcname (sort (all-completions "eshell-test--" |
dace60cf | 171 | obarray 'functionp) |
a9eeff78 | 172 | 'string-lessp)) |
affbf647 GM |
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) | |
73171bd4 | 179 | (- (float-time) begin))) |
affbf647 GM |
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 | ||
affbf647 GM |
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 | ||
affbf647 GM |
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) | |
73171bd4 | 225 | (- (float-time |
affbf647 | 226 | eshell-metric-after-command) |
73171bd4 | 227 | (float-time |
affbf647 GM |
228 | eshell-metric-before-command)))) |
229 | "\n")))) | |
230 | nil t)) | |
231 | ||
81fb60b2 GM |
232 | |
233 | ;;; The tests. | |
234 | ||
235 | (defmacro eshell-deftest (module name label &rest forms) | |
236 | (declare (indent 2)) | |
237 | (if (and (fboundp 'cl-compiling-file) (cl-compiling-file)) | |
238 | nil | |
239 | (let ((fsym (intern (concat "eshell-test--" (symbol-name name))))) | |
240 | `(eval-when-compile | |
241 | (ignore | |
242 | (defun ,fsym () ,label | |
243 | (eshell-run-test (quote ,module) (quote ,fsym) ,label | |
244 | (quote (progn ,@forms))))))))) | |
245 | ||
246 | ||
247 | (eshell-deftest mode same-window-buffer-names | |
248 | "`eshell-buffer-name' is a member of `same-window-buffer-names'" | |
249 | (member eshell-buffer-name same-window-buffer-names)) | |
250 | ||
251 | (eshell-deftest mode eshell-directory-exists | |
252 | "`eshell-directory-name' exists and is writable" | |
253 | (file-writable-p eshell-directory-name)) | |
254 | ||
255 | (eshell-deftest mode eshell-directory-modes | |
256 | "`eshell-directory-name' has correct access protections" | |
257 | (or (eshell-under-windows-p) | |
258 | (= (file-modes eshell-directory-name) | |
259 | eshell-private-directory-modes))) | |
260 | ||
261 | (eshell-deftest mode simple-command-result | |
262 | "`eshell-command-result' works with a simple command." | |
263 | (= (eshell-command-result "+ 1 2") 3)) | |
264 | ||
265 | ||
266 | (require 'em-banner) | |
267 | ||
268 | (eshell-deftest banner banner-displayed | |
269 | "Startup banner is displayed at point-min" | |
270 | (assert eshell-banner-message) | |
271 | (let ((msg (eval eshell-banner-message))) | |
272 | (assert msg) | |
273 | (goto-char (point-min)) | |
274 | (looking-at msg))) | |
275 | ||
276 | ||
277 | (require 'esh-cmd) | |
278 | ||
279 | (eshell-deftest var last-result-var | |
280 | "\"last result\" variable" | |
281 | (eshell-command-result-p "+ 1 2; + $$ 2" "3\n5\n")) | |
282 | ||
283 | (eshell-deftest var last-result-var2 | |
284 | "\"last result\" variable" | |
285 | (eshell-command-result-p "+ 1 2; + $$ $$" "3\n6\n")) | |
286 | ||
287 | (eshell-deftest var last-arg-var | |
288 | "\"last arg\" variable" | |
289 | (eshell-command-result-p "+ 1 2; + $_ 4" "3\n6\n")) | |
290 | ||
291 | (eshell-deftest cmd lisp-command | |
292 | "Evaluate Lisp command" | |
293 | (eshell-command-result-p "(+ 1 2)" "3")) | |
294 | ||
295 | (eshell-deftest cmd lisp-command-args | |
296 | "Evaluate Lisp command (ignore args)" | |
297 | (eshell-command-result-p "(+ 1 2) 3" "3")) | |
298 | ||
299 | (eshell-deftest cmd subcommand | |
300 | "Run subcommand" | |
301 | (eshell-command-result-p "{+ 1 2}" "3\n")) | |
302 | ||
303 | (eshell-deftest cmd subcommand-args | |
304 | "Run subcommand (ignore args)" | |
305 | (eshell-command-result-p "{+ 1 2} 3" "3\n")) | |
306 | ||
307 | (eshell-deftest cmd subcommand-lisp | |
308 | "Run subcommand + Lisp form" | |
309 | (eshell-command-result-p "{(+ 1 2)}" "3\n")) | |
310 | ||
311 | (eshell-deftest cmd named-command | |
312 | "Execute named command" | |
313 | (eshell-command-result-p "+ 1 2" "3\n")) | |
314 | ||
315 | ||
316 | (require 'esh-mode) | |
317 | ||
318 | (eshell-deftest mode major-mode | |
319 | "Major mode is correct" | |
320 | (eq major-mode 'eshell-mode)) | |
321 | ||
322 | (eshell-deftest mode eshell-mode-variable | |
323 | "`eshell-mode' is true" | |
324 | (eq eshell-mode t)) | |
325 | ||
326 | (eshell-deftest var window-height | |
327 | "LINES equals window height" | |
328 | (let ((eshell-stringify-t t)) | |
329 | (eshell-command-result-p "= $LINES (window-height)" "t\n"))) | |
330 | ||
331 | (eshell-deftest mode command-running-p | |
332 | "Modeline shows no command running" | |
333 | (or (featurep 'xemacs) | |
334 | (not eshell-status-in-modeline) | |
335 | (and (memq 'eshell-command-running-string mode-line-format) | |
336 | (equal eshell-command-running-string "--")))) | |
337 | ||
338 | (eshell-deftest arg forward-arg | |
339 | "Move across command arguments" | |
340 | (eshell-insert-command "echo $(+ 1 (- 4 3)) \"alpha beta\" file" 'ignore) | |
341 | (let ((here (point)) begin valid) | |
342 | (eshell-bol) | |
343 | (setq begin (point)) | |
344 | (eshell-forward-argument 4) | |
345 | (setq valid (= here (point))) | |
346 | (eshell-backward-argument 4) | |
347 | (prog1 | |
348 | (and valid (= begin (point))) | |
349 | (eshell-bol) | |
350 | (delete-region (point) (point-max))))) | |
351 | ||
352 | (eshell-deftest mode queue-input | |
353 | "Queue command input" | |
354 | (eshell-insert-command "sleep 2") | |
355 | (eshell-insert-command "echo alpha" 'eshell-queue-input) | |
356 | (let ((count 10)) | |
357 | (while (and eshell-current-command | |
358 | (> count 0)) | |
359 | (sit-for 1 0) | |
360 | (setq count (1- count)))) | |
361 | (eshell-match-result "alpha\n")) | |
362 | ||
363 | ; (eshell-deftest proc send-to-subprocess | |
364 | ; "Send input to a subprocess" | |
365 | ; ;; jww (1999-12-06): what about when bc is unavailable? | |
366 | ; (if (not (eshell-search-path "bc")) | |
367 | ; t | |
368 | ; (eshell-insert-command "bc") | |
369 | ; (eshell-insert-command "1 + 2") | |
370 | ; (sit-for 1 0) | |
371 | ; (forward-line -1) | |
372 | ; (prog1 | |
373 | ; (looking-at "3\n") | |
374 | ; (eshell-insert-command "quit") | |
375 | ; (sit-for 1 0)))) | |
376 | ||
377 | (eshell-deftest io flush-output | |
378 | "Flush previous output" | |
379 | (eshell-insert-command "echo alpha") | |
380 | (eshell-kill-output) | |
381 | (and (eshell-match-result (regexp-quote "*** output flushed ***\n")) | |
382 | (forward-line) | |
383 | (= (point) eshell-last-output-start))) | |
384 | ||
385 | (eshell-deftest mode run-old-command | |
386 | "Re-run an old command" | |
387 | (eshell-insert-command "echo alpha") | |
388 | (goto-char eshell-last-input-start) | |
389 | (string= (eshell-get-old-input) "echo alpha")) | |
390 | ||
391 | ||
392 | (require 'esh-var) | |
393 | ||
394 | (eshell-deftest var interp-cmd | |
395 | "Interpolate command result" | |
396 | (eshell-command-result-p "+ ${+ 1 2} 3" "6\n")) | |
397 | ||
398 | (eshell-deftest var interp-lisp | |
5eba16a3 | 399 | "Interpolate Lisp form evaluation" |
81fb60b2 GM |
400 | (eshell-command-result-p "+ $(+ 1 2) 3" "6\n")) |
401 | ||
402 | (eshell-deftest var interp-concat | |
403 | "Interpolate and concat command" | |
404 | (eshell-command-result-p "+ ${+ 1 2}3 3" "36\n")) | |
405 | ||
406 | (eshell-deftest var interp-concat-lisp | |
407 | "Interpolate and concat Lisp form" | |
408 | (eshell-command-result-p "+ $(+ 1 2)3 3" "36\n")) | |
409 | ||
410 | (eshell-deftest var interp-concat2 | |
411 | "Interpolate and concat two commands" | |
412 | (eshell-command-result-p "+ ${+ 1 2}${+ 1 2} 3" "36\n")) | |
413 | ||
414 | (eshell-deftest var interp-concat-lisp2 | |
415 | "Interpolate and concat two Lisp forms" | |
416 | (eshell-command-result-p "+ $(+ 1 2)$(+ 1 2) 3" "36\n")) | |
417 | ||
418 | ||
00c3ec76 GM |
419 | (provide 'esh-test) |
420 | ||
affbf647 | 421 | ;;; esh-test.el ends here |