From: Po Lu Date: Wed, 26 Feb 2025 08:03:12 +0000 (+0800) Subject: Port Eshell tests to Android X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=d37dc7b6cd9daf2fdacd653ede2decb8f7e13a6a;p=emacs.git Port Eshell tests to Android * test/infra/android/test-controller.el (ats-run-test): Run tests in a temp buffer. * test/lisp/eshell/em-alias-tests.el (ert, em-alias) (eshell-tests-helpers): * test/lisp/eshell/em-basic-tests.el (em-basic) (eshell-tests-helpers): * test/lisp/eshell/em-cmpl-tests.el (em-unix) (eshell-tests-helpers): * test/lisp/eshell/em-dirs-tests.el (em-dirs) (eshell-tests-helpers): * test/lisp/eshell/em-extpipe-tests.el (ert-x) (eshell-tests-helpers): * test/lisp/eshell/em-glob-tests.el (ert, eshell-tests-helpers): * test/lisp/eshell/em-hist-tests.el (eshell) (eshell-tests-helpers): * test/lisp/eshell/em-pred-tests.el (em-pred) (eshell-tests-helpers): * test/lisp/eshell/em-prompt-tests.el (em-prompt) (eshell-tests-helpers): * test/lisp/eshell/em-script-tests.el (em-script) (eshell-tests-helpers): * test/lisp/eshell/em-unix-tests.el (ert-x) (eshell-tests-helpers): * test/lisp/eshell/esh-arg-tests.el (eshell) (eshell-tests-helpers): * test/lisp/eshell/esh-cmd-tests.el (eshell) (eshell-tests-helpers): * test/lisp/eshell/esh-ext-tests.el (eshell) (eshell-tests-helpers): * test/lisp/eshell/esh-io-tests.el (eshell) (eshell-tests-helpers): * test/lisp/eshell/esh-mode-tests.el (em-prompt) (eshell-tests-helpers): * test/lisp/eshell/esh-proc-tests.el (em-prompt) (eshell-tests-helpers): * test/lisp/eshell/esh-util-tests.el (esh-util) (eshell-tests-helpers): * test/lisp/eshell/esh-var-tests.el (eshell) (eshell-tests-helpers): * test/lisp/eshell/eshell-tests.el (esh-mode) (eshell-tests-helpers): Load `eshell-tests-helpers' from the resource directory. * test/lisp/eshell/resources/eshell-tests-helpers.el: Move from `test/lisp/eshell'. (cherry picked from commit 3e496fc31746517440285c2cd9f2b4a09c227d7b) --- diff --git a/test/infra/android/test-controller.el b/test/infra/android/test-controller.el index 89b9b93f7b1..d318c9a0d4b 100644 --- a/test/infra/android/test-controller.el +++ b/test/infra/android/test-controller.el @@ -2442,7 +2442,11 @@ Display the output of the tests executed in a buffer." (with-current-buffer temp-buffer (insert message "\n"))))) (let ((noninteractive t)) - (ert-run-tests-batch ',selector)) + ;; Prevent activation of the mark and + ;; other actions taken by the tests + ;; from affecting the test buffer. + (with-temp-buffer + (ert-run-tests-batch ',selector))) (insert "=== Test execution complete ===\n") (buffer-substring-no-properties (point-min) (point-max))))))) diff --git a/test/lisp/eshell/em-alias-tests.el b/test/lisp/eshell/em-alias-tests.el index 2386e04bbd3..51af80af148 100644 --- a/test/lisp/eshell/em-alias-tests.el +++ b/test/lisp/eshell/em-alias-tests.el @@ -24,14 +24,14 @@ ;;; Code: (require 'ert) +(require 'ert-x) (require 'esh-mode) (require 'eshell) (require 'em-alias) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) ;;; Tests: (ert-deftest em-alias-test/simple-alias () diff --git a/test/lisp/eshell/em-basic-tests.el b/test/lisp/eshell/em-basic-tests.el index ee2a624cf6f..cd141dc440e 100644 --- a/test/lisp/eshell/em-basic-tests.el +++ b/test/lisp/eshell/em-basic-tests.el @@ -25,11 +25,10 @@ (require 'ert) (require 'em-basic) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) ;;; Tests: diff --git a/test/lisp/eshell/em-cmpl-tests.el b/test/lisp/eshell/em-cmpl-tests.el index 5b4fc5b48b1..f7f8958d816 100644 --- a/test/lisp/eshell/em-cmpl-tests.el +++ b/test/lisp/eshell/em-cmpl-tests.el @@ -30,11 +30,10 @@ (require 'em-hist) (require 'em-tramp) (require 'em-unix) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-test-value nil) diff --git a/test/lisp/eshell/em-dirs-tests.el b/test/lisp/eshell/em-dirs-tests.el index ae3c19aa022..a3e2ad7287c 100644 --- a/test/lisp/eshell/em-dirs-tests.el +++ b/test/lisp/eshell/em-dirs-tests.el @@ -27,11 +27,10 @@ (require 'esh-mode) (require 'eshell) (require 'em-dirs) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) ;;; Tests: diff --git a/test/lisp/eshell/em-extpipe-tests.el b/test/lisp/eshell/em-extpipe-tests.el index ed7ed2a35b8..b2c55256341 100644 --- a/test/lisp/eshell/em-extpipe-tests.el +++ b/test/lisp/eshell/em-extpipe-tests.el @@ -27,11 +27,10 @@ (require 'cl-lib) (require 'ert) (require 'ert-x) +(require 'ert-x) (require 'em-extpipe) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defmacro em-extpipe-tests--deftest (name input &rest body) (declare (indent 2)) diff --git a/test/lisp/eshell/em-glob-tests.el b/test/lisp/eshell/em-glob-tests.el index 4bc32848518..fd5ef537a0b 100644 --- a/test/lisp/eshell/em-glob-tests.el +++ b/test/lisp/eshell/em-glob-tests.el @@ -25,12 +25,11 @@ (require 'tramp) (require 'ert) +(require 'ert-x) (require 'em-glob) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-prefer-lisp-functions) diff --git a/test/lisp/eshell/em-hist-tests.el b/test/lisp/eshell/em-hist-tests.el index f0bb4dd16c3..6288ae071dc 100644 --- a/test/lisp/eshell/em-hist-tests.el +++ b/test/lisp/eshell/em-hist-tests.el @@ -26,11 +26,10 @@ (require 'ert-x) (require 'em-hist) (require 'eshell) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (cl-defun em-hist-test/check-history-file (file-name expected &optional (expected-ring t)) diff --git a/test/lisp/eshell/em-pred-tests.el b/test/lisp/eshell/em-pred-tests.el index a050c6426e9..05ee5d4c4c3 100644 --- a/test/lisp/eshell/em-pred-tests.el +++ b/test/lisp/eshell/em-pred-tests.el @@ -28,11 +28,10 @@ (require 'eshell) (require 'em-glob) (require 'em-pred) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-test-value nil) diff --git a/test/lisp/eshell/em-prompt-tests.el b/test/lisp/eshell/em-prompt-tests.el index 62ad76f644b..73cd6b14478 100644 --- a/test/lisp/eshell/em-prompt-tests.el +++ b/test/lisp/eshell/em-prompt-tests.el @@ -26,11 +26,10 @@ (require 'ert) (require 'eshell) (require 'em-prompt) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defmacro em-prompt-test--with-multiline (&rest body) "Execute BODY with a multiline Eshell prompt." diff --git a/test/lisp/eshell/em-script-tests.el b/test/lisp/eshell/em-script-tests.el index 3259022957b..01dc5fd9a72 100644 --- a/test/lisp/eshell/em-script-tests.el +++ b/test/lisp/eshell/em-script-tests.el @@ -28,11 +28,10 @@ (require 'esh-mode) (require 'eshell) (require 'em-script) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-execute-file-output) diff --git a/test/lisp/eshell/em-unix-tests.el b/test/lisp/eshell/em-unix-tests.el index 59cd0034507..b8969c5568a 100644 --- a/test/lisp/eshell/em-unix-tests.el +++ b/test/lisp/eshell/em-unix-tests.el @@ -30,8 +30,9 @@ (defvar this-directory (file-name-directory (or load-file-name default-directory)))) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" this-directory)) + (ert-resource-file "eshell-tests-helpers")) ;;; Tests: diff --git a/test/lisp/eshell/esh-arg-tests.el b/test/lisp/eshell/esh-arg-tests.el index f498cf6674c..4c34711ec2e 100644 --- a/test/lisp/eshell/esh-arg-tests.el +++ b/test/lisp/eshell/esh-arg-tests.el @@ -26,11 +26,10 @@ (require 'ert) (require 'esh-mode) (require 'eshell) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-test-value nil) diff --git a/test/lisp/eshell/esh-cmd-tests.el b/test/lisp/eshell/esh-cmd-tests.el index 7c04749e7ec..25e6c0fda7c 100644 --- a/test/lisp/eshell/esh-cmd-tests.el +++ b/test/lisp/eshell/esh-cmd-tests.el @@ -26,11 +26,10 @@ (require 'ert) (require 'esh-mode) (require 'eshell) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-test-value nil) diff --git a/test/lisp/eshell/esh-ext-tests.el b/test/lisp/eshell/esh-ext-tests.el index 696e679ccec..c6f5c48ae3e 100644 --- a/test/lisp/eshell/esh-ext-tests.el +++ b/test/lisp/eshell/esh-ext-tests.el @@ -28,11 +28,10 @@ (require 'esh-mode) (require 'esh-ext) (require 'eshell) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) ;;; Tests: diff --git a/test/lisp/eshell/esh-io-tests.el b/test/lisp/eshell/esh-io-tests.el index 0b25ad812fa..d95b52297c7 100644 --- a/test/lisp/eshell/esh-io-tests.el +++ b/test/lisp/eshell/esh-io-tests.el @@ -23,11 +23,10 @@ (require 'ert-x) (require 'esh-mode) (require 'eshell) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-test-value nil) diff --git a/test/lisp/eshell/esh-mode-tests.el b/test/lisp/eshell/esh-mode-tests.el index 052f62d6b9b..b8023029369 100644 --- a/test/lisp/eshell/esh-mode-tests.el +++ b/test/lisp/eshell/esh-mode-tests.el @@ -28,11 +28,10 @@ (require 'eshell) (require 'em-banner) (require 'em-prompt) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) ;;; Tests: diff --git a/test/lisp/eshell/esh-proc-tests.el b/test/lisp/eshell/esh-proc-tests.el index 16f8d82e976..2ef8b9d7536 100644 --- a/test/lisp/eshell/esh-proc-tests.el +++ b/test/lisp/eshell/esh-proc-tests.el @@ -24,11 +24,10 @@ (require 'esh-mode) (require 'eshell) (require 'em-prompt) ; For `eshell-previous-prompt' +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar esh-proc-test--output-cmd (concat "sh -c '" diff --git a/test/lisp/eshell/esh-util-tests.el b/test/lisp/eshell/esh-util-tests.el index 6967dbcf012..39fc6288110 100644 --- a/test/lisp/eshell/esh-util-tests.el +++ b/test/lisp/eshell/esh-util-tests.el @@ -22,11 +22,10 @@ (require 'tramp) (require 'ert) (require 'esh-util) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) ;;; Tests: diff --git a/test/lisp/eshell/esh-var-tests.el b/test/lisp/eshell/esh-var-tests.el index 153f8418153..20575d93407 100644 --- a/test/lisp/eshell/esh-var-tests.el +++ b/test/lisp/eshell/esh-var-tests.el @@ -28,11 +28,10 @@ (require 'esh-mode) (require 'esh-var) (require 'eshell) +(require 'ert-x) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-test-value nil) (defvar eshell-test-begin nil) diff --git a/test/lisp/eshell/eshell-tests-helpers.el b/test/lisp/eshell/eshell-tests-helpers.el deleted file mode 100644 index 870e199f686..00000000000 --- a/test/lisp/eshell/eshell-tests-helpers.el +++ /dev/null @@ -1,233 +0,0 @@ -;;; eshell-tests-helpers.el --- Eshell test suite helpers -*- lexical-binding:t -*- - -;; Copyright (C) 1999-2025 Free Software Foundation, Inc. - -;; Author: John Wiegley - -;; 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 . - -;;; Commentary: - -;; Eshell test suite helpers. - -;;; Code: - -(require 'ert) -(require 'ert-x) -(require 'esh-mode) -(require 'eshell) - -(defvar eshell-aliases-file nil) -(defvar eshell-command-aliases-list nil) -(defvar eshell-history-file-name nil) -(defvar eshell-last-dir-ring-file-name nil) - -(defvar eshell-test--max-wait-time 5 - "The maximum amount of time to wait for a condition to resolve, in seconds. -See `eshell-wait-for'.") - -(defun eshell-tests-remote-accessible-p () - "Return if a test involving remote files can proceed. -If using this function, be sure to load `tramp' near the -beginning of the test file." - (ignore-errors - (and - (file-remote-p ert-remote-temporary-file-directory) - (file-directory-p ert-remote-temporary-file-directory) - (file-writable-p ert-remote-temporary-file-directory)))) - -(defmacro with-temp-eshell-settings (&rest body) - "Configure Eshell to leave no trace behind, and then evaluate BODY." - (declare (indent 0)) - `(ert-with-temp-directory eshell-directory-name - (let (;; We want no history file, so prevent Eshell from falling - ;; back on $HISTFILE. - (process-environment (cons "HISTFILE" process-environment)) - ;; Enable process debug instrumentation. We may be able to - ;; remove this eventually once we're confident that all the - ;; process bugs have been worked out. (At that point, we can - ;; just enable this selectively when needed.) See also - ;; `eshell-test-command-result' below. - (eshell-debug-command (cons 'process eshell-debug-command)) - (eshell-aliases-file nil) - (eshell-command-aliases-list nil) - (eshell-history-file-name nil) - (eshell-last-dir-ring-file-name nil) - (eshell-module-loading-messages nil)) - ,@body))) - -(defmacro with-temp-eshell (&rest body) - "Evaluate BODY in a temporary Eshell buffer." - (declare (indent 0)) - `(save-current-buffer - (with-temp-eshell-settings - (let ((eshell-buffer (eshell t))) - (unwind-protect - (with-current-buffer eshell-buffer - ,@body) - (let (kill-buffer-query-functions) - (kill-buffer eshell-buffer))))))) - -(defmacro eshell-with-temp-buffer (bufname text &rest body) - "Create a temporary buffer containing TEXT and evaluate BODY there. -BUFNAME will be set to the name of the temporary buffer." - (declare (indent 2)) - `(with-temp-buffer - (insert ,text) - (rename-buffer "eshell-temp-buffer" t) - (let ((,bufname (buffer-name))) - ,@body))) - -(defun eshell-wait-for (predicate &optional message) - "Wait until PREDICATE returns non-nil. -If this takes longer than `eshell-test--max-wait-time', raise an -error. MESSAGE is an optional message to use if this times out." - (let ((start (current-time)) - (message (or message "timed out waiting for condition"))) - (while (not (funcall predicate)) - (when (> (float-time (time-since start)) - eshell-test--max-wait-time) - (error message)) - (sit-for 0.1)))) - -(defun eshell-wait-for-subprocess (&optional all) - "Wait until there is no interactive subprocess running in Eshell. -If ALL is non-nil, wait until there are no Eshell subprocesses at -all running. - -If this takes longer than `eshell-test--max-wait-time', -raise an error." - (eshell-wait-for - (lambda () - (not (if all eshell-process-list (eshell-interactive-process-p)))))) - -(defun eshell-get-debug-logs () - "Get debug command logs for displaying on test failures." - (when (get-buffer eshell-debug-command-buffer) - (let ((separator (make-string 40 ?-))) - (with-current-buffer eshell-debug-command-buffer - (string-replace "\f" separator (buffer-string)))))) - -(defun eshell-insert-command (command &optional func) - "Insert a COMMAND at the end of the buffer. -After inserting, call FUNC. If FUNC is nil, instead call -`eshell-send-input'." - (goto-char eshell-last-output-end) - (insert-and-inherit command) - (funcall (or func 'eshell-send-input))) - -(defun eshell-last-input () - "Return the input of the last Eshell command." - (buffer-substring-no-properties - eshell-last-input-start eshell-last-input-end)) - -(defun eshell-last-output () - "Return the output of the last Eshell command." - (buffer-substring-no-properties - (eshell-beginning-of-output) (eshell-end-of-output))) - -(defun eshell-test-file-string (file) - "Return the contents of FILE as a string." - (with-temp-buffer - (insert-file-contents file) - (buffer-string))) - -(defun eshell-match-output (regexp) - "Test whether the output of the last command matches REGEXP." - (string-match-p regexp (eshell-last-output))) - -(defun eshell-match-output--explainer (regexp) - "Explain the result of `eshell-match-output'." - `(mismatched-output - (command ,(eshell-last-input)) - (output ,(eshell-last-output)) - (regexp ,regexp))) - -(put 'eshell-match-output 'ert-explainer #'eshell-match-output--explainer) - -(defun eshell-match-command-output (command regexp &optional func - ignore-errors) - "Insert a COMMAND at the end of the buffer and match the output with REGEXP. -FUNC is the function to call after inserting the text (see -`eshell-insert-command'). - -If IGNORE-ERRORS is non-nil, ignore any errors signaled when -inserting the command." - (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") - (let ((debug-on-error (and (not ignore-errors) debug-on-error))) - (eshell-insert-command command func)) - (eshell-wait-for-subprocess) - (should (eshell-match-output regexp)))) - -(defvar eshell-history-file-name) - -(defun eshell-test-command-result (command) - "Like `eshell-command-result', but not using HOME." - (ert-with-temp-directory eshell-directory-name - (let ((eshell-history-file-name nil) - ;; Enable process debug instrumentation. See - ;; `with-temp-eshell' above. - (eshell-debug-command (cons 'process eshell-debug-command))) - (eshell-command-result command)))) - -(defun eshell-command-result--equal (_command actual expected) - "Compare the ACTUAL result of a COMMAND with its EXPECTED value." - (equal actual expected)) - -(defun eshell-command-result--equal-explainer (command actual expected) - "Explain the result of `eshell-command-result--equal'." - `(nonequal-result - (command ,command) - (result ,actual) - (expected ,expected))) - -(put 'eshell-command-result--equal 'ert-explainer - #'eshell-command-result--equal-explainer) - -(defun eshell-command-result-equal (command result) - "Execute COMMAND non-interactively and compare it to RESULT." - (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") - (let ((eshell-module-loading-messages nil)) - (should (eshell-command-result--equal - command - (eshell-test-command-result command) - result))))) - -(defun eshell-command-result--match (_command regexp actual) - "Compare the ACTUAL result of a COMMAND with REGEXP." - (string-match regexp actual)) - -(defun eshell-command-result--match-explainer (command regexp actual) - "Explain the result of `eshell-command-result--match'." - `(mismatched-result - (command ,command) - (result ,actual) - (regexp ,regexp))) - -(put 'eshell-command-result--match 'ert-explainer - #'eshell-command-result--match-explainer) - -(defun eshell-command-result-match (command regexp) - "Execute COMMAND non-interactively and compare it to REGEXP." - (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") - (let ((eshell-module-loading-messages nil)) - (should (eshell-command-result--match - command regexp - (eshell-test-command-result command)))))) - -(provide 'eshell-tests-helpers) - -;;; eshell-tests-helpers.el ends here diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el index 6565a4be65c..99e4528ab00 100644 --- a/test/lisp/eshell/eshell-tests.el +++ b/test/lisp/eshell/eshell-tests.el @@ -28,11 +28,10 @@ (require 'ert) (require 'ert-x) (require 'esh-mode) +(require 'ert-x) (require 'eshell) (require 'eshell-tests-helpers - (expand-file-name "eshell-tests-helpers" - (file-name-directory (or load-file-name - default-directory)))) + (ert-resource-file "eshell-tests-helpers")) (defvar eshell-test-value nil) (defvar eshell-command-output) diff --git a/test/lisp/eshell/resources/eshell-tests-helpers.el b/test/lisp/eshell/resources/eshell-tests-helpers.el new file mode 100644 index 00000000000..870e199f686 --- /dev/null +++ b/test/lisp/eshell/resources/eshell-tests-helpers.el @@ -0,0 +1,233 @@ +;;; eshell-tests-helpers.el --- Eshell test suite helpers -*- lexical-binding:t -*- + +;; Copyright (C) 1999-2025 Free Software Foundation, Inc. + +;; Author: John Wiegley + +;; 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 . + +;;; Commentary: + +;; Eshell test suite helpers. + +;;; Code: + +(require 'ert) +(require 'ert-x) +(require 'esh-mode) +(require 'eshell) + +(defvar eshell-aliases-file nil) +(defvar eshell-command-aliases-list nil) +(defvar eshell-history-file-name nil) +(defvar eshell-last-dir-ring-file-name nil) + +(defvar eshell-test--max-wait-time 5 + "The maximum amount of time to wait for a condition to resolve, in seconds. +See `eshell-wait-for'.") + +(defun eshell-tests-remote-accessible-p () + "Return if a test involving remote files can proceed. +If using this function, be sure to load `tramp' near the +beginning of the test file." + (ignore-errors + (and + (file-remote-p ert-remote-temporary-file-directory) + (file-directory-p ert-remote-temporary-file-directory) + (file-writable-p ert-remote-temporary-file-directory)))) + +(defmacro with-temp-eshell-settings (&rest body) + "Configure Eshell to leave no trace behind, and then evaluate BODY." + (declare (indent 0)) + `(ert-with-temp-directory eshell-directory-name + (let (;; We want no history file, so prevent Eshell from falling + ;; back on $HISTFILE. + (process-environment (cons "HISTFILE" process-environment)) + ;; Enable process debug instrumentation. We may be able to + ;; remove this eventually once we're confident that all the + ;; process bugs have been worked out. (At that point, we can + ;; just enable this selectively when needed.) See also + ;; `eshell-test-command-result' below. + (eshell-debug-command (cons 'process eshell-debug-command)) + (eshell-aliases-file nil) + (eshell-command-aliases-list nil) + (eshell-history-file-name nil) + (eshell-last-dir-ring-file-name nil) + (eshell-module-loading-messages nil)) + ,@body))) + +(defmacro with-temp-eshell (&rest body) + "Evaluate BODY in a temporary Eshell buffer." + (declare (indent 0)) + `(save-current-buffer + (with-temp-eshell-settings + (let ((eshell-buffer (eshell t))) + (unwind-protect + (with-current-buffer eshell-buffer + ,@body) + (let (kill-buffer-query-functions) + (kill-buffer eshell-buffer))))))) + +(defmacro eshell-with-temp-buffer (bufname text &rest body) + "Create a temporary buffer containing TEXT and evaluate BODY there. +BUFNAME will be set to the name of the temporary buffer." + (declare (indent 2)) + `(with-temp-buffer + (insert ,text) + (rename-buffer "eshell-temp-buffer" t) + (let ((,bufname (buffer-name))) + ,@body))) + +(defun eshell-wait-for (predicate &optional message) + "Wait until PREDICATE returns non-nil. +If this takes longer than `eshell-test--max-wait-time', raise an +error. MESSAGE is an optional message to use if this times out." + (let ((start (current-time)) + (message (or message "timed out waiting for condition"))) + (while (not (funcall predicate)) + (when (> (float-time (time-since start)) + eshell-test--max-wait-time) + (error message)) + (sit-for 0.1)))) + +(defun eshell-wait-for-subprocess (&optional all) + "Wait until there is no interactive subprocess running in Eshell. +If ALL is non-nil, wait until there are no Eshell subprocesses at +all running. + +If this takes longer than `eshell-test--max-wait-time', +raise an error." + (eshell-wait-for + (lambda () + (not (if all eshell-process-list (eshell-interactive-process-p)))))) + +(defun eshell-get-debug-logs () + "Get debug command logs for displaying on test failures." + (when (get-buffer eshell-debug-command-buffer) + (let ((separator (make-string 40 ?-))) + (with-current-buffer eshell-debug-command-buffer + (string-replace "\f" separator (buffer-string)))))) + +(defun eshell-insert-command (command &optional func) + "Insert a COMMAND at the end of the buffer. +After inserting, call FUNC. If FUNC is nil, instead call +`eshell-send-input'." + (goto-char eshell-last-output-end) + (insert-and-inherit command) + (funcall (or func 'eshell-send-input))) + +(defun eshell-last-input () + "Return the input of the last Eshell command." + (buffer-substring-no-properties + eshell-last-input-start eshell-last-input-end)) + +(defun eshell-last-output () + "Return the output of the last Eshell command." + (buffer-substring-no-properties + (eshell-beginning-of-output) (eshell-end-of-output))) + +(defun eshell-test-file-string (file) + "Return the contents of FILE as a string." + (with-temp-buffer + (insert-file-contents file) + (buffer-string))) + +(defun eshell-match-output (regexp) + "Test whether the output of the last command matches REGEXP." + (string-match-p regexp (eshell-last-output))) + +(defun eshell-match-output--explainer (regexp) + "Explain the result of `eshell-match-output'." + `(mismatched-output + (command ,(eshell-last-input)) + (output ,(eshell-last-output)) + (regexp ,regexp))) + +(put 'eshell-match-output 'ert-explainer #'eshell-match-output--explainer) + +(defun eshell-match-command-output (command regexp &optional func + ignore-errors) + "Insert a COMMAND at the end of the buffer and match the output with REGEXP. +FUNC is the function to call after inserting the text (see +`eshell-insert-command'). + +If IGNORE-ERRORS is non-nil, ignore any errors signaled when +inserting the command." + (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") + (let ((debug-on-error (and (not ignore-errors) debug-on-error))) + (eshell-insert-command command func)) + (eshell-wait-for-subprocess) + (should (eshell-match-output regexp)))) + +(defvar eshell-history-file-name) + +(defun eshell-test-command-result (command) + "Like `eshell-command-result', but not using HOME." + (ert-with-temp-directory eshell-directory-name + (let ((eshell-history-file-name nil) + ;; Enable process debug instrumentation. See + ;; `with-temp-eshell' above. + (eshell-debug-command (cons 'process eshell-debug-command))) + (eshell-command-result command)))) + +(defun eshell-command-result--equal (_command actual expected) + "Compare the ACTUAL result of a COMMAND with its EXPECTED value." + (equal actual expected)) + +(defun eshell-command-result--equal-explainer (command actual expected) + "Explain the result of `eshell-command-result--equal'." + `(nonequal-result + (command ,command) + (result ,actual) + (expected ,expected))) + +(put 'eshell-command-result--equal 'ert-explainer + #'eshell-command-result--equal-explainer) + +(defun eshell-command-result-equal (command result) + "Execute COMMAND non-interactively and compare it to RESULT." + (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") + (let ((eshell-module-loading-messages nil)) + (should (eshell-command-result--equal + command + (eshell-test-command-result command) + result))))) + +(defun eshell-command-result--match (_command regexp actual) + "Compare the ACTUAL result of a COMMAND with REGEXP." + (string-match regexp actual)) + +(defun eshell-command-result--match-explainer (command regexp actual) + "Explain the result of `eshell-command-result--match'." + `(mismatched-result + (command ,command) + (result ,actual) + (regexp ,regexp))) + +(put 'eshell-command-result--match 'ert-explainer + #'eshell-command-result--match-explainer) + +(defun eshell-command-result-match (command regexp) + "Execute COMMAND non-interactively and compare it to REGEXP." + (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ") + (let ((eshell-module-loading-messages nil)) + (should (eshell-command-result--match + command regexp + (eshell-test-command-result command)))))) + +(provide 'eshell-tests-helpers) + +;;; eshell-tests-helpers.el ends here