2022-07-09 10:34:31 -07:00
|
|
|
|
;;; esh-io-tests.el --- esh-io test suite -*- lexical-binding:t -*-
|
|
|
|
|
|
2024-01-02 09:47:10 +08:00
|
|
|
|
;; Copyright (C) 2022-2024 Free Software Foundation, Inc.
|
2022-07-09 10:34:31 -07:00
|
|
|
|
|
|
|
|
|
;; 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/>.
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(require 'ert)
|
|
|
|
|
(require 'ert-x)
|
|
|
|
|
(require 'esh-mode)
|
|
|
|
|
(require 'eshell)
|
|
|
|
|
|
|
|
|
|
(require 'eshell-tests-helpers
|
|
|
|
|
(expand-file-name "eshell-tests-helpers"
|
|
|
|
|
(file-name-directory (or load-file-name
|
|
|
|
|
default-directory))))
|
|
|
|
|
|
|
|
|
|
(defvar eshell-test-value nil)
|
|
|
|
|
|
2023-03-11 18:44:43 -08:00
|
|
|
|
(defvar eshell-test-value-with-fun nil)
|
|
|
|
|
(defun eshell-test-value-with-fun ())
|
|
|
|
|
|
2022-07-09 10:34:31 -07:00
|
|
|
|
(defun eshell/test-output ()
|
|
|
|
|
"Write some test output separately to stdout and stderr."
|
|
|
|
|
(eshell-printn "stdout")
|
|
|
|
|
(eshell-errorn "stderr"))
|
|
|
|
|
|
|
|
|
|
;;; Tests:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Basic redirection
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-file/overwrite ()
|
|
|
|
|
"Check that redirecting to a file in overwrite mode works."
|
|
|
|
|
(ert-with-temp-file temp-file
|
|
|
|
|
:text "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo new > %s" temp-file)))
|
|
|
|
|
(should (equal (eshell-test-file-string temp-file) "new"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-file/append ()
|
|
|
|
|
"Check that redirecting to a file in append mode works."
|
|
|
|
|
(ert-with-temp-file temp-file
|
|
|
|
|
:text "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo new >> %s" temp-file)))
|
|
|
|
|
(should (equal (eshell-test-file-string temp-file) "oldnew"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-file/insert ()
|
|
|
|
|
"Check that redirecting to a file in insert works."
|
|
|
|
|
(ert-with-temp-file temp-file
|
|
|
|
|
:text "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo new >>> %s" temp-file)))
|
|
|
|
|
(should (equal (eshell-test-file-string temp-file) "newold"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-buffer/overwrite ()
|
|
|
|
|
"Check that redirecting to a buffer in overwrite mode works."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo new > #<%s>" bufname)))
|
|
|
|
|
(should (equal (buffer-string) "new"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-buffer/append ()
|
|
|
|
|
"Check that redirecting to a buffer in append mode works."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo new >> #<%s>" bufname)))
|
|
|
|
|
(should (equal (buffer-string) "oldnew"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-buffer/insert ()
|
|
|
|
|
"Check that redirecting to a buffer in insert mode works."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo new >>> #<%s>" bufname)))
|
|
|
|
|
(should (equal (buffer-string) "newold"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-buffer/escaped ()
|
|
|
|
|
"Check that redirecting to a buffer with escaped characters works."
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(rename-buffer "eshell\\temp\\buffer" t)
|
|
|
|
|
(let ((bufname (buffer-name)))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo hi > #<%s>"
|
|
|
|
|
(string-replace "\\" "\\\\" bufname))))
|
|
|
|
|
(should (equal (buffer-string) "hi")))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-symbol/overwrite ()
|
|
|
|
|
"Check that redirecting to a symbol in overwrite mode works."
|
|
|
|
|
(let ((eshell-test-value "old"))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command "echo new > #'eshell-test-value"))
|
|
|
|
|
(should (equal eshell-test-value "new"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-symbol/append ()
|
|
|
|
|
"Check that redirecting to a symbol in append mode works."
|
|
|
|
|
(let ((eshell-test-value "old"))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command "echo new >> #'eshell-test-value"))
|
|
|
|
|
(should (equal eshell-test-value "oldnew"))))
|
|
|
|
|
|
2023-03-11 18:44:43 -08:00
|
|
|
|
(ert-deftest esh-io-test/redirect-symbol/with-function-slot ()
|
|
|
|
|
"Check that redirecting to a symbol with function slot set works."
|
|
|
|
|
(let ((eshell-test-value-with-fun))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command "echo hi > #'eshell-test-value-with-fun"))
|
|
|
|
|
(should (equal eshell-test-value-with-fun "hi"))))
|
|
|
|
|
|
2022-07-09 10:34:31 -07:00
|
|
|
|
(ert-deftest esh-io-test/redirect-marker ()
|
|
|
|
|
"Check that redirecting to a marker works."
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(let ((eshell-test-value (point-marker)))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command "echo hi > $eshell-test-value"))
|
|
|
|
|
(should (equal (buffer-string) "hi")))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-multiple ()
|
|
|
|
|
"Check that redirecting to multiple targets works."
|
|
|
|
|
(let ((eshell-test-value "old"))
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "echo new > #<%s> > #'eshell-test-value"
|
|
|
|
|
bufname)))
|
|
|
|
|
(should (equal (buffer-string) "new"))
|
|
|
|
|
(should (equal eshell-test-value "new")))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-multiple/repeat ()
|
|
|
|
|
"Check that redirecting to multiple targets works when repeating a target."
|
|
|
|
|
(let ((eshell-test-value "old"))
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command
|
|
|
|
|
(format "echo new > #<%s> > #'eshell-test-value > #<%s>"
|
|
|
|
|
bufname bufname)))
|
|
|
|
|
(should (equal (buffer-string) "new"))
|
|
|
|
|
(should (equal eshell-test-value "new")))))
|
|
|
|
|
|
2022-12-20 09:39:07 -08:00
|
|
|
|
(ert-deftest esh-io-test/redirect-subcommands ()
|
|
|
|
|
"Check that redirecting subcommands applies to all subcommands."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command (format "{echo foo; echo bar} > #<%s>" bufname)))
|
|
|
|
|
(should (equal (buffer-string) "foobar"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-subcommands/override ()
|
|
|
|
|
"Check that redirecting subcommands applies to all subcommands.
|
|
|
|
|
Include a redirect to another location in the subcommand to
|
|
|
|
|
ensure only its statement is redirected."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(eshell-with-temp-buffer bufname-2 "also old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command
|
|
|
|
|
(format "{echo foo; echo bar > #<%s>; echo baz} > #<%s>"
|
|
|
|
|
bufname-2 bufname)))
|
|
|
|
|
(should (equal (buffer-string) "bar")))
|
|
|
|
|
(should (equal (buffer-string) "foobaz"))))
|
|
|
|
|
|
2022-12-20 13:47:20 -08:00
|
|
|
|
(ert-deftest esh-io-test/redirect-subcommands/dev-null ()
|
|
|
|
|
"Check that redirecting subcommands applies to all subcommands.
|
|
|
|
|
Include a redirect to /dev/null to ensure it only applies to its
|
|
|
|
|
statement."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command
|
|
|
|
|
(format "{echo foo; echo bar > /dev/null; echo baz} > #<%s>"
|
|
|
|
|
bufname)))
|
|
|
|
|
(should (equal (buffer-string) "foobaz"))))
|
|
|
|
|
|
2022-12-20 09:39:07 -08:00
|
|
|
|
(ert-deftest esh-io-test/redirect-subcommands/interpolated ()
|
|
|
|
|
"Check that redirecting interpolated subcommands applies to all subcommands."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command
|
|
|
|
|
(format "echo ${echo foo; echo bar} > #<%s>" bufname)))
|
|
|
|
|
(should (equal (buffer-string) "foobar"))))
|
|
|
|
|
|
2022-07-09 10:34:31 -07:00
|
|
|
|
|
|
|
|
|
;; Redirecting specific handles
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-stdout ()
|
|
|
|
|
"Check that redirecting to stdout doesn't redirect stderr."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output > #<%s>" bufname)
|
|
|
|
|
"stderr\n"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\n")))
|
|
|
|
|
;; Also check explicitly specifying the stdout fd.
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output 1> #<%s>" bufname)
|
|
|
|
|
"stderr\n"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\n"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-stderr/overwrite ()
|
|
|
|
|
"Check that redirecting to stderr doesn't redirect stdout."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output 2> #<%s>" bufname)
|
|
|
|
|
"stdout\n"))
|
|
|
|
|
(should (equal (buffer-string) "stderr\n"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-stderr/append ()
|
|
|
|
|
"Check that redirecting to stderr doesn't redirect stdout."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output 2>> #<%s>" bufname)
|
|
|
|
|
"stdout\n"))
|
|
|
|
|
(should (equal (buffer-string) "oldstderr\n"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-stderr/insert ()
|
|
|
|
|
"Check that redirecting to stderr doesn't redirect stdout."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output 2>>> #<%s>" bufname)
|
|
|
|
|
"stdout\n"))
|
|
|
|
|
(should (equal (buffer-string) "stderr\nold"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-stdout-and-stderr ()
|
|
|
|
|
"Check that redirecting to both stdout and stderr works."
|
|
|
|
|
(eshell-with-temp-buffer bufname-1 "old"
|
|
|
|
|
(eshell-with-temp-buffer bufname-2 "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output > #<%s> 2> #<%s>"
|
|
|
|
|
bufname-1 bufname-2)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "stderr\n")))
|
|
|
|
|
(should (equal (buffer-string) "stdout\n"))))
|
|
|
|
|
|
2022-07-09 16:26:55 -07:00
|
|
|
|
(ert-deftest esh-io-test/redirect-all/overwrite ()
|
|
|
|
|
"Check that redirecting to stdout and stderr via shorthand works."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output &> #<%s>" bufname)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\nstderr\n")))
|
|
|
|
|
;; Also check the alternate (and less-preferred in Bash) `>&' syntax.
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output >& #<%s>" bufname)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\nstderr\n"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-all/append ()
|
|
|
|
|
"Check that redirecting to stdout and stderr via shorthand works."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output &>> #<%s>" bufname)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "oldstdout\nstderr\n")))
|
|
|
|
|
;; Also check the alternate (and less-preferred in Bash) `>>&' syntax.
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output >>& #<%s>" bufname)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "oldstdout\nstderr\n"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-all/insert ()
|
|
|
|
|
"Check that redirecting to stdout and stderr via shorthand works."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output &>>> #<%s>" bufname)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\nstderr\nold")))
|
|
|
|
|
;; Also check the alternate `>>>&' syntax.
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output >>>& #<%s>" bufname)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\nstderr\nold"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-copy ()
|
|
|
|
|
"Check that redirecting stdout and then copying stdout to stderr works.
|
|
|
|
|
This should redirect both stdout and stderr to the same place."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output > #<%s> 2>&1" bufname)
|
|
|
|
|
"\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\nstderr\n"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/redirect-copy-first ()
|
|
|
|
|
"Check that copying stdout to stderr and then redirecting stdout works.
|
|
|
|
|
This should redirect stdout to a buffer, and stderr to where
|
|
|
|
|
stdout originally pointed (the terminal)."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output (format "test-output 2>&1 > #<%s>" bufname)
|
|
|
|
|
"stderr\n"))
|
|
|
|
|
(should (equal (buffer-string) "stdout\n"))))
|
|
|
|
|
|
Fix reference-counting of Eshell I/O handles
This ensures that output targets in Eshell are only closed when Eshell
is actually done with them. In particular, this means that
"{ echo foo; echo bar } | rev" prints "raboof" as expected
(bug#59545).
* lisp/eshell/esh-io.el (eshell-create-handles): Structure the handles
differently so the targets and their ref-count can be shared.
(eshell-duplicate-handles): Reimplement this to share targets between
the original and new handle sets. Add STEAL-P argument.
(eshell-protect-handles, eshell-copy-output-handle)
(eshell-interactive-output-p, eshell-output-object): Account for
changes to the handle structure.
(eshell-close-handle): New function...
(eshell-close-handles, eshell-set-output-handle): ... use it.
(eshell-get-targets): Remove. This only existed to make the previous
implementation of 'eshell-duplicate-handles' work.
* lisp/eshell/esh-cmd.el (eshell-with-copied-handles): New argument
STEAL-P.
(eshell-do-pipelines): Use STEAL-P for the last item in the pipeline.
(eshell-parse-command): Don't copy handles for the last command in the
list; explain why we can't use STEAL-P here.
(eshell-eval-command): When queuing input, set 'eshell-command-body'
and 'eshell-test-body' for the 'if' conditional (see
'eshell-do-eval').
* test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-pipe): Split
into...
(esh-io-test/pipeline/default, esh-io-test/pipeline/all): ... these.
(esh-io-test/pipeline/subcommands): New test.
* test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop-pipe)
(esh-cmd-test/while-loop-pipe, esh-cmd-test/if-statement-pipe)
esh-cmd-test/if-else-statement-pipe): New tests.
(esh-cmd-test/while-loop): Use 'pop' to simplify the test a bit.
* test/lisp/eshell/eshell-test-helpers.el
(eshell-test--max-subprocess-time): Rename to...
(eshell-test--max-wait-time): ... this.
(eshell-wait-for): New function...
(eshell-wait-for-subprocess): ... use it.
* test/lisp/eshell/eshell-tests.el (eshell-test/queue-input): Fix this
test. Previously, it didn't correctly verify that the original
command completed.
* test/lisp/eshell/em-tramp-tests.el
(em-tramp-test/should-replace-command): New macro...
(em-tramp-test/su-default, em-tramp-test/su-user)
(em-tramp-test/su-login, em-tramp-test/sudo-shell)
(em-tramp-test/sudo-user-shell, em-tramp-test/doas-shell)
(em-tramp-test/doas-user-shell): ... use it.
2022-12-24 14:31:50 -08:00
|
|
|
|
|
|
|
|
|
;; Pipelines
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/pipeline/default ()
|
|
|
|
|
"Check that `|' only pipes stdout."
|
|
|
|
|
(skip-unless (executable-find "rev"))
|
2022-07-09 16:26:55 -07:00
|
|
|
|
(eshell-command-result-equal "test-output | rev"
|
Fix reference-counting of Eshell I/O handles
This ensures that output targets in Eshell are only closed when Eshell
is actually done with them. In particular, this means that
"{ echo foo; echo bar } | rev" prints "raboof" as expected
(bug#59545).
* lisp/eshell/esh-io.el (eshell-create-handles): Structure the handles
differently so the targets and their ref-count can be shared.
(eshell-duplicate-handles): Reimplement this to share targets between
the original and new handle sets. Add STEAL-P argument.
(eshell-protect-handles, eshell-copy-output-handle)
(eshell-interactive-output-p, eshell-output-object): Account for
changes to the handle structure.
(eshell-close-handle): New function...
(eshell-close-handles, eshell-set-output-handle): ... use it.
(eshell-get-targets): Remove. This only existed to make the previous
implementation of 'eshell-duplicate-handles' work.
* lisp/eshell/esh-cmd.el (eshell-with-copied-handles): New argument
STEAL-P.
(eshell-do-pipelines): Use STEAL-P for the last item in the pipeline.
(eshell-parse-command): Don't copy handles for the last command in the
list; explain why we can't use STEAL-P here.
(eshell-eval-command): When queuing input, set 'eshell-command-body'
and 'eshell-test-body' for the 'if' conditional (see
'eshell-do-eval').
* test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-pipe): Split
into...
(esh-io-test/pipeline/default, esh-io-test/pipeline/all): ... these.
(esh-io-test/pipeline/subcommands): New test.
* test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop-pipe)
(esh-cmd-test/while-loop-pipe, esh-cmd-test/if-statement-pipe)
esh-cmd-test/if-else-statement-pipe): New tests.
(esh-cmd-test/while-loop): Use 'pop' to simplify the test a bit.
* test/lisp/eshell/eshell-test-helpers.el
(eshell-test--max-subprocess-time): Rename to...
(eshell-test--max-wait-time): ... this.
(eshell-wait-for): New function...
(eshell-wait-for-subprocess): ... use it.
* test/lisp/eshell/eshell-tests.el (eshell-test/queue-input): Fix this
test. Previously, it didn't correctly verify that the original
command completed.
* test/lisp/eshell/em-tramp-tests.el
(em-tramp-test/should-replace-command): New macro...
(em-tramp-test/su-default, em-tramp-test/su-user)
(em-tramp-test/su-login, em-tramp-test/sudo-shell)
(em-tramp-test/sudo-user-shell, em-tramp-test/doas-shell)
(em-tramp-test/doas-user-shell): ... use it.
2022-12-24 14:31:50 -08:00
|
|
|
|
"stderr\ntuodts\n"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/pipeline/all ()
|
|
|
|
|
"Check that `|&' only pipes stdout and stderr."
|
|
|
|
|
(skip-unless (executable-find "rev"))
|
2022-07-09 16:26:55 -07:00
|
|
|
|
(eshell-command-result-equal "test-output |& rev"
|
|
|
|
|
"tuodts\nrredts\n"))
|
|
|
|
|
|
Fix reference-counting of Eshell I/O handles
This ensures that output targets in Eshell are only closed when Eshell
is actually done with them. In particular, this means that
"{ echo foo; echo bar } | rev" prints "raboof" as expected
(bug#59545).
* lisp/eshell/esh-io.el (eshell-create-handles): Structure the handles
differently so the targets and their ref-count can be shared.
(eshell-duplicate-handles): Reimplement this to share targets between
the original and new handle sets. Add STEAL-P argument.
(eshell-protect-handles, eshell-copy-output-handle)
(eshell-interactive-output-p, eshell-output-object): Account for
changes to the handle structure.
(eshell-close-handle): New function...
(eshell-close-handles, eshell-set-output-handle): ... use it.
(eshell-get-targets): Remove. This only existed to make the previous
implementation of 'eshell-duplicate-handles' work.
* lisp/eshell/esh-cmd.el (eshell-with-copied-handles): New argument
STEAL-P.
(eshell-do-pipelines): Use STEAL-P for the last item in the pipeline.
(eshell-parse-command): Don't copy handles for the last command in the
list; explain why we can't use STEAL-P here.
(eshell-eval-command): When queuing input, set 'eshell-command-body'
and 'eshell-test-body' for the 'if' conditional (see
'eshell-do-eval').
* test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-pipe): Split
into...
(esh-io-test/pipeline/default, esh-io-test/pipeline/all): ... these.
(esh-io-test/pipeline/subcommands): New test.
* test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop-pipe)
(esh-cmd-test/while-loop-pipe, esh-cmd-test/if-statement-pipe)
esh-cmd-test/if-else-statement-pipe): New tests.
(esh-cmd-test/while-loop): Use 'pop' to simplify the test a bit.
* test/lisp/eshell/eshell-test-helpers.el
(eshell-test--max-subprocess-time): Rename to...
(eshell-test--max-wait-time): ... this.
(eshell-wait-for): New function...
(eshell-wait-for-subprocess): ... use it.
* test/lisp/eshell/eshell-tests.el (eshell-test/queue-input): Fix this
test. Previously, it didn't correctly verify that the original
command completed.
* test/lisp/eshell/em-tramp-tests.el
(em-tramp-test/should-replace-command): New macro...
(em-tramp-test/su-default, em-tramp-test/su-user)
(em-tramp-test/su-login, em-tramp-test/sudo-shell)
(em-tramp-test/sudo-user-shell, em-tramp-test/doas-shell)
(em-tramp-test/doas-user-shell): ... use it.
2022-12-24 14:31:50 -08:00
|
|
|
|
(ert-deftest esh-io-test/pipeline/subcommands ()
|
2023-12-10 13:22:04 +01:00
|
|
|
|
"Check that all commands in a subcommand are properly piped."
|
Fix reference-counting of Eshell I/O handles
This ensures that output targets in Eshell are only closed when Eshell
is actually done with them. In particular, this means that
"{ echo foo; echo bar } | rev" prints "raboof" as expected
(bug#59545).
* lisp/eshell/esh-io.el (eshell-create-handles): Structure the handles
differently so the targets and their ref-count can be shared.
(eshell-duplicate-handles): Reimplement this to share targets between
the original and new handle sets. Add STEAL-P argument.
(eshell-protect-handles, eshell-copy-output-handle)
(eshell-interactive-output-p, eshell-output-object): Account for
changes to the handle structure.
(eshell-close-handle): New function...
(eshell-close-handles, eshell-set-output-handle): ... use it.
(eshell-get-targets): Remove. This only existed to make the previous
implementation of 'eshell-duplicate-handles' work.
* lisp/eshell/esh-cmd.el (eshell-with-copied-handles): New argument
STEAL-P.
(eshell-do-pipelines): Use STEAL-P for the last item in the pipeline.
(eshell-parse-command): Don't copy handles for the last command in the
list; explain why we can't use STEAL-P here.
(eshell-eval-command): When queuing input, set 'eshell-command-body'
and 'eshell-test-body' for the 'if' conditional (see
'eshell-do-eval').
* test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-pipe): Split
into...
(esh-io-test/pipeline/default, esh-io-test/pipeline/all): ... these.
(esh-io-test/pipeline/subcommands): New test.
* test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop-pipe)
(esh-cmd-test/while-loop-pipe, esh-cmd-test/if-statement-pipe)
esh-cmd-test/if-else-statement-pipe): New tests.
(esh-cmd-test/while-loop): Use 'pop' to simplify the test a bit.
* test/lisp/eshell/eshell-test-helpers.el
(eshell-test--max-subprocess-time): Rename to...
(eshell-test--max-wait-time): ... this.
(eshell-wait-for): New function...
(eshell-wait-for-subprocess): ... use it.
* test/lisp/eshell/eshell-tests.el (eshell-test/queue-input): Fix this
test. Previously, it didn't correctly verify that the original
command completed.
* test/lisp/eshell/em-tramp-tests.el
(em-tramp-test/should-replace-command): New macro...
(em-tramp-test/su-default, em-tramp-test/su-user)
(em-tramp-test/su-login, em-tramp-test/sudo-shell)
(em-tramp-test/sudo-user-shell, em-tramp-test/doas-shell)
(em-tramp-test/doas-user-shell): ... use it.
2022-12-24 14:31:50 -08:00
|
|
|
|
(skip-unless (executable-find "rev"))
|
2022-12-30 10:40:08 -08:00
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output "{echo foo; echo bar} | rev"
|
|
|
|
|
"\\`raboof\n?")))
|
Fix reference-counting of Eshell I/O handles
This ensures that output targets in Eshell are only closed when Eshell
is actually done with them. In particular, this means that
"{ echo foo; echo bar } | rev" prints "raboof" as expected
(bug#59545).
* lisp/eshell/esh-io.el (eshell-create-handles): Structure the handles
differently so the targets and their ref-count can be shared.
(eshell-duplicate-handles): Reimplement this to share targets between
the original and new handle sets. Add STEAL-P argument.
(eshell-protect-handles, eshell-copy-output-handle)
(eshell-interactive-output-p, eshell-output-object): Account for
changes to the handle structure.
(eshell-close-handle): New function...
(eshell-close-handles, eshell-set-output-handle): ... use it.
(eshell-get-targets): Remove. This only existed to make the previous
implementation of 'eshell-duplicate-handles' work.
* lisp/eshell/esh-cmd.el (eshell-with-copied-handles): New argument
STEAL-P.
(eshell-do-pipelines): Use STEAL-P for the last item in the pipeline.
(eshell-parse-command): Don't copy handles for the last command in the
list; explain why we can't use STEAL-P here.
(eshell-eval-command): When queuing input, set 'eshell-command-body'
and 'eshell-test-body' for the 'if' conditional (see
'eshell-do-eval').
* test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-pipe): Split
into...
(esh-io-test/pipeline/default, esh-io-test/pipeline/all): ... these.
(esh-io-test/pipeline/subcommands): New test.
* test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop-pipe)
(esh-cmd-test/while-loop-pipe, esh-cmd-test/if-statement-pipe)
esh-cmd-test/if-else-statement-pipe): New tests.
(esh-cmd-test/while-loop): Use 'pop' to simplify the test a bit.
* test/lisp/eshell/eshell-test-helpers.el
(eshell-test--max-subprocess-time): Rename to...
(eshell-test--max-wait-time): ... this.
(eshell-wait-for): New function...
(eshell-wait-for-subprocess): ... use it.
* test/lisp/eshell/eshell-tests.el (eshell-test/queue-input): Fix this
test. Previously, it didn't correctly verify that the original
command completed.
* test/lisp/eshell/em-tramp-tests.el
(em-tramp-test/should-replace-command): New macro...
(em-tramp-test/su-default, em-tramp-test/su-user)
(em-tramp-test/su-login, em-tramp-test/sudo-shell)
(em-tramp-test/sudo-user-shell, em-tramp-test/doas-shell)
(em-tramp-test/doas-user-shell): ... use it.
2022-12-24 14:31:50 -08:00
|
|
|
|
|
2023-09-06 17:00:59 -07:00
|
|
|
|
(ert-deftest esh-io-test/pipeline/stdin-to-head ()
|
|
|
|
|
"Check that standard input is sent to the head process in a pipeline."
|
|
|
|
|
(skip-unless (and (executable-find "tr")
|
|
|
|
|
(executable-find "rev")))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command "tr a-z A-Z | rev")
|
|
|
|
|
(eshell-insert-command "hello")
|
|
|
|
|
(eshell-send-eof-to-process)
|
|
|
|
|
(eshell-wait-for-subprocess)
|
|
|
|
|
(should (eshell-match-output "OLLEH\n"))))
|
|
|
|
|
|
2022-07-09 10:34:31 -07:00
|
|
|
|
|
|
|
|
|
;; Virtual targets
|
|
|
|
|
|
2022-12-20 13:47:20 -08:00
|
|
|
|
(ert-deftest esh-io-test/virtual/dev-null ()
|
|
|
|
|
"Check that redirecting to /dev/null works."
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output "echo hi > /dev/null" "\\`\\'")))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/virtual/dev-null/multiple ()
|
|
|
|
|
"Check that redirecting to /dev/null works alongside other redirections."
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output
|
|
|
|
|
(format "echo new > /dev/null > #<%s>" bufname) "\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "new")))
|
|
|
|
|
(eshell-with-temp-buffer bufname "old"
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output
|
|
|
|
|
(format "echo new > #<%s> > /dev/null" bufname) "\\`\\'"))
|
|
|
|
|
(should (equal (buffer-string) "new"))))
|
|
|
|
|
|
|
|
|
|
(ert-deftest esh-io-test/virtual/dev-eshell ()
|
2022-07-09 10:34:31 -07:00
|
|
|
|
"Check that redirecting to /dev/eshell works."
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-match-command-output "echo hi > /dev/eshell" "hi")))
|
|
|
|
|
|
2022-12-20 13:47:20 -08:00
|
|
|
|
(ert-deftest esh-io-test/virtual/dev-kill ()
|
2022-07-09 10:34:31 -07:00
|
|
|
|
"Check that redirecting to /dev/kill works."
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command "echo one > /dev/kill")
|
|
|
|
|
(should (equal (car kill-ring) "one"))
|
|
|
|
|
(eshell-insert-command "echo two > /dev/kill")
|
|
|
|
|
(should (equal (car kill-ring) "two"))
|
|
|
|
|
(eshell-insert-command "echo three >> /dev/kill")
|
|
|
|
|
(should (equal (car kill-ring) "twothree"))))
|
|
|
|
|
|
2024-05-23 14:52:07 -07:00
|
|
|
|
(ert-deftest esh-io-test/virtual/device-close ()
|
|
|
|
|
"Check that the close function for `eshell-function-target' works."
|
|
|
|
|
(let* ((data nil)
|
|
|
|
|
(status nil)
|
|
|
|
|
(eshell-virtual-targets
|
|
|
|
|
`(("/dev/virtual"
|
|
|
|
|
,(eshell-function-target-create
|
|
|
|
|
(lambda (d) (setq data d))
|
|
|
|
|
(lambda (s) (setq status s)))
|
|
|
|
|
nil))))
|
|
|
|
|
(with-temp-eshell
|
|
|
|
|
(eshell-insert-command "echo hello > /dev/virtual")
|
|
|
|
|
(should (equal data "hello"))
|
|
|
|
|
(should (equal status t)))))
|
|
|
|
|
|
2022-07-09 10:34:31 -07:00
|
|
|
|
;;; esh-io-tests.el ends here
|