-;;; esh-test.el --- Eshell test suite
+;;; tests/eshell.el --- Eshell test suite
;; Copyright (C) 1999-2013 Free Software Foundation, Inc.
;;; Commentary:
-;; The purpose of this module is to verify that Eshell works as
-;; expected. To run it on your system, use the command
-;; \\[eshell-test].
+;; Eshell test suite.
;;; Code:
-(eval-when-compile
- (require 'cl-lib)
- (require 'eshell)
- (require 'esh-util))
-(require 'esh-mode)
-
-(defgroup eshell-test nil
- "This module is meant to ensure that Eshell is working correctly."
- :tag "Eshell test suite"
- :group 'eshell)
-
-;;; User Variables:
-
-(defface eshell-test-ok
- '((((class color) (background light)) (:foreground "Green" :bold t))
- (((class color) (background dark)) (:foreground "Green" :bold t)))
- "The face used to highlight OK result strings."
- :group 'eshell-test)
-(define-obsolete-face-alias 'eshell-test-ok-face 'eshell-test-ok "22.1")
-
-(defface eshell-test-failed
- '((((class color) (background light)) (:foreground "OrangeRed" :bold t))
- (((class color) (background dark)) (:foreground "OrangeRed" :bold t))
- (t (:bold t)))
- "The face used to highlight FAILED result strings."
- :group 'eshell-test)
-(define-obsolete-face-alias 'eshell-test-failed-face 'eshell-test-failed "22.1")
-
-(defcustom eshell-show-usage-metrics nil
- "If non-nil, display different usage metrics for each Eshell command."
- :set (lambda (symbol value)
- (if value
- (add-hook 'eshell-mode-hook 'eshell-show-usage-metrics)
- (remove-hook 'eshell-mode-hook 'eshell-show-usage-metrics))
- (set symbol value))
- :type '(choice (const :tag "No metrics" nil)
- (const :tag "Cons cells consumed" t)
- (const :tag "Time elapsed" 0))
- :group 'eshell-test)
+(require 'ert)
+(require 'eshell)
-;;; Code:
-
-(defvar test-buffer)
+(defmacro with-temp-eshell (&rest body)
+ "Evaluate BODY in a temporary Eshell buffer."
+ `(let ((eshell-buffer (eshell t)))
+ (unwind-protect
+ (with-current-buffer eshell-buffer
+ ,@body)
+ (kill-buffer eshell-buffer))))
(defun eshell-insert-command (text &optional func)
"Insert a command at the end of the buffer."
(eshell-insert-command text func)
(eshell-match-result regexp))
-(defvar eshell-test-failures nil)
-
-(defun eshell-run-test (module funcsym label command)
- "Test whether FORM evaluates to a non-nil value."
- (when (let ((sym (intern-soft (concat "eshell-" (symbol-name module)))))
- (or (memq sym (eshell-subgroups 'eshell))
- (eshell-using-module sym)))
- (with-current-buffer test-buffer
- (insert-before-markers
- (format "%-70s " (substring label 0 (min 70 (length label)))))
- (insert-before-markers " ....")
- (eshell-redisplay))
- (let ((truth (eval command)))
- (with-current-buffer test-buffer
- (delete-char -6)
- (insert-before-markers
- "[" (let (str)
- (if truth
- (progn
- (setq str " OK ")
- (put-text-property 0 6 'face 'eshell-test-ok str))
- (setq str "FAILED")
- (setq eshell-test-failures (1+ eshell-test-failures))
- (put-text-property 0 6 'face 'eshell-test-failed str))
- str) "]")
- (add-text-properties (line-beginning-position) (point)
- (list 'test-func funcsym))
- (eshell-redisplay)))))
-
-(defun eshell-test-goto-func ()
- "Jump to the function that defines a particular test."
- (interactive)
- (let ((fsym (get-text-property (point) 'test-func)))
- (when fsym
- (let* ((def (symbol-function fsym))
- (library (locate-library (symbol-file fsym 'defun)))
- (name (substring (symbol-name fsym)
- (length "eshell-test--")))
- (inhibit-redisplay t))
- (find-file library)
- (goto-char (point-min))
- (re-search-forward (concat "^(eshell-deftest\\s-+\\w+\\s-+"
- name))
- (beginning-of-line)))))
-
-(defun eshell-run-one-test (&optional arg)
- "Jump to the function that defines a particular test."
- (interactive "P")
- (let ((fsym (get-text-property (point) 'test-func)))
- (when fsym
- (beginning-of-line)
- (delete-region (point) (line-end-position))
- (let ((test-buffer (current-buffer)))
- (set-buffer (let ((inhibit-redisplay t))
- (save-window-excursion (eshell t))))
- (funcall fsym)
- (unless arg
- (kill-buffer (current-buffer)))))))
-
-;;;###autoload
-(defun eshell-test (&optional arg)
- "Test Eshell to verify that it works as expected."
- (interactive "P")
- (let* ((begin (float-time))
- (test-buffer (get-buffer-create "*eshell test*")))
- (set-buffer (let ((inhibit-redisplay t))
- (save-window-excursion (eshell t))))
- (with-current-buffer test-buffer
- (erase-buffer)
- (setq major-mode 'eshell-test-mode)
- (setq mode-name "EShell Test")
- (set (make-local-variable 'eshell-test-failures) 0)
- (local-set-key [(control ?c) (control ?c)] 'eshell-test-goto-func)
- (local-set-key [(control ?c) (control ?r)] 'eshell-run-one-test)
- (local-set-key [(control ?m)] 'eshell-test-goto-func)
- (local-set-key [return] 'eshell-test-goto-func)
-
- (insert "Testing Eshell under " (emacs-version))
- (switch-to-buffer test-buffer)
- (delete-other-windows))
- (dolist (funcname (sort (all-completions "eshell-test--"
- obarray 'functionp)
- 'string-lessp))
- (with-current-buffer test-buffer
- (insert "\n"))
- (funcall (intern-soft funcname)))
- (with-current-buffer test-buffer
- (insert (format "\n\n--- %s --- (completed in %d seconds)\n"
- (current-time-string)
- (- (float-time) begin)))
- (message "Eshell test suite completed: %s failure%s"
- (if (> eshell-test-failures 0)
- (number-to-string eshell-test-failures)
- "No")
- (if (= eshell-test-failures 1) "" "s"))))
- (goto-char eshell-last-output-end)
- (unless arg
- (kill-buffer (current-buffer))))
-
-
-(defvar eshell-metric-before-command 0)
-(defvar eshell-metric-after-command 0)
-
-(defun eshell-show-usage-metrics ()
- "If run at Eshell mode startup, metrics are shown after each command."
- (set (make-local-variable 'eshell-metric-before-command)
- (if (eq eshell-show-usage-metrics t)
- 0
- (current-time)))
- (set (make-local-variable 'eshell-metric-after-command)
- (if (eq eshell-show-usage-metrics t)
- 0
- (current-time)))
-
- (add-hook 'eshell-pre-command-hook
- (function
- (lambda ()
- (setq eshell-metric-before-command
- (if (eq eshell-show-usage-metrics t)
- (car (memory-use-counts))
- (current-time))))) nil t)
-
- (add-hook 'eshell-post-command-hook
- (function
- (lambda ()
- (setq eshell-metric-after-command
- (if (eq eshell-show-usage-metrics t)
- (car (memory-use-counts))
- (current-time)))
- (eshell-interactive-print
- (concat
- (int-to-string
- (if (eq eshell-show-usage-metrics t)
- (- eshell-metric-after-command
- eshell-metric-before-command 7)
- (- (float-time
- eshell-metric-after-command)
- (float-time
- eshell-metric-before-command))))
- "\n"))))
- nil t))
-
-
-;;; The tests.
-
-(defmacro eshell-deftest (module name label &rest forms)
- (declare (indent 2))
- (if (and (fboundp 'cl-compiling-file) (cl-compiling-file))
- nil
- (let ((fsym (intern (concat "eshell-test--" (symbol-name name)))))
- `(eval-when-compile
- (ignore
- (defun ,fsym () ,label
- (eshell-run-test (quote ,module) (quote ,fsym) ,label
- (quote (progn ,@forms)))))))))
-
-
-(eshell-deftest mode same-window-buffer-names
- "`eshell-buffer-name' is a member of `same-window-buffer-names'"
- (member eshell-buffer-name same-window-buffer-names))
-
-(eshell-deftest mode eshell-directory-exists
- "`eshell-directory-name' exists and is writable"
- (file-writable-p eshell-directory-name))
-
-(eshell-deftest mode eshell-directory-modes
- "`eshell-directory-name' has correct access protections"
- (or (eshell-under-windows-p)
- (= (file-modes eshell-directory-name)
- eshell-private-directory-modes)))
-
-(eshell-deftest mode simple-command-result
- "`eshell-command-result' works with a simple command."
- (= (eshell-command-result "+ 1 2") 3))
-
-
-(require 'em-banner)
-
-(eshell-deftest banner banner-displayed
- "Startup banner is displayed at point-min"
- (cl-assert eshell-banner-message)
- (let ((msg (eval eshell-banner-message)))
- (cl-assert msg)
- (goto-char (point-min))
- (looking-at msg)))
-
-
-(require 'esh-cmd)
-
-(eshell-deftest var last-result-var
- "\"last result\" variable"
- (eshell-command-result-p "+ 1 2; + $$ 2" "3\n5\n"))
-
-(eshell-deftest var last-result-var2
- "\"last result\" variable"
- (eshell-command-result-p "+ 1 2; + $$ $$" "3\n6\n"))
-
-(eshell-deftest var last-arg-var
- "\"last arg\" variable"
- (eshell-command-result-p "+ 1 2; + $_ 4" "3\n6\n"))
-
-(eshell-deftest cmd lisp-command
- "Evaluate Lisp command"
- (eshell-command-result-p "(+ 1 2)" "3"))
-
-(eshell-deftest cmd lisp-command-args
- "Evaluate Lisp command (ignore args)"
- (eshell-command-result-p "(+ 1 2) 3" "3"))
-
-(eshell-deftest cmd subcommand
- "Run subcommand"
- (eshell-command-result-p "{+ 1 2}" "3\n"))
-
-(eshell-deftest cmd subcommand-args
- "Run subcommand (ignore args)"
- (eshell-command-result-p "{+ 1 2} 3" "3\n"))
-
-(eshell-deftest cmd subcommand-lisp
- "Run subcommand + Lisp form"
- (eshell-command-result-p "{(+ 1 2)}" "3\n"))
-
-(eshell-deftest cmd named-command
- "Execute named command"
- (eshell-command-result-p "+ 1 2" "3\n"))
-
-
-(require 'esh-mode)
-
-(eshell-deftest mode major-mode
- "Major mode is correct"
- (eq major-mode 'eshell-mode))
-
-(eshell-deftest mode eshell-mode-variable
- "`eshell-mode' is true"
- (eq eshell-mode t))
-
-(eshell-deftest var window-height
- "LINES equals window height"
- (let ((eshell-stringify-t t))
- (eshell-command-result-p "= $LINES (window-height)" "t\n")))
-
-(eshell-deftest mode command-running-p
- "Modeline shows no command running"
- (or (featurep 'xemacs)
- (not eshell-status-in-modeline)
- (and (memq 'eshell-command-running-string mode-line-format)
- (equal eshell-command-running-string "--"))))
-
-(eshell-deftest arg forward-arg
- "Move across command arguments"
- (eshell-insert-command "echo $(+ 1 (- 4 3)) \"alpha beta\" file" 'ignore)
- (let ((here (point)) begin valid)
- (eshell-bol)
- (setq begin (point))
- (eshell-forward-argument 4)
- (setq valid (= here (point)))
- (eshell-backward-argument 4)
- (prog1
- (and valid (= begin (point)))
- (eshell-bol)
- (delete-region (point) (point-max)))))
-
-(eshell-deftest mode queue-input
- "Queue command input"
- (eshell-insert-command "sleep 2")
- (eshell-insert-command "echo alpha" 'eshell-queue-input)
- (let ((count 10))
- (while (and eshell-current-command
- (> count 0))
- (sit-for 1 0)
- (setq count (1- count))))
- (eshell-match-result "alpha\n"))
-
-; (eshell-deftest proc send-to-subprocess
-; "Send input to a subprocess"
-; ;; jww (1999-12-06): what about when bc is unavailable?
-; (if (not (eshell-search-path "bc"))
-; t
-; (eshell-insert-command "bc")
-; (eshell-insert-command "1 + 2")
-; (sit-for 1 0)
-; (forward-line -1)
-; (prog1
-; (looking-at "3\n")
-; (eshell-insert-command "quit")
-; (sit-for 1 0))))
-
-(eshell-deftest io flush-output
- "Flush previous output"
- (eshell-insert-command "echo alpha")
- (eshell-kill-output)
- (and (eshell-match-result (regexp-quote "*** output flushed ***\n"))
- (forward-line)
- (= (point) eshell-last-output-start)))
-
-(eshell-deftest mode run-old-command
- "Re-run an old command"
- (eshell-insert-command "echo alpha")
- (goto-char eshell-last-input-start)
- (string= (eshell-get-old-input) "echo alpha"))
+;;; Tests:
+
+(ert-deftest eshell-test/simple-command-result ()
+ "Test `eshell-command-result' with a simple command."
+ (should (equal (eshell-command-result "+ 1 2") 3)))
+(ert-deftest eshell-test/lisp-command ()
+ "Test `eshell-command-result' with an elisp command."
+ (should (equal (eshell-command-result "(+ 1 2)") 3)))
-(require 'esh-var)
+(ert-deftest eshell-test/lisp-command-args ()
+ "Test `eshell-command-result' with elisp and trailing args.
+Test that trailing arguments outside the S-expression are
+ignored. e.g. \"(+ 1 2) 3\" => 3"
+ (should (equal (eshell-command-result "(+ 1 2) 3") 3)))
-(eshell-deftest var interp-cmd
+(ert-deftest eshell-test/subcommand ()
+ "Test `eshell-command-result' with a simple subcommand."
+ (should (equal (eshell-command-result "{+ 1 2}") 3)))
+
+(ert-deftest eshell-test/subcommand-args ()
+ "Test `eshell-command-result' with a subcommand and trailing args.
+Test that trailing arguments outside the subcommand are ignored.
+e.g. \"{+ 1 2} 3\" => 3"
+ (should (equal (eshell-command-result "{+ 1 2} 3") 3)))
+
+(ert-deftest eshell-test/subcommand-lisp ()
+ "Test `eshell-command-result' with an elisp subcommand and trailing args.
+Test that trailing arguments outside the subcommand are ignored.
+e.g. \"{(+ 1 2)} 3\" => 3"
+ (should (equal (eshell-command-result "{(+ 1 2)} 3") 3)))
+
+(ert-deftest eshell-test/interp-cmd ()
"Interpolate command result"
- (eshell-command-result-p "+ ${+ 1 2} 3" "6\n"))
+ (should (equal (eshell-command-result "+ ${+ 1 2} 3") 6)))
-(eshell-deftest var interp-lisp
+(ert-deftest eshell-test/interp-lisp ()
"Interpolate Lisp form evaluation"
- (eshell-command-result-p "+ $(+ 1 2) 3" "6\n"))
+ (should (equal (eshell-command-result "+ $(+ 1 2) 3") 6)))
-(eshell-deftest var interp-concat
+(ert-deftest eshell-test/interp-concat ()
"Interpolate and concat command"
- (eshell-command-result-p "+ ${+ 1 2}3 3" "36\n"))
+ (should (equal (eshell-command-result "+ ${+ 1 2}3 3") 36)))
-(eshell-deftest var interp-concat-lisp
+(ert-deftest eshell-test/interp-concat-lisp ()
"Interpolate and concat Lisp form"
- (eshell-command-result-p "+ $(+ 1 2)3 3" "36\n"))
+ (should (equal (eshell-command-result "+ $(+ 1 2)3 3") 36)))
-(eshell-deftest var interp-concat2
+(ert-deftest eshell-test/interp-concat2 ()
"Interpolate and concat two commands"
- (eshell-command-result-p "+ ${+ 1 2}${+ 1 2} 3" "36\n"))
+ (should (equal (eshell-command-result "+ ${+ 1 2}${+ 1 2} 3") 36)))
-(eshell-deftest var interp-concat-lisp2
+(ert-deftest eshell-test/interp-concat-lisp2 ()
"Interpolate and concat two Lisp forms"
- (eshell-command-result-p "+ $(+ 1 2)$(+ 1 2) 3" "36\n"))
-
+ (should (equal (eshell-command-result "+ $(+ 1 2)$(+ 1 2) 3") 36)))
+
+(ert-deftest eshell-test/window-height ()
+ "$LINES should equal (window-height)"
+ (should (eshell-command-result "= $LINES (window-height)")))
+
+(ert-deftest eshell-test/window-width ()
+ "$COLUMNS should equal (window-width)"
+ (should (eshell-command-result "= $COLUMNS (window-width)")))
+
+(ert-deftest eshell-test/last-result-var ()
+ "Test using the \"last result\" ($$) variable"
+ (with-temp-eshell
+ (should
+ (eshell-command-result-p "+ 1 2; + $$ 2"
+ "3\n5\n"))))
+
+(ert-deftest eshell-test/last-result-var2 ()
+ "Test using the \"last result\" ($$) variable twice"
+ (with-temp-eshell
+ (should
+ (eshell-command-result-p "+ 1 2; + $$ $$"
+ "3\n6\n"))))
+
+(ert-deftest eshell-test/last-arg-var ()
+ "Test using the \"last arg\" ($_) variable"
+ (with-temp-eshell
+ (should
+ (eshell-command-result-p "+ 1 2; + $_ 4"
+ "3\n6\n"))))
+
+(ert-deftest eshell-test/command-running-p ()
+ "Modeline should show no command running"
+ (with-temp-eshell
+ (let ((eshell-status-in-mode-line t))
+ (should (memq 'eshell-command-running-string mode-line-format))
+ (should (equal eshell-command-running-string "--")))))
+
+(ert-deftest eshell-test/forward-arg ()
+ "Test moving across command arguments"
+ (with-temp-eshell
+ (eshell-insert-command "echo $(+ 1 (- 4 3)) \"alpha beta\" file" 'ignore)
+ (let ((here (point)) begin valid)
+ (eshell-bol)
+ (setq begin (point))
+ (eshell-forward-argument 4)
+ (setq valid (= here (point)))
+ (eshell-backward-argument 4)
+ (prog1
+ (and valid (= begin (point)))
+ (eshell-bol)
+ (delete-region (point) (point-max))))))
+
+(ert-deftest eshell-test/queue-input ()
+ "Test queuing command input"
+ (with-temp-eshell
+ (eshell-insert-command "sleep 2")
+ (eshell-insert-command "echo alpha" 'eshell-queue-input)
+ (let ((count 10))
+ (while (and eshell-current-command
+ (> count 0))
+ (sit-for 1)
+ (setq count (1- count))))
+ (should (eshell-match-result "alpha\n"))))
+
+(ert-deftest eshell-test/flush-output ()
+ "Test flushing of previous output"
+ (with-temp-eshell
+ (eshell-insert-command "echo alpha")
+ (eshell-kill-output)
+ (should (eshell-match-result (regexp-quote "*** output flushed ***\n")))
+ (should (forward-line))
+ (should (= (point) eshell-last-output-start))))
+
+(ert-deftest eshell-test/run-old-command ()
+ "Re-run an old command"
+ (with-temp-eshell
+ (eshell-insert-command "echo alpha")
+ (goto-char eshell-last-input-start)
+ (string= (eshell-get-old-input) "echo alpha")))
(provide 'esh-test)
-;;; esh-test.el ends here
+;;; tests/eshell.el ends here