1 ;;; esh-test.el --- Eshell test suite
3 ;; Copyright (C) 1999-2011 Free Software Foundation, Inc.
5 ;; Author: John Wiegley <johnw@gnu.org>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
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.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
24 ;; The purpose of this module is to verify that Eshell works as
25 ;; expected. To run it on your system, use the command
31 (require 'cl
) ; assert
36 (defgroup eshell-test nil
37 "This module is meant to ensure that Eshell is working correctly."
38 :tag
"Eshell test suite"
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."
48 (define-obsolete-face-alias 'eshell-test-ok-face
'eshell-test-ok
"22.1")
50 (defface eshell-test-failed
51 '((((class color
) (background light
)) (:foreground
"OrangeRed" :bold t
))
52 (((class color
) (background dark
)) (:foreground
"OrangeRed" :bold t
))
54 "The face used to highlight FAILED result strings."
56 (define-obsolete-face-alias 'eshell-test-failed-face
'eshell-test-failed
"22.1")
58 (defcustom eshell-show-usage-metrics nil
59 "If non-nil, display different usage metrics for each Eshell command."
60 :set
(lambda (symbol value
)
62 (add-hook 'eshell-mode-hook
'eshell-show-usage-metrics
)
63 (remove-hook 'eshell-mode-hook
'eshell-show-usage-metrics
))
65 :type
'(choice (const :tag
"No metrics" nil
)
66 (const :tag
"Cons cells consumed" t
)
67 (const :tag
"Time elapsed" 0))
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
)))
80 (defun eshell-match-result (regexp)
81 "Insert a command at the end of the buffer."
82 (goto-char eshell-last-input-end
)
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
))
90 (defvar eshell-test-failures nil
)
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 " ....")
102 (let ((truth (eval command
)))
103 (with-current-buffer test-buffer
105 (insert-before-markers
110 (put-text-property 0 6 'face
'eshell-test-ok str
))
112 (setq eshell-test-failures
(1+ eshell-test-failures
))
113 (put-text-property 0 6 'face
'eshell-test-failed str
))
115 (add-text-properties (line-beginning-position) (point)
116 (list 'test-func funcsym
))
117 (eshell-redisplay)))))
119 (defun eshell-test-goto-func ()
120 "Jump to the function that defines a particular test."
122 (let ((fsym (get-text-property (point) 'test-func
)))
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
))
130 (goto-char (point-min))
131 (re-search-forward (concat "^(eshell-deftest\\s-+\\w+\\s-+"
133 (beginning-of-line)))))
135 (defun eshell-run-one-test (&optional arg
)
136 "Jump to the function that defines a particular test."
138 (let ((fsym (get-text-property (point) 'test-func
)))
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
))))
147 (kill-buffer (current-buffer)))))))
150 (defun eshell-test (&optional arg
)
151 "Test Eshell to verify that it works as expected."
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
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)
167 (insert "Testing Eshell under " (emacs-version))
168 (switch-to-buffer test-buffer)
169 (delete-other-windows))
170 (dolist (funcname (sort (all-completions "eshell-test--"
173 (with-current-buffer test-buffer
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)
184 (if (= eshell-test-failures 1) "" "s"))))
185 (goto-char eshell-last-output-end)
187 (kill-buffer (current-buffer))))
190 (defvar eshell-metric-before-command 0)
191 (defvar eshell-metric-after-command 0)
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)
199 (set (make-local-variable 'eshell-metric-after-command)
200 (if (eq eshell-show-usage-metrics t)
204 (add-hook 'eshell-pre-command-hook
207 (setq eshell-metric-before-command
208 (if (eq eshell-show-usage-metrics t)
209 (car (memory-use-counts))
210 (current-time))))) nil t)
212 (add-hook 'eshell-post-command-hook
215 (setq eshell-metric-after-command
216 (if (eq eshell-show-usage-metrics t)
217 (car (memory-use-counts))
219 (eshell-interactive-print
222 (if (eq eshell-show-usage-metrics t)
223 (- eshell-metric-after-command
224 eshell-metric-before-command 7)
226 eshell-metric-after-command)
228 eshell-metric-before-command))))
235 (defmacro eshell-deftest (module name label &rest forms)
237 (if (and (fboundp 'cl-compiling-file) (cl-compiling-file))
239 (let ((fsym (intern (concat "eshell-test--" (symbol-name name)))))
242 (defun ,fsym () ,label
243 (eshell-run-test (quote ,module) (quote ,fsym) ,label
244 (quote (progn ,@forms)))))))))
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))
251 (eshell-deftest mode eshell-directory-exists
252 "`eshell-directory-name' exists and is writable"
253 (file-writable-p eshell-directory-name))
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)))
261 (eshell-deftest mode simple-command-result
262 "`eshell-command-result' works with a simple command."
263 (= (eshell-command-result "+ 1 2") 3))
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)))
273 (goto-char (point-min))
279 (eshell-deftest var last-result-var
280 "\"last result\" variable"
281 (eshell-command-result-p "+ 1 2; + $$ 2" "3\n5\n"))
283 (eshell-deftest var last-result-var2
284 "\"last result\" variable"
285 (eshell-command-result-p "+ 1 2; + $$ $$" "3\n6\n"))
287 (eshell-deftest var last-arg-var
288 "\"last arg\" variable"
289 (eshell-command-result-p "+ 1 2; + $_ 4" "3\n6\n"))
291 (eshell-deftest cmd lisp-command
292 "Evaluate Lisp command"
293 (eshell-command-result-p "(+ 1 2)" "3"))
295 (eshell-deftest cmd lisp-command-args
296 "Evaluate Lisp command (ignore args)"
297 (eshell-command-result-p "(+ 1 2) 3" "3"))
299 (eshell-deftest cmd subcommand
301 (eshell-command-result-p "{+ 1 2}" "3\n"))
303 (eshell-deftest cmd subcommand-args
304 "Run subcommand (ignore args)"
305 (eshell-command-result-p "{+ 1 2} 3" "3\n"))
307 (eshell-deftest cmd subcommand-lisp
308 "Run subcommand + Lisp form"
309 (eshell-command-result-p "{(+ 1 2)}" "3\n"))
311 (eshell-deftest cmd named-command
312 "Execute named command"
313 (eshell-command-result-p "+ 1 2" "3\n"))
318 (eshell-deftest mode major-mode
319 "Major mode is correct"
320 (eq major-mode 'eshell-mode))
322 (eshell-deftest mode eshell-mode-variable
323 "`eshell-mode' is true"
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")))
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 "--"))))
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)
344 (eshell-forward-argument 4)
345 (setq valid (= here (point)))
346 (eshell-backward-argument 4)
348 (and valid (= begin (point)))
350 (delete-region (point) (point-max)))))
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)
357 (while (and eshell-current-command
360 (setq count (1- count))))
361 (eshell-match-result "alpha\n"))
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"))
368 ; (eshell-insert-command "bc")
369 ; (eshell-insert-command "1 + 2")
374 ; (eshell-insert-command "quit")
377 (eshell-deftest io flush-output
378 "Flush previous output"
379 (eshell-insert-command "echo alpha")
381 (and (eshell-match-result (regexp-quote "*** output flushed ***\n"))
383 (= (point) eshell-last-output-start)))
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"))
394 (eshell-deftest var interp-cmd
395 "Interpolate command result"
396 (eshell-command-result-p "+ ${+ 1 2} 3" "6\n"))
398 (eshell-deftest var interp-lisp
399 "Interpolate Lisp form evalution"
400 (eshell-command-result-p "+ $(+ 1 2) 3" "6\n"))
402 (eshell-deftest var interp-concat
403 "Interpolate and concat command"
404 (eshell-command-result-p "+ ${+ 1 2}3 3" "36\n"))
406 (eshell-deftest var interp-concat-lisp
407 "Interpolate and concat Lisp form"
408 (eshell-command-result-p "+ $(+ 1 2)3 3" "36\n"))
410 (eshell-deftest var interp-concat2
411 "Interpolate and concat two commands"
412 (eshell-command-result-p "+ ${+ 1 2}${+ 1 2} 3" "36\n"))
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"))
421 ;;; esh-test.el ends here