Add 2012 to FSF copyright years for Emacs files
[bpt/emacs.git] / test / eshell.el
CommitLineData
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