summaryrefslogtreecommitdiff
path: root/test/lisp/eshell/em-cmpl-tests.el
diff options
context:
space:
mode:
Diffstat (limited to 'test/lisp/eshell/em-cmpl-tests.el')
-rw-r--r--test/lisp/eshell/em-cmpl-tests.el380
1 files changed, 380 insertions, 0 deletions
diff --git a/test/lisp/eshell/em-cmpl-tests.el b/test/lisp/eshell/em-cmpl-tests.el
new file mode 100644
index 00000000000..f778816c4e1
--- /dev/null
+++ b/test/lisp/eshell/em-cmpl-tests.el
@@ -0,0 +1,380 @@
+;;; em-cmpl-tests.el --- em-cmpl test suite -*- lexical-binding:t -*-
+
+;; Copyright (C) 2023-2024 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tests for Eshell's interactive completion.
+
+;;; Code:
+
+(require 'ert)
+(require 'eshell)
+(require 'em-cmpl)
+(require 'em-dirs)
+(require 'em-hist)
+(require 'em-tramp)
+(require 'em-unix)
+
+(require 'eshell-tests-helpers
+ (expand-file-name "eshell-tests-helpers"
+ (file-name-directory (or load-file-name
+ default-directory))))
+
+(defvar eshell-test-value nil)
+
+(defun eshell-insert-and-complete (input)
+ "Insert INPUT and invoke completion, returning the result."
+ (insert input)
+ (completion-at-point)
+ (eshell-get-old-input))
+
+(defun eshell-arguments-equal (actual expected)
+ "Return t if ACTUAL and EXPECTED are equal, including properties of strings.
+ACTUAL and EXPECTED should both be lists of strings."
+ (when (length= actual (length expected))
+ (catch 'not-equal
+ (cl-mapc (lambda (i j)
+ (unless (equal-including-properties i j)
+ (throw 'not-equal nil)))
+ actual expected)
+ t)))
+
+(defun eshell-arguments-equal--equal-explainer (actual expected)
+ "Explain the result of `eshell-arguments-equal'."
+ `(nonequal-result
+ (actual ,actual)
+ (expected ,expected)))
+
+(put 'eshell-arguments-equal 'ert-explainer
+ #'eshell-arguments-equal--equal-explainer)
+
+;;; Tests:
+
+(ert-deftest em-cmpl-test/parse-arguments/pipeline ()
+ "Test that parsing arguments for completion discards earlier commands."
+ (with-temp-eshell
+ (insert "echo hi | cat")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ '("cat")))))
+
+(ert-deftest em-cmpl-test/parse-arguments/multiple-dots ()
+ "Test parsing arguments with multiple dots like \".../\"."
+ (with-temp-eshell
+ (insert "echo .../file.txt")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize "../../file.txt"
+ 'pcomplete-arg-value
+ ".../file.txt"))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/variable/numeric ()
+ "Test parsing arguments with a numeric variable interpolation."
+ (with-temp-eshell
+ (let ((eshell-test-value 42))
+ (insert "echo $eshell-test-value")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize "42" 'pcomplete-arg-value 42)))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/variable/nil ()
+ "Test parsing arguments with a nil variable interpolation."
+ (with-temp-eshell
+ (let ((eshell-test-value nil))
+ (insert "echo $eshell-test-value")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize "" 'pcomplete-arg-value nil)))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/variable/list ()
+ "Test parsing arguments with a list variable interpolation."
+ (with-temp-eshell
+ (let ((eshell-test-value '("foo" "bar")))
+ (insert "echo $eshell-test-value")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize "(\"foo\" \"bar\")"
+ 'pcomplete-arg-value
+ eshell-test-value)))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/variable/splice ()
+ "Test parsing arguments with a spliced variable interpolation."
+ (with-temp-eshell
+ (let ((eshell-test-value '("foo" "bar")))
+ (insert "echo $@eshell-test-value")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ '("echo" "foo" "bar"))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/unevaluated-subcommand ()
+ "Test that subcommands return a stub when parsing for completion."
+ (with-temp-eshell
+ (insert "echo {echo hi}")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'named-command)))))
+ (with-temp-eshell
+ (insert "echo ${echo hi}")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'named-command))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/unevaluated-lisp-form ()
+ "Test that Lisp forms return a stub when parsing for completion."
+ (with-temp-eshell
+ (insert "echo (concat \"hi\")")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'lisp-command)))))
+ (with-temp-eshell
+ (insert "echo $(concat \"hi\")")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'lisp-command))))))
+
+(ert-deftest em-cmpl-test/parse-arguments/unevaluated-inner-subcommand ()
+ "Test that nested subcommands return a stub when parsing for completion."
+ (with-temp-eshell
+ (insert "echo $exec-path[${echo 0}]")
+ (should (eshell-arguments-equal
+ (car (eshell-complete-parse-arguments))
+ `("echo" ,(propertize
+ "\0" 'eshell-argument-stub 'named-command))))))
+
+(ert-deftest em-cmpl-test/file-completion/unique ()
+ "Test completion of file names when there's a unique result."
+ (with-temp-eshell
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (should (equal (eshell-insert-and-complete "echo fi")
+ "echo file.txt ")))))
+
+(ert-deftest em-cmpl-test/file-completion/non-unique ()
+ "Test completion of file names when there are multiple results."
+ (with-temp-eshell
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (write-region nil nil (expand-file-name "file.el"))
+ ;; Complete the first time. This should insert the common prefix
+ ;; of our completions.
+ (should (equal (eshell-insert-and-complete "echo fi")
+ "echo file."))
+ ;; Make sure the completions buffer isn't displayed.
+ (should-not (get-buffer-window "*Completions*"))
+ ;; Now try completing again.
+ (let ((minibuffer-message-timeout 0)
+ (inhibit-message t))
+ (completion-at-point))
+ ;; This time, we should display the completions buffer.
+ (should (get-buffer-window "*Completions*")))))
+
+(ert-deftest em-cmpl-test/file-completion/glob ()
+ "Test completion of file names using a glob."
+ (with-temp-eshell
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (write-region nil nil (expand-file-name "file.el"))
+ (should (equal (eshell-insert-and-complete "echo fi*.el")
+ "echo file.el ")))))
+
+(ert-deftest em-cmpl-test/file-completion/after-list ()
+ "Test completion of file names after previous list arguments.
+See bug#59956."
+ (with-temp-eshell
+ (let ((eshell-test-value '("foo" "bar")))
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (should (equal (eshell-insert-and-complete "echo $eshell-test-value fi")
+ "echo $eshell-test-value file.txt "))))))
+
+(ert-deftest em-cmpl-test/command-completion ()
+ "Test completion of command names like \"command\"."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "listif")
+ "listify "))))
+
+(ert-deftest em-cmpl-test/subcommand-completion ()
+ "Test completion of command names like \"{command}\"."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "{ listif")
+ "{ listify ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo ${ listif")
+ "echo ${ listify "))))
+
+(ert-deftest em-cmpl-test/lisp-symbol-completion ()
+ "Test completion of Lisp forms like \"#'symbol\" and \"`symbol\".
+See <lisp/eshell/esh-cmd.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo #'system-nam")
+ "echo #'system-name ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo `system-nam")
+ "echo `system-name "))))
+
+(ert-deftest em-cmpl-test/lisp-function-completion ()
+ "Test completion of Lisp forms like \"(func)\".
+See <lisp/eshell/esh-cmd.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo (eshell/ech")
+ "echo (eshell/echo")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo $(eshell/ech")
+ "echo $(eshell/echo"))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/type ()
+ "Test completion of the start of special reference types like \"#<buffer\".
+See <lisp/eshell/esh-arg.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buf")
+ "echo hi > #<buffer ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<proc")
+ "echo hi > #<process ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<mark")
+ "echo hi > #<marker "))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/implicit-buffer ()
+ "Test completion of special references like \"#<buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<my-buf")
+ (format "echo hi > #<%s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<anoth")
+ (format "echo hi > #<%s> "
+ (string-replace " " "\\ " bufname))))))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/buffer ()
+ "Test completion of special references like \"#<buffer buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer my-buf")
+ (format "echo hi > #<buffer %s> " bufname))))
+ (setq bufname (rename-buffer "another buffer" t))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo hi > #<buffer anoth")
+ (format "echo hi > #<buffer %s> "
+ (string-replace " " "\\ " bufname))))))))
+
+(ert-deftest em-cmpl-test/special-ref-completion/marker ()
+ "Test completion of special references like \"#<marker 1 buf>\".
+See <lisp/eshell/esh-arg.el>."
+ (let (bufname)
+ (with-temp-buffer
+ (setq bufname (rename-buffer "my-buffer" t))
+ ;; Complete the buffer name in various forms.
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete
+ "echo hi > #<marker 1 my-buf")
+ (format "echo hi > #<marker 1 %s> " bufname))))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete
+ "echo hi > #<marker 1 #<my-buf")
+ (format "echo hi > #<marker 1 #<%s>> " bufname))))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete
+ "echo hi > #<marker 1 #<buffer my-buf")
+ (format "echo hi > #<marker 1 #<buffer %s>> " bufname))))
+ ;; Partially-complete the "buffer" type name.
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete
+ "echo hi > #<marker 1 #<buf")
+ "echo hi > #<marker 1 #<buffer "))))))
+
+(ert-deftest em-cmpl-test/variable-ref-completion ()
+ "Test completion of variable references like \"$var\".
+See <lisp/eshell/esh-var.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo $system-nam")
+ "echo $system-name "))))
+
+(ert-deftest em-cmpl-test/quoted-variable-ref-completion ()
+ "Test completion of variable references like \"$'var'\".
+See <lisp/eshell/esh-var.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo $'system-nam")
+ "echo $'system-name' ")))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo $\"system-nam")
+ "echo $\"system-name\" "))))
+
+(ert-deftest em-cmpl-test/variable-ref-completion/directory ()
+ "Test completion of variable references that expand to directories.
+See <lisp/eshell/esh-var.el>."
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo $PW")
+ "echo $PWD/")))
+ (with-temp-eshell
+ (let ((minibuffer-message-timeout 0)
+ (inhibit-message t))
+ (should (equal (eshell-insert-and-complete "echo $PWD")
+ "echo $PWD/"))))
+ (with-temp-eshell
+ (should (equal (eshell-insert-and-complete "echo $'PW")
+ "echo $'PWD'/"))))
+
+(ert-deftest em-cmpl-test/variable-assign-completion ()
+ "Test completion of variable assignments like \"var=value\".
+See <lisp/eshell/esh-var.el>."
+ (with-temp-eshell
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (should (equal (eshell-insert-and-complete "VAR=f")
+ "VAR=file.txt ")))))
+
+(ert-deftest em-cmpl-test/variable-assign-completion/non-assignment ()
+ "Test completion of things that look like variable assignment, but aren't.
+For example, the second argument in \"tar --directory=dir\" looks
+like it could be a variable assignment, but it's not. We should
+let `pcomplete/tar' handle it instead.
+
+See <lisp/eshell/esh-var.el>."
+ (with-temp-eshell
+ (ert-with-temp-directory default-directory
+ (write-region nil nil (expand-file-name "file.txt"))
+ (make-directory "dir")
+ (should (equal (eshell-insert-and-complete "tar --directory=")
+ "tar --directory=dir/")))))
+
+(ert-deftest em-cmpl-test/user-ref-completion ()
+ "Test completion of user references like \"~user\".
+See <lisp/eshell/em-dirs.el>."
+ (unwind-protect
+ (with-temp-eshell
+ (cl-letf (((symbol-function 'eshell-read-user-names)
+ (lambda () (setq eshell-user-names '((1234 . "user"))))))
+ (should (equal (eshell-insert-and-complete "echo ~us")
+ "echo ~user/"))))
+ ;; Clear the cached user names we set above.
+ (setq eshell-user-names nil)))
+
+;;; em-cmpl-tests.el ends here