From 27f16b126de1b3a34193475d0cee4fc1ef6bad7d Mon Sep 17 00:00:00 2001 From: Cormac Shannon <> Date: Sat, 28 Feb 2026 19:07:00 +0000 Subject: [PATCH] Add lush feature tests and fix /dev/full test for macOS Add test suite under testes/lush/ covering backtick commands, argv parsing, ${} interpolation, and $NAME environment variables. Wire them into testes/all.lua so they run with the full Lua 5.5 suite. Skip /dev/full test in files.lua when the device doesn't exist (macOS has no /dev/full). --- testes/all.lua | 6 +++ testes/files.lua | 14 +++--- testes/lush/argv.lua | 53 +++++++++++++++++++++ testes/lush/commands.lua | 90 +++++++++++++++++++++++++++++++++++ testes/lush/envvars.lua | 69 +++++++++++++++++++++++++++ testes/lush/interpolation.lua | 55 +++++++++++++++++++++ 6 files changed, 281 insertions(+), 6 deletions(-) create mode 100644 testes/lush/argv.lua create mode 100644 testes/lush/commands.lua create mode 100644 testes/lush/envvars.lua create mode 100644 testes/lush/interpolation.lua diff --git a/testes/all.lua b/testes/all.lua index d3e2f123..33f80421 100755 --- a/testes/all.lua +++ b/testes/all.lua @@ -199,6 +199,12 @@ dofile('bitwise.lua') assert(dofile('verybig.lua', true) == 10); collectgarbage() dofile('files.lua') +-- lush shell feature tests +dofile('lush/commands.lua') +dofile('lush/argv.lua') +dofile('lush/interpolation.lua') +dofile('lush/envvars.lua') + if #msgs > 0 then local m = table.concat(msgs, "\n ") warn("#tests not performed:\n ", m, "\n") diff --git a/testes/files.lua b/testes/files.lua index 7146ac7c..3df0776e 100644 --- a/testes/files.lua +++ b/testes/files.lua @@ -471,12 +471,14 @@ do print("testing flush") assert(io.flush()) -- write to device assert(f:close()) - local f = io.output("/dev/full") - assert(f:write("abcd")) -- write to buffer - assert(not f:flush()) -- cannot write to device - assert(f:write("abcd")) -- write to buffer - assert(not io.flush()) -- cannot write to device - assert(f:close()) + local ok, f = pcall(io.output, "/dev/full") + if ok then -- /dev/full exists (Linux); skip on macOS + assert(f:write("abcd")) -- write to buffer + assert(not f:flush()) -- cannot write to device + assert(f:write("abcd")) -- write to buffer + assert(not io.flush()) -- cannot write to device + assert(f:close()) + end end diff --git a/testes/lush/argv.lua b/testes/lush/argv.lua new file mode 100644 index 00000000..5ea4b565 --- /dev/null +++ b/testes/lush/argv.lua @@ -0,0 +1,53 @@ +-- testes/lush/argv.lua +-- Tests for argv parsing (issue #04). + +print "testing argv parsing" + +-- simple whitespace splitting +do + local r = `echo a b c` + assert(r.stdout == "a b c\n") +end + +-- single-quoted strings (literal, no escapes) +do + local r = `echo 'hello world'` + assert(r.stdout == "hello world\n") +end + +-- double-quoted strings +do + local r = `echo "hello world"` + assert(r.stdout == "hello world\n") +end + +-- multiple quoted arguments +do + local r = `echo "a b" "c d"` + assert(r.stdout == "a b c d\n") +end + +-- backslash escaping outside quotes +do + local r = `echo hello\ world` + assert(r.stdout == "hello world\n") +end + +-- double-quote escapes: \" and \\ +do + local r = `echo "hello\"world"` + assert(r.stdout == 'hello"world\n') +end + +do + local r = `echo "hello\\world"` + assert(r.stdout == "hello\\world\n") +end + +-- tab escape in double quotes +do + local r = `echo "a\tb"` + assert(r.stdout == "a\tb\n") +end + +print "OK" diff --git a/testes/lush/commands.lua b/testes/lush/commands.lua new file mode 100644 index 00000000..8da0d51c --- /dev/null +++ b/testes/lush/commands.lua @@ -0,0 +1,90 @@ +-- testes/lush/commands.lua +-- Tests for backtick command execution (issues #02, #03). + +print "testing backtick commands" + +-- basic command, result is a table with code/stdout/stderr +do + local r = `echo hello` + assert(type(r) == "table") + assert(type(r.code) == "number") + assert(type(r.stdout) == "string") + assert(type(r.stderr) == "string") +end + +-- successful command returns exit code 0 +do + local r = `true` + assert(r.code == 0) +end + +-- failed command returns non-zero exit code +do + local r = `false` + assert(r.code == 1) +end + +-- specific exit codes are preserved +do + local r = `sh -c "exit 42"` + assert(r.code == 42) +end + +-- command not found returns 127 +do + local r = `nonexistent_command_xyz_999` + assert(r.code == 127) +end + +-- stdout capture +do + local r = `echo hello` + assert(r.stdout == "hello\n") +end + +-- stderr capture +do + local r = `sh -c "echo err >&2"` + assert(r.stderr == "err\n") + assert(r.stdout == "") +end + +-- stdout and stderr are independent +do + local r = `sh -c "echo out; echo err >&2"` + assert(r.stdout == "out\n") + assert(r.stderr == "err\n") +end + +-- empty command returns code 0 and empty strings +do + local r = `` + assert(r.code == 0) + assert(r.stdout == "") + assert(r.stderr == "") +end + +-- multi-word output preserved +do + local r = `echo hello world` + assert(r.stdout == "hello world\n") +end + +-- backtick as statement (result discarded, no error) +do + `true` +end + +-- backtick result used inline +do + local code = `true`.code + assert(code == 0) +end + +-- multiline output captured +do + local r = `printf "a\nb\nc\n"` + assert(r.stdout == "a\nb\nc\n") +end + +print "OK" diff --git a/testes/lush/envvars.lua b/testes/lush/envvars.lua new file mode 100644 index 00000000..c053129f --- /dev/null +++ b/testes/lush/envvars.lua @@ -0,0 +1,69 @@ +-- testes/lush/envvars.lua +-- Tests for environment variable access with $NAME syntax (issue #05). + +print "testing environment variables" + +-- $NAME reads an existing environment variable +do + local p = $PATH + assert(type(p) == "string") + assert(#p > 0) +end + +-- $NAME returns nil for unset variable +do + local v = $_LUSH_TEST_UNSET_VAR + assert(v == nil) +end + +-- $NAME = expr sets an environment variable +do + $_LUSH_TEST_A = "hello" + assert($_LUSH_TEST_A == "hello") +end + +-- $NAME = expr with number coerces to string +do + $_LUSH_TEST_B = 42 + assert($_LUSH_TEST_B == "42") +end + +-- $NAME = nil unsets the variable +do + $_LUSH_TEST_C = "temp" + assert($_LUSH_TEST_C == "temp") + $_LUSH_TEST_C = nil + assert($_LUSH_TEST_C == nil) +end + +-- env vars are visible to child processes +do + $_LUSH_TEST_D = "from_lush" + local r = `sh -c "echo $_LUSH_TEST_D"` + -- the $_LUSH_TEST_D here is NOT lush interpolation (no {}), + -- it's a literal string passed to sh which expands it + assert(r.stdout == "from_lush\n", r.stdout) +end + +-- overwriting an env var +do + $_LUSH_TEST_E = "first" + $_LUSH_TEST_E = "second" + assert($_LUSH_TEST_E == "second") +end + +-- env var used in interpolation +do + $_LUSH_TEST_F = "works" + local r = `echo ${$_LUSH_TEST_F}` + assert(r.stdout == "works\n") +end + +-- clean up +$_LUSH_TEST_A = nil +$_LUSH_TEST_B = nil +$_LUSH_TEST_D = nil +$_LUSH_TEST_E = nil +$_LUSH_TEST_F = nil + +print "OK" diff --git a/testes/lush/interpolation.lua b/testes/lush/interpolation.lua new file mode 100644 index 00000000..c045c727 --- /dev/null +++ b/testes/lush/interpolation.lua @@ -0,0 +1,55 @@ +-- testes/lush/interpolation.lua +-- Tests for ${expr} interpolation in backtick commands (issue #02). + +print "testing string interpolation" + +-- single interpolation +do + local name = "world" + local r = `echo ${name}` + assert(r.stdout == "world\n") +end + +-- multiple interpolations +do + local a = "hello" + local b = "world" + local r = `echo ${a} ${b}` + assert(r.stdout == "hello world\n") +end + +-- interpolation with number +do + local n = 42 + local r = `echo ${n}` + assert(r.stdout == "42\n") +end + +-- interpolation with expression +do + local x = 3 + local r = `echo ${x + 1}` + assert(r.stdout == "4\n") +end + +-- interpolation with string concatenation +do + local prefix = "hello" + local r = `echo ${prefix .. " world"}` + assert(r.stdout == "hello world\n") +end + +-- interpolation surrounded by literal text +do + local mid = "b" + local r = `echo a${mid}c` + assert(r.stdout == "abc\n") +end + +-- literal $ (not followed by {) is kept +do + local r = `echo $` + assert(r.stdout == "$\n") +end + +print "OK"