Refactor lush tests: remove duplication, fill coverage gaps, wire into all.lua
- Wire all 11 lush test files into testes/all.lua (was only 4) - Delete dead commands-interactive.lua (disabled via os.exit(0)) - Remove duplicate regression test from piping.lua - Add user-defined command tests (lush.commands): basic, int return, table return, error handling, removal, API call - Add lush.subcmd() and lush.interactive() direct call tests - Add argv edge cases: single-quote backslash, \$ escape, unknown escape, adjacent quote concatenation - Add globbing edge cases: brace suffix, no-comma braces, bracket glob, mid-word tilde passthrough - Add signal exit code test (128+9=137) - Add escaped-pipe and empty interactive command tests
This commit is contained in:
@@ -204,6 +204,13 @@ dofile('lush/commands.lua')
|
|||||||
dofile('lush/argv.lua')
|
dofile('lush/argv.lua')
|
||||||
dofile('lush/interpolation.lua')
|
dofile('lush/interpolation.lua')
|
||||||
dofile('lush/envvars.lua')
|
dofile('lush/envvars.lua')
|
||||||
|
dofile('lush/piping.lua')
|
||||||
|
dofile('lush/globbing.lua')
|
||||||
|
dofile('lush/interactive.lua')
|
||||||
|
dofile('lush/builtins.lua')
|
||||||
|
dofile('lush/lushlib.lua')
|
||||||
|
dofile('lush/config.lua')
|
||||||
|
dofile('lush/prompt.lua')
|
||||||
|
|
||||||
if #msgs > 0 then
|
if #msgs > 0 then
|
||||||
local m = table.concat(msgs, "\n ")
|
local m = table.concat(msgs, "\n ")
|
||||||
|
|||||||
@@ -50,4 +50,28 @@ do
|
|||||||
assert(r.stdout == "a\tb\n")
|
assert(r.stdout == "a\tb\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- backslash in single quotes is literal (no escape processing)
|
||||||
|
do
|
||||||
|
local r = `echo 'hello\\nworld'`
|
||||||
|
assert(r.stdout == "hello\\nworld\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- \$ in double quotes → literal $
|
||||||
|
do
|
||||||
|
local r = `echo "\$HOME"`
|
||||||
|
assert(r.stdout == "$HOME\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- unknown escape in double quotes preserved
|
||||||
|
do
|
||||||
|
local r = `echo "\q"`
|
||||||
|
assert(r.stdout == "\\q\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- adjacent quotes form a single token
|
||||||
|
do
|
||||||
|
local r = `echo "hello"'world'`
|
||||||
|
assert(r.stdout == "helloworld\n")
|
||||||
|
end
|
||||||
|
|
||||||
print "OK"
|
print "OK"
|
||||||
|
|||||||
@@ -1,366 +0,0 @@
|
|||||||
-- testes/lush/commands-interactive.lua
|
|
||||||
-- Tests for interactive command execution (issue #10).
|
|
||||||
-- This file serves as a design playground: it documents how bare-word
|
|
||||||
-- commands should behave alongside Lua in both scripts and the REPL.
|
|
||||||
|
|
||||||
print "Skipping interactive commands - not implemented yet"
|
|
||||||
os.exit(0) --[[
|
|
||||||
|
|
||||||
print "testing interactive commands"
|
|
||||||
|
|
||||||
-- ===== RESULT TABLE STRUCTURE =====
|
|
||||||
|
|
||||||
-- basic command, result is a table with code/stdout/stderr
|
|
||||||
do
|
|
||||||
echo hello
|
|
||||||
assert(type(_) == "table")
|
|
||||||
assert(type(_.code) == "number")
|
|
||||||
assert(type(_.stdout) == "string")
|
|
||||||
assert(type(_.stderr) == "string")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== EXIT CODES =====
|
|
||||||
|
|
||||||
-- successful command returns exit code 0
|
|
||||||
do
|
|
||||||
sh -c "exit 0"
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- failed command returns non-zero exit code
|
|
||||||
do
|
|
||||||
sh -c "exit 1"
|
|
||||||
assert(_.code == 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- specific exit codes are preserved
|
|
||||||
do
|
|
||||||
sh -c "exit 42"
|
|
||||||
assert(_.code == 42)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- command not found returns 127
|
|
||||||
do
|
|
||||||
nonexistent_command_xyz_999
|
|
||||||
assert(_.code == 127)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== INTERACTIVE MODE: NO STDOUT/STDERR CAPTURE =====
|
|
||||||
-- interactive commands inherit the terminal; stdout/stderr go directly
|
|
||||||
-- to the user's screen, so _.stdout and _.stderr are always empty.
|
|
||||||
|
|
||||||
do
|
|
||||||
echo hello
|
|
||||||
assert(_.stdout == "")
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
sh -c "echo err >&2"
|
|
||||||
assert(_.stderr == "")
|
|
||||||
assert(_.stdout == "")
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
sh -c "echo out; echo err >&2"
|
|
||||||
assert(_.stdout == "")
|
|
||||||
assert(_.stderr == "")
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
echo hello world
|
|
||||||
assert(_.stdout == "")
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== PARSER HEURISTIC: IDENTIFIER + NON-EXCEPTION TOKEN =====
|
|
||||||
-- the parser detects shell commands when an identifier at statement
|
|
||||||
-- position is followed by a token NOT in the exception list:
|
|
||||||
-- ( string { . : [ = ,
|
|
||||||
|
|
||||||
-- bare identifier, no arguments (next token is keyword/identifier/EOF)
|
|
||||||
do
|
|
||||||
ls
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- identifier + dash flag
|
|
||||||
do
|
|
||||||
ls -la /
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- identifier + slash (path argument)
|
|
||||||
do
|
|
||||||
ls /tmp
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- identifier + identifier (subcommand pattern)
|
|
||||||
do
|
|
||||||
git --version
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== COMMANDS INSIDE LUA BLOCKS =====
|
|
||||||
-- bare commands work anywhere a Lua statement can appear.
|
|
||||||
|
|
||||||
-- inside do/end
|
|
||||||
do
|
|
||||||
echo inside-do
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- inside if/end
|
|
||||||
do
|
|
||||||
if true then
|
|
||||||
echo inside-if
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- inside for/end
|
|
||||||
do
|
|
||||||
for i = 1, 3 do
|
|
||||||
echo loop
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- inside while/end
|
|
||||||
do
|
|
||||||
local n = 0
|
|
||||||
while n < 2 do
|
|
||||||
echo while-loop
|
|
||||||
assert(_.code == 0)
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- inside function body
|
|
||||||
do
|
|
||||||
local function run_cmd()
|
|
||||||
ls /
|
|
||||||
return _.code
|
|
||||||
end
|
|
||||||
assert(run_cmd() == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- nested blocks
|
|
||||||
do
|
|
||||||
if true then
|
|
||||||
for i = 1, 2 do
|
|
||||||
echo nested
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== _ BEHAVIOR =====
|
|
||||||
|
|
||||||
-- _ is overwritten by subsequent commands
|
|
||||||
do
|
|
||||||
sh -c "exit 0"
|
|
||||||
assert(_.code == 0)
|
|
||||||
sh -c "exit 5"
|
|
||||||
assert(_.code == 5)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- _ persists across block boundaries (it's a global)
|
|
||||||
do
|
|
||||||
sh -c "exit 3"
|
|
||||||
end
|
|
||||||
assert(_.code == 3)
|
|
||||||
|
|
||||||
-- ===== RUNTIME FALLBACK: STRING-ARG FUNCTION CALL SYNTAX =====
|
|
||||||
-- echo "hello" parses as Lua echo("hello") because string literal
|
|
||||||
-- is in the exception list. at runtime, echo is nil → "attempt to
|
|
||||||
-- call a nil value" → check PATH → found → run as shell command.
|
|
||||||
|
|
||||||
do
|
|
||||||
echo "hello"
|
|
||||||
assert(_.code == 0)
|
|
||||||
assert(_.stdout == "")
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
printf "hello\n"
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- undefined name NOT in PATH → original Lua error preserved
|
|
||||||
do
|
|
||||||
local ok, err = pcall(function()
|
|
||||||
pirnt "hello"
|
|
||||||
end)
|
|
||||||
assert(not ok)
|
|
||||||
assert(string.find(err, "pirnt"))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== LUA VARIABLE SHADOWS SHELL COMMAND =====
|
|
||||||
-- if a Lua variable with the same name as a shell command is defined,
|
|
||||||
-- the Lua variable wins. the shell fallback only triggers on nil.
|
|
||||||
|
|
||||||
-- string-arg sugar: echo "hello" parses as echo("hello").
|
|
||||||
-- echo is a local function, so Lua calls it — NOT /bin/echo.
|
|
||||||
do
|
|
||||||
local func_called = false
|
|
||||||
local echo = function(x) func_called = true end
|
|
||||||
echo "hello"
|
|
||||||
assert(func_called == true)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- table-arg sugar: same principle with { } syntax
|
|
||||||
do
|
|
||||||
local received = nil
|
|
||||||
local grep = function(t) received = t end
|
|
||||||
grep {"pattern", "file.txt"}
|
|
||||||
assert(received[1] == "pattern")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- paren call: unambiguously Lua, local wins
|
|
||||||
do
|
|
||||||
local func_called = false
|
|
||||||
local ls = function(...) func_called = true end
|
|
||||||
ls("/tmp")
|
|
||||||
assert(func_called == true)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- global function shadows command name
|
|
||||||
do
|
|
||||||
local func_called = false
|
|
||||||
function echo(x) func_called = true end
|
|
||||||
echo "test"
|
|
||||||
assert(func_called == true)
|
|
||||||
echo = nil -- clean up global
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== LUA SYNTAX PRESERVED =====
|
|
||||||
-- all exception-list tokens correctly route to Lua parsing.
|
|
||||||
|
|
||||||
-- multi-line function call: string arg on next line
|
|
||||||
do
|
|
||||||
local function my_func(arg)
|
|
||||||
return arg
|
|
||||||
end
|
|
||||||
|
|
||||||
local r = my_func
|
|
||||||
"hello world"
|
|
||||||
assert(r == "hello world")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- multi-line function call: paren arg on next line
|
|
||||||
do
|
|
||||||
local function add(a, b) return a + b end
|
|
||||||
|
|
||||||
local r = add
|
|
||||||
(1, 2)
|
|
||||||
assert(r == 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- multi-line function call: table arg on next line
|
|
||||||
do
|
|
||||||
local function first(t) return t[1] end
|
|
||||||
|
|
||||||
local r = first
|
|
||||||
{42}
|
|
||||||
assert(r == 42)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- assignment
|
|
||||||
do
|
|
||||||
local x = 5
|
|
||||||
assert(x == 5)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- multi-assignment
|
|
||||||
do
|
|
||||||
local a, b = 1, 2
|
|
||||||
assert(a == 1 and b == 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- field access
|
|
||||||
do
|
|
||||||
local t = {field = 10}
|
|
||||||
assert(t.field == 10)
|
|
||||||
t.field = 20
|
|
||||||
assert(t.field == 20)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- method calls
|
|
||||||
do
|
|
||||||
local s = "hello"
|
|
||||||
assert(s:upper() == "HELLO")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- indexing
|
|
||||||
do
|
|
||||||
local t = {10, 20, 30}
|
|
||||||
assert(t[2] == 20)
|
|
||||||
t[2] = 99
|
|
||||||
assert(t[2] == 99)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- table-arg function call
|
|
||||||
do
|
|
||||||
local function f(t) return t[1] end
|
|
||||||
assert(f {42} == 42)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- keyword-led statements
|
|
||||||
do
|
|
||||||
local x = 1
|
|
||||||
if x == 1 then x = 2 end
|
|
||||||
assert(x == 2)
|
|
||||||
for i = 1, 1 do x = 3 end
|
|
||||||
assert(x == 3)
|
|
||||||
while x > 3 do x = x - 1 end
|
|
||||||
assert(x == 3)
|
|
||||||
repeat x = x - 1 until x == 0
|
|
||||||
assert(x == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== INTERLEAVED LUA AND SHELL =====
|
|
||||||
|
|
||||||
do
|
|
||||||
local x = 10
|
|
||||||
ls /
|
|
||||||
assert(_.code == 0)
|
|
||||||
local y = x + 20
|
|
||||||
assert(y == 30)
|
|
||||||
echo hello
|
|
||||||
assert(_.code == 0)
|
|
||||||
local z = y * 2
|
|
||||||
assert(z == 60)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== EDGE CASES =====
|
|
||||||
|
|
||||||
-- double-dash flags (--) look like Lua comments to the lexer.
|
|
||||||
-- the parser must capture raw source text BEFORE the lexer consumes
|
|
||||||
-- the comment, so the full argument string is preserved.
|
|
||||||
do
|
|
||||||
git --version
|
|
||||||
assert(_.code == 0)
|
|
||||||
ls --color=auto /tmp
|
|
||||||
assert(_.code == 0) -- may fail if ls doesn't support --color
|
|
||||||
end
|
|
||||||
|
|
||||||
-- commands where first arg is another known command name
|
|
||||||
do
|
|
||||||
env ls
|
|
||||||
-- env runs ls; both are valid commands, this is identifier + identifier
|
|
||||||
assert(type(_.code) == "number")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- semicolons: Lua uses ; as optional statement separator.
|
|
||||||
-- with the heuristic, ls followed by ; is ambiguous.
|
|
||||||
-- for now, use separate lines instead:
|
|
||||||
do
|
|
||||||
ls /tmp
|
|
||||||
echo done
|
|
||||||
assert(_.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
print "OK"
|
|
||||||
|
|
||||||
--]]
|
|
||||||
@@ -87,4 +87,10 @@ do
|
|||||||
assert(r.stdout == "a\nb\nc\n")
|
assert(r.stdout == "a\nb\nc\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- process killed by signal returns 128 + signal number
|
||||||
|
do
|
||||||
|
local r = `sh -c "kill -9 $$"`
|
||||||
|
assert(r.code == 137, "expected 128+9=137, got: " .. r.code)
|
||||||
|
end
|
||||||
|
|
||||||
print "OK"
|
print "OK"
|
||||||
|
|||||||
@@ -132,4 +132,40 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- brace with suffix
|
||||||
|
do
|
||||||
|
os.execute("mkdir -p /tmp/_lush_brace_test")
|
||||||
|
os.execute("touch /tmp/_lush_brace_test/a.txt /tmp/_lush_brace_test/b.txt")
|
||||||
|
local r = `echo /tmp/_lush_brace_test/{a,b}.txt`
|
||||||
|
local out = r.stdout:gsub("\n$", "")
|
||||||
|
assert(out == "/tmp/_lush_brace_test/a.txt /tmp/_lush_brace_test/b.txt",
|
||||||
|
"brace suffix failed, got: " .. out)
|
||||||
|
os.execute("rm -rf /tmp/_lush_brace_test")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- no commas in braces → no expansion
|
||||||
|
do
|
||||||
|
local r = `echo {abc}`
|
||||||
|
local out = r.stdout:gsub("\n$", "")
|
||||||
|
assert(out == "{abc}", "no-comma braces should not expand, got: " .. out)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- [?] glob metacharacter
|
||||||
|
do
|
||||||
|
os.execute("mkdir -p /tmp/_lush_bracket_test")
|
||||||
|
os.execute("touch /tmp/_lush_bracket_test/fa /tmp/_lush_bracket_test/fb")
|
||||||
|
local r = `echo /tmp/_lush_bracket_test/f[ab]`
|
||||||
|
local out = r.stdout:gsub("\n$", "")
|
||||||
|
assert(out:find("fa"), "bracket glob should match fa, got: " .. out)
|
||||||
|
assert(out:find("fb"), "bracket glob should match fb, got: " .. out)
|
||||||
|
os.execute("rm -rf /tmp/_lush_bracket_test")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- tilde NOT expanded mid-word
|
||||||
|
do
|
||||||
|
local r = `echo foo~bar`
|
||||||
|
local out = r.stdout:gsub("\n$", "")
|
||||||
|
assert(out == "foo~bar", "mid-word tilde should not expand, got: " .. out)
|
||||||
|
end
|
||||||
|
|
||||||
print "OK"
|
print "OK"
|
||||||
|
|||||||
@@ -102,4 +102,14 @@ do
|
|||||||
assert(_.code == 0)
|
assert(_.code == 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- empty interactive command (whitespace only)
|
||||||
|
do
|
||||||
|
_ = nil
|
||||||
|
!
|
||||||
|
assert(type(_) == "table")
|
||||||
|
assert(_.code == 0)
|
||||||
|
assert(_.stdout == "")
|
||||||
|
assert(_.stderr == "")
|
||||||
|
end
|
||||||
|
|
||||||
print "OK"
|
print "OK"
|
||||||
|
|||||||
@@ -189,4 +189,91 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- === lush.commands (user-defined commands) ===
|
||||||
|
|
||||||
|
do
|
||||||
|
assert(type(lush.commands) == "table", "lush.commands missing")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- basic user command via backtick
|
||||||
|
do
|
||||||
|
lush.commands.greetcmd = function(name, ...)
|
||||||
|
print(table.concat({...}, " "))
|
||||||
|
end
|
||||||
|
local r = `greetcmd hello world`
|
||||||
|
assert(r.code == 0, "user command failed: " .. r.code)
|
||||||
|
assert(r.stdout == "hello world\n",
|
||||||
|
"expected 'hello world\\n', got: " .. r.stdout)
|
||||||
|
lush.commands.greetcmd = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- user command returning integer exit code
|
||||||
|
do
|
||||||
|
lush.commands.exitwith = function(name, code)
|
||||||
|
return tonumber(code)
|
||||||
|
end
|
||||||
|
local r = `exitwith 42`
|
||||||
|
assert(r.code == 42, "expected code 42, got: " .. r.code)
|
||||||
|
lush.commands.exitwith = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- user command returning table with code field
|
||||||
|
do
|
||||||
|
lush.commands.tblret = function(name)
|
||||||
|
return { code = 7 }
|
||||||
|
end
|
||||||
|
local r = `tblret`
|
||||||
|
assert(r.code == 7, "expected code 7, got: " .. r.code)
|
||||||
|
lush.commands.tblret = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- user command error → nonzero exit + stderr
|
||||||
|
do
|
||||||
|
lush.commands.failcmd = function(name)
|
||||||
|
error("deliberate error")
|
||||||
|
end
|
||||||
|
local r = `failcmd`
|
||||||
|
assert(r.code ~= 0, "erroring command should have nonzero exit")
|
||||||
|
assert(r.stderr:find("deliberate error"),
|
||||||
|
"expected error in stderr, got: " .. r.stderr)
|
||||||
|
lush.commands.failcmd = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- removed user command falls through to exec (command not found)
|
||||||
|
do
|
||||||
|
lush.commands.tmptest = function() end
|
||||||
|
lush.commands.tmptest = nil
|
||||||
|
local r = `tmptest`
|
||||||
|
assert(r.code == 127, "removed command should not be found")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- user command via lush.command()
|
||||||
|
do
|
||||||
|
lush.commands.apicmd = function(name, ...)
|
||||||
|
print("api " .. table.concat({...}, " "))
|
||||||
|
end
|
||||||
|
local r = lush.command("apicmd x y")
|
||||||
|
assert(r.stdout == "api x y\n",
|
||||||
|
"expected 'api x y\\n', got: " .. r.stdout)
|
||||||
|
lush.commands.apicmd = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- === lush.subcmd() direct call ===
|
||||||
|
|
||||||
|
do
|
||||||
|
local result = lush.subcmd("echo hello")
|
||||||
|
assert(result == "hello", "subcmd should return stdout without trailing newline, got: " .. tostring(result))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- === lush.interactive() direct call ===
|
||||||
|
|
||||||
|
do
|
||||||
|
lush.interactive("true")
|
||||||
|
assert(type(_) == "table")
|
||||||
|
assert(_.code == 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
print "OK"
|
print "OK"
|
||||||
|
|||||||
@@ -54,13 +54,6 @@ do
|
|||||||
assert(r.stderr == "", "middle stderr not captured: '" .. r.stderr .. "'")
|
assert(r.stderr == "", "middle stderr not captured: '" .. r.stderr .. "'")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- single command unchanged (regression check)
|
|
||||||
do
|
|
||||||
local r = `echo hello`
|
|
||||||
assert(r.stdout == "hello\n")
|
|
||||||
assert(r.code == 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- pipe preserves multi-line output
|
-- pipe preserves multi-line output
|
||||||
do
|
do
|
||||||
local r = `printf "line1\nline2\nline3\n" | cat`
|
local r = `printf "line1\nline2\nline3\n" | cat`
|
||||||
@@ -80,4 +73,10 @@ do
|
|||||||
assert(r.stderr == "piperr\n", "last stage stderr: " .. r.stderr)
|
assert(r.stderr == "piperr\n", "last stage stderr: " .. r.stderr)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- backslash-escaped pipe is NOT a separator
|
||||||
|
do
|
||||||
|
local r = `echo a\|b`
|
||||||
|
assert(r.stdout == "a|b\n", "escaped pipe: " .. r.stdout)
|
||||||
|
end
|
||||||
|
|
||||||
print "OK"
|
print "OK"
|
||||||
|
|||||||
Reference in New Issue
Block a user