emacs/test/lisp/eshell/em-cmpl-tests.el

381 lines
15 KiB
EmacsLisp

;;; 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