mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-04-27 16:51:06 -07:00
Simplify Eshell handle functions and add tests/documentation
* lisp/eshell/esh-arg.el (eshell-parse-argument-hook): Explain how to use 'eshell-finish-arg'. * lisp/eshell/esh-io.el (eshell-create-handles): Only call 'eshell-get-target' for stderr if necessary. (eshell-protect-handles): Use 'dotimes'. (eshell-set-output-handle): Pass HANDLES and fix an edge case with setting a duplicate TARGET. * test/lisp/eshell/eshell-tests-helpers.el (eshell-with-temp-buffer): New macro. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/quoted-lisp-form) (esh-cmd-test/backquoted-lisp-form) (esh-cmd-test/backquoted-lisp-form/splice): New tests. * test/lisp/eshell/eshell-tests.el (eshell-test/redirect-buffer) (eshell-test/redirect-buffer-escaped): Move to... * test/lisp/eshell/esh-io-tests.el: ... here, and add other I/O tests. * doc/misc/eshell.texi (Arguments): Add documentation for special argument types. (Input/Output): Expand documentation for redirection and pipelines.
This commit is contained in:
parent
5af5ed6c62
commit
1be925faa1
7 changed files with 411 additions and 72 deletions
|
|
@ -256,7 +256,6 @@ as an argument will ``spread'' the elements into multiple arguments:
|
|||
@end example
|
||||
|
||||
@subsection Quoting and escaping
|
||||
|
||||
As with other shells, you can escape special characters and spaces
|
||||
with by prefixing the character with a backslash (@code{\}), or by
|
||||
surrounding the string with apostrophes (@code{''}) or double quotes
|
||||
|
|
@ -268,6 +267,40 @@ When using expansions (@pxref{Expansion}) in an Eshell command, the
|
|||
result may potentially be of any data type. To ensure that the result
|
||||
is always a string, the expansion can be surrounded by double quotes.
|
||||
|
||||
@subsection Special argument types
|
||||
In addition to strings and numbers, Eshell supports a number of
|
||||
special argument types. These let you refer to various other Emacs
|
||||
Lisp data types, such as lists or buffers.
|
||||
|
||||
@table @code
|
||||
|
||||
@item #'@var{lisp-form}
|
||||
This refers to the quoted Emacs Lisp form @var{lisp-form}. Though
|
||||
this looks similar to the ``sharp quote'' syntax for functions
|
||||
(@pxref{Special Read Syntax, , , elisp, The Emacs Lisp Reference
|
||||
Manual}), it instead corresponds to @code{quote} and can be used for
|
||||
any quoted form.@footnote{Eshell would interpret a bare apostrophe
|
||||
(@code{'}) as the start of a single-quoted string.}
|
||||
|
||||
@item `@var{lisp-form}
|
||||
This refers to the backquoted Emacs Lisp form @var{lisp-form}
|
||||
(@pxref{Backquote, , , elisp, The Emacs Lisp Reference Manual}). As
|
||||
in Emacs Lisp, you can use @samp{,} and @samp{,@@} to refer to
|
||||
non-constant values.
|
||||
|
||||
@item #<buffer @var{name}>
|
||||
@itemx #<@var{name}>
|
||||
Return the buffer named @var{name}. This is equivalent to
|
||||
@samp{$(get-buffer-create "@var{name}")} (@pxref{Creating Buffers, , ,
|
||||
elisp, The Emacs Lisp Reference Manual}).
|
||||
|
||||
@item #<process @var{name}>
|
||||
Return the process named @var{name}. This is equivalent to
|
||||
@samp{$(get-process "@var{name}")} (@pxref{Process Information, , ,
|
||||
elisp, The Emacs Lisp Reference Manual}).
|
||||
|
||||
@end table
|
||||
|
||||
@node Built-ins
|
||||
@section Built-in commands
|
||||
Several commands are built-in in Eshell. In order to call the
|
||||
|
|
@ -1560,6 +1593,13 @@ Reverses the order of a list of values.
|
|||
Since Eshell does not communicate with a terminal like most command
|
||||
shells, IO is a little different.
|
||||
|
||||
@menu
|
||||
* Visual Commands::
|
||||
* Redirection::
|
||||
* Pipelines::
|
||||
@end menu
|
||||
|
||||
@node Visual Commands
|
||||
@section Visual Commands
|
||||
If you try to run programs from within Eshell that are not
|
||||
line-oriented, such as programs that use ncurses, you will just get
|
||||
|
|
@ -1592,40 +1632,104 @@ program exits, customize the variable
|
|||
@code{eshell-destroy-buffer-when-process-dies} to a non-@code{nil}
|
||||
value; the default is @code{nil}.
|
||||
|
||||
@node Redirection
|
||||
@section Redirection
|
||||
Redirection is mostly the same in Eshell as it is in other command
|
||||
shells. The output redirection operators @code{>} and @code{>>} as
|
||||
well as pipes are supported, but there is not yet any support for
|
||||
input redirection. Output can also be redirected to buffers, using
|
||||
the @code{>>>} redirection operator, and Elisp functions, using
|
||||
virtual devices.
|
||||
Redirection in Eshell is similar to that of other command shells. You
|
||||
can use the output redirection operators @code{>} and @code{>>}, but
|
||||
there is not yet any support for input redirection. In the cases
|
||||
below, @var{fd} specifies the file descriptor to redirect; if not
|
||||
specified, file descriptor 1 (standard output) will be used by
|
||||
default.
|
||||
|
||||
The buffer redirection operator, @code{>>>}, expects a buffer object
|
||||
on the right-hand side, into which it inserts the output of the
|
||||
left-hand side. e.g., @samp{echo hello >>> #<buffer *scratch*>}
|
||||
inserts the string @code{"hello"} into the @file{*scratch*} buffer.
|
||||
The convenience shorthand variant @samp{#<@var{buffer-name}>}, as in
|
||||
@samp{#<*scratch*>}, is also accepted.
|
||||
@table @code
|
||||
|
||||
@code{eshell-virtual-targets} is a list of mappings of virtual device
|
||||
names to functions. Eshell comes with two virtual devices:
|
||||
@file{/dev/kill}, which sends the text to the kill ring, and
|
||||
@file{/dev/clip}, which sends text to the clipboard.
|
||||
@item > @var{dest}
|
||||
@itemx @var{fd}> @var{dest}
|
||||
Redirect output to @var{dest}, overwriting its contents with the new
|
||||
output.
|
||||
|
||||
@item >> @var{dest}
|
||||
@itemx @var{fd}>> @var{dest}
|
||||
Redirect output to @var{dest}, appending it to the existing contents
|
||||
of @var{dest}.
|
||||
|
||||
@item >>> @var{buffer}
|
||||
@itemx @var{fd}>>> @var{buffer}
|
||||
Redirect output to @var{dest}, inserting it at the current mark if
|
||||
@var{dest} is a buffer, at the beginning of the file if @var{dest} is
|
||||
a file, or otherwise behaving the same as @code{>>}.
|
||||
|
||||
@end table
|
||||
|
||||
Eshell supports redirecting output to several different types of
|
||||
targets:
|
||||
|
||||
@itemize @bullet
|
||||
|
||||
@item
|
||||
files, including virtual targets (see below);
|
||||
|
||||
@item
|
||||
buffers (@pxref{Buffers, , , elisp, GNU Emacs Lisp Reference Manual});
|
||||
|
||||
@item
|
||||
markers (@pxref{Markers, , , elisp, GNU Emacs Lisp Reference Manual});
|
||||
|
||||
@item
|
||||
processes (@pxref{Processes, , , elisp, GNU Emacs Lisp Reference
|
||||
Manual}); and
|
||||
|
||||
@item
|
||||
symbols (@pxref{Symbols, , , elisp, GNU Emacs Lisp Reference Manual}).
|
||||
|
||||
@end itemize
|
||||
|
||||
@subsection Virtual Targets
|
||||
Virtual targets are mapping of device names to functions. Eshell
|
||||
comes with four virtual devices:
|
||||
|
||||
@table @file
|
||||
|
||||
@item /dev/null
|
||||
Does nothing with the output passed to it.
|
||||
|
||||
@item /dev/eshell
|
||||
Writes the text passed to it to the display.
|
||||
|
||||
@item /dev/kill
|
||||
Adds the text passed to it to the kill ring.
|
||||
|
||||
@item /dev/clip
|
||||
Adds the text passed to it to the clipboard.
|
||||
|
||||
@end table
|
||||
|
||||
@vindex eshell-virtual-targets
|
||||
You can, of course, define your own virtual targets. They are defined
|
||||
by adding a list of the form @samp{("/dev/name" @var{function} @var{mode})} to
|
||||
@code{eshell-virtual-targets}. The first element is the device name;
|
||||
@var{function} may be either a lambda or a function name. If
|
||||
@var{mode} is @code{nil}, then the function is the output function; if it is
|
||||
non-@code{nil}, then the function is passed the redirection mode as a
|
||||
symbol--@code{overwrite} for @code{>}, @code{append} for @code{>>}, or
|
||||
@code{insert} for @code{>>>}--and the function is expected to return
|
||||
the output function.
|
||||
by adding a list of the form @samp{("/dev/name" @var{function}
|
||||
@var{mode})} to @code{eshell-virtual-targets}. The first element is
|
||||
the device name; @var{function} may be either a lambda or a function
|
||||
name. If @var{mode} is @code{nil}, then the function is the output
|
||||
function; if it is non-@code{nil}, then the function is passed the
|
||||
redirection mode as a symbol--@code{overwrite} for @code{>},
|
||||
@code{append} for @code{>>}, or @code{insert} for @code{>>>}--and the
|
||||
function is expected to return the output function.
|
||||
|
||||
The output function is called once on each line of output until
|
||||
@code{nil} is passed, indicating end of output.
|
||||
|
||||
@section Running Shell Pipelines Natively
|
||||
@node Pipelines
|
||||
@section Pipelines
|
||||
As with most other shells, Eshell supports pipelines to pass the
|
||||
output of one command the input of the next command. You can pipe
|
||||
commands to each other using the @code{|} operator. For example,
|
||||
|
||||
@example
|
||||
~ $ echo hello | rev
|
||||
olleh
|
||||
@end example
|
||||
|
||||
@subsection Running Shell Pipelines Natively
|
||||
When constructing shell pipelines that will move a lot of data, it is
|
||||
a good idea to bypass Eshell's own pipelining support and use the
|
||||
operating system shell's instead. This is especially relevant when
|
||||
|
|
|
|||
|
|
@ -147,6 +147,10 @@ return the result of the parse as a sexp. It is also responsible for
|
|||
moving the point forward to reflect the amount of input text that was
|
||||
parsed.
|
||||
|
||||
If the hook determines that it has reached the end of an argument, it
|
||||
should call `eshell-finish-arg' to complete processing of the current
|
||||
argument and proceed to the next.
|
||||
|
||||
If no function handles the current character at point, it will be
|
||||
treated as a literal character."
|
||||
:type 'hook
|
||||
|
|
|
|||
|
|
@ -236,22 +236,21 @@ The default location for standard output and standard error will go to
|
|||
STDOUT and STDERR, respectively.
|
||||
OUTPUT-MODE and ERROR-MODE are either `overwrite', `append' or `insert';
|
||||
a nil value of mode defaults to `insert'."
|
||||
(let ((handles (make-vector eshell-number-of-handles nil))
|
||||
(output-target (eshell-get-target stdout output-mode))
|
||||
(error-target (eshell-get-target stderr error-mode)))
|
||||
(let* ((handles (make-vector eshell-number-of-handles nil))
|
||||
(output-target (eshell-get-target stdout output-mode))
|
||||
(error-target (if stderr
|
||||
(eshell-get-target stderr error-mode)
|
||||
output-target)))
|
||||
(aset handles eshell-output-handle (cons output-target 1))
|
||||
(aset handles eshell-error-handle
|
||||
(cons (if stderr error-target output-target) 1))
|
||||
(aset handles eshell-error-handle (cons error-target 1))
|
||||
handles))
|
||||
|
||||
(defun eshell-protect-handles (handles)
|
||||
"Protect the handles in HANDLES from a being closed."
|
||||
(let ((idx 0))
|
||||
(while (< idx eshell-number-of-handles)
|
||||
(if (aref handles idx)
|
||||
(setcdr (aref handles idx)
|
||||
(1+ (cdr (aref handles idx)))))
|
||||
(setq idx (1+ idx))))
|
||||
(dotimes (idx eshell-number-of-handles)
|
||||
(when (aref handles idx)
|
||||
(setcdr (aref handles idx)
|
||||
(1+ (cdr (aref handles idx))))))
|
||||
handles)
|
||||
|
||||
(defun eshell-close-handles (&optional exit-code result handles)
|
||||
|
|
@ -278,6 +277,24 @@ the value already set in `eshell-last-command-result'."
|
|||
(eshell-close-target target (= eshell-last-command-status 0)))
|
||||
(setcar handle nil))))))
|
||||
|
||||
(defun eshell-set-output-handle (index mode &optional target handles)
|
||||
"Set handle INDEX for the current HANDLES to point to TARGET using MODE.
|
||||
If HANDLES is nil, use `eshell-current-handles'."
|
||||
(when target
|
||||
(let ((handles (or handles eshell-current-handles)))
|
||||
(if (and (stringp target)
|
||||
(string= target (null-device)))
|
||||
(aset handles index nil)
|
||||
(let ((where (eshell-get-target target mode))
|
||||
(current (car (aref handles index))))
|
||||
(if (listp current)
|
||||
(unless (member where current)
|
||||
(setq current (append current (list where))))
|
||||
(setq current (list where)))
|
||||
(if (not (aref handles index))
|
||||
(aset handles index (cons nil 1)))
|
||||
(setcar (aref handles index) current))))))
|
||||
|
||||
(defun eshell-close-target (target status)
|
||||
"Close an output TARGET, passing STATUS as the result.
|
||||
STATUS should be non-nil on successful termination of the output."
|
||||
|
|
@ -390,22 +407,6 @@ it defaults to `insert'."
|
|||
(error "Invalid redirection target: %s"
|
||||
(eshell-stringify target)))))
|
||||
|
||||
(defun eshell-set-output-handle (index mode &optional target)
|
||||
"Set handle INDEX, using MODE, to point to TARGET."
|
||||
(when target
|
||||
(if (and (stringp target)
|
||||
(string= target (null-device)))
|
||||
(aset eshell-current-handles index nil)
|
||||
(let ((where (eshell-get-target target mode))
|
||||
(current (car (aref eshell-current-handles index))))
|
||||
(if (and (listp current)
|
||||
(not (member where current)))
|
||||
(setq current (append current (list where)))
|
||||
(setq current (list where)))
|
||||
(if (not (aref eshell-current-handles index))
|
||||
(aset eshell-current-handles index (cons nil 1)))
|
||||
(setcar (aref eshell-current-handles index) current)))))
|
||||
|
||||
(defun eshell-interactive-output-p ()
|
||||
"Return non-nil if current handles are bound for interactive display."
|
||||
(and (eq (car (aref eshell-current-handles
|
||||
|
|
|
|||
|
|
@ -73,6 +73,25 @@ Test that trailing arguments outside the subcommand are ignored.
|
|||
e.g. \"{(+ 1 2)} 3\" => 3"
|
||||
(eshell-command-result-equal "{(+ 1 2)} 3" 3))
|
||||
|
||||
|
||||
;; Lisp forms
|
||||
|
||||
(ert-deftest esh-cmd-test/quoted-lisp-form ()
|
||||
"Test parsing of a quoted Lisp form."
|
||||
(eshell-command-result-equal "echo #'(1 2)" '(1 2)))
|
||||
|
||||
(ert-deftest esh-cmd-test/backquoted-lisp-form ()
|
||||
"Test parsing of a backquoted Lisp form."
|
||||
(let ((eshell-test-value 42))
|
||||
(eshell-command-result-equal "echo `(answer ,eshell-test-value)"
|
||||
'(answer 42))))
|
||||
|
||||
(ert-deftest esh-cmd-test/backquoted-lisp-form/splice ()
|
||||
"Test parsing of a backquoted Lisp form using splicing."
|
||||
(let ((eshell-test-value '(2 3)))
|
||||
(eshell-command-result-equal "echo `(1 ,@eshell-test-value)"
|
||||
'(1 2 3))))
|
||||
|
||||
|
||||
;; Logical operators
|
||||
|
||||
|
|
|
|||
220
test/lisp/eshell/esh-io-tests.el
Normal file
220
test/lisp/eshell/esh-io-tests.el
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
;;; esh-io-tests.el --- esh-io test suite -*- lexical-binding:t -*-
|
||||
|
||||
;; Copyright (C) 2022 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/>.
|
||||
|
||||
;;; 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)
|
||||
|
||||
(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/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"))))
|
||||
|
||||
(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")))))
|
||||
|
||||
|
||||
;; 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"))))
|
||||
|
||||
|
||||
;; Virtual targets
|
||||
|
||||
(ert-deftest esh-io-test/virtual-dev-eshell ()
|
||||
"Check that redirecting to /dev/eshell works."
|
||||
(with-temp-eshell
|
||||
(eshell-match-command-output "echo hi > /dev/eshell" "hi")))
|
||||
|
||||
(ert-deftest esh-io-test/virtual-dev-kill ()
|
||||
"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"))))
|
||||
|
||||
;;; esh-io-tests.el ends here
|
||||
|
|
@ -51,6 +51,16 @@ See `eshell-wait-for-subprocess'.")
|
|||
(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-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
|
||||
|
|
|
|||
|
|
@ -105,25 +105,6 @@
|
|||
(format template "format \"%s\" eshell-in-pipeline-p")
|
||||
"nil")))
|
||||
|
||||
(ert-deftest eshell-test/redirect-buffer ()
|
||||
"Check that piping to a buffer works"
|
||||
(with-temp-buffer
|
||||
(rename-buffer "eshell-temp-buffer" t)
|
||||
(let ((bufname (buffer-name)))
|
||||
(with-temp-eshell
|
||||
(eshell-insert-command (format "echo hi > #<%s>" bufname)))
|
||||
(should (equal (buffer-string) "hi")))))
|
||||
|
||||
(ert-deftest eshell-test/redirect-buffer-escaped ()
|
||||
"Check that piping 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 eshell-test/escape-nonspecial ()
|
||||
"Test that \"\\c\" and \"c\" are equivalent when \"c\" is not a
|
||||
special character."
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue