Merge from emacs-24; up to 2012-12-19T19:51:40Z!monnier@iro.umontreal.ca
[emacs.git] / test / eshell.el
blob7baeb58a56a98f78bb95fa8f6f17db4445ce744d
1 ;;; esh-test.el --- Eshell test suite
3 ;; Copyright (C) 1999-2013 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/>.
22 ;;; Commentary:
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].
28 ;;; Code:
30 (eval-when-compile
31 (require 'cl-lib)
32 (require 'eshell)
33 (require 'esh-util))
34 (require 'esh-mode)
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)
41 ;;; User Variables:
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")
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")
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)
70 ;;; Code:
72 (defvar test-buffer)
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)
83 (looking-at regexp))
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 " ....")
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)))))
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)))))
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)))))))
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)
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--"
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))))
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)
198 (current-time)))
199 (set (make-local-variable 'eshell-metric-after-command)
200 (if (eq eshell-show-usage-metrics t)
202 (current-time)))
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)
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))
233 ;;; The tests.
235 (defmacro eshell-deftest (module name label &rest forms)
236 (declare (indent 2))
237 (if (and (fboundp 'cl-compiling-file) (cl-compiling-file))
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)))))))))
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))
266 (require 'em-banner)
268 (eshell-deftest banner banner-displayed
269 "Startup banner is displayed at point-min"
270 (cl-assert eshell-banner-message)
271 (let ((msg (eval eshell-banner-message)))
272 (cl-assert msg)
273 (goto-char (point-min))
274 (looking-at msg)))
277 (require 'esh-cmd)
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
300 "Run 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"))
316 (require 'esh-mode)
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"
324 (eq eshell-mode t))
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)
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)))))
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"))
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")
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))))
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)))
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"))
392 (require 'esh-var)
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 evaluation"
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"))
419 (provide 'esh-test)
421 ;;; esh-test.el ends here