Merge nucleic/sleek-thistle-egret-fyej into dev

This commit is contained in:
2026-07-18 05:19:31 -07:00
commit b6be87b72d
677 changed files with 102939 additions and 0 deletions
@@ -0,0 +1,16 @@
name: "Compound commands: arithmetic"
cases:
- name: "Basic arithmetic statements"
stdin: |
((0 == 0)) && echo "0 == 0"
((0 != 0)) && echo "0 != 0"
- name: "Arithmetic statements with parens"
stdin: |
(( (0) )) && echo "0"
(( (1) )) && echo "1"
- name: "Arithmetic statements with parens and operators"
stdin: |
(( (0) == 0 )) && echo "0 == 0"
(( (1) != 0 )) && echo "1 != 0"
@@ -0,0 +1,110 @@
name: "Compound commands: arithmetic for"
cases:
- name: "Single-line arithmetic for loop"
stdin: |
for ((i = 0; i < 5; i++)); do echo $i; done
echo "Result: $?"
- name: "Arithmetic for loop without separator"
stdin: |
for ((i = 0; i < 5; i++)) do echo $i; done
echo "Result: $?"
- name: "Arithmetic for loop with empty condition"
stdin: |
for (( ; ; )); do
echo "In loop; status: $?"
break
done
echo "Result: $?"
- name: "Arithmetic for loop with ;;"
known_failure: true
stdin: |
for ((;;)); do
echo "In loop; status: $?"
break
done
echo "Result: $?"
- name: "Break in arithmetic for loop"
stdin: |
for ((i = 0; i < 5; i++)); do
echo $i
break
done
echo "Result: $?"
- name: "Continue in arithmetic for loop"
stdin: |
for ((i = 0; i < 5; i++)); do
continue
echo "Should not print"
done
echo "Result: $?"
- name: "Arithmetic for with break in condition inside outer loop"
stdin: |
for j in 1 2 3; do
echo "Outer iteration $j"
for ((i = 0; break; i++)); do
echo "Inner body"
done
echo "After inner for"
done
echo "After outer loop"
- name: "Arithmetic for with continue in condition inside outer loop"
stdin: |
for j in 1 2 3; do
echo "Outer iteration $j"
for ((i = 0; continue; i++)); do
echo "Inner body"
done
echo "After inner for"
done
echo "After outer loop"
- name: "Arithmetic for with return in body"
stdin: |
f() {
for ((i = 0; i < 5; i++)); do
echo "Iteration $i"
if [ $i -eq 2 ]; then
return 42
fi
done
echo "After for"
}
f
echo "Exit code: $?"
- name: "Arithmetic for with exit in body"
stdin: |
for ((i = 0; i < 5; i++)); do
echo "Iteration $i"
if [ $i -eq 2 ]; then
exit 42
fi
done
echo "After for"
- name: "Arithmetic for with alternate syntax"
stdin: |
for ((i = 0; i < 5; i++)) {
echo "Iteration $i"
}
- name: "Arithmetic for with alternate syntax (one line)"
stdin: |
for ((i = 0; i < 5; i++)) { echo "Iteration $i"; }
- name: "Arithmetic for loop with nameref"
known_failure: true
stdin: |
target=0
declare -n ref=target
for ((ref=1; ref<=3; ref++)); do
echo "ref=$ref target=$target"
done
echo "final target=$target"
@@ -0,0 +1,5 @@
name: "Compound commands: brace"
cases:
- name: "Brace command"
stdin: |
{ echo 'hi'; }
@@ -0,0 +1,185 @@
name: "Compound commands: case"
cases:
- name: "Basic case statement with double semi"
test_files:
- path: "script.sh"
contents: |
case x in
x) echo hi;;
esac
args: ["./script.sh"]
- name: "Basic catch-all pattern against multi-line input"
stdin: |
case "$(echo hi; echo there)" in
"not-it") echo "did not work" ;;
*) echo "caught it" ;;
esac
- name: "One-line case statement with double semi"
stdin: |
case x in x) echo "hi";; esac
- name: "Interesting characters in cases"
stdin: |
case "{" in
{) echo "curly brace" ;;
*) echo "unhandled case" ;;
esac
- name: "Case with case insensitive pattern"
stdin: |
shopt -s nocasematch
case "A" in
a) echo "matched" ;;
*) echo "did not match" ;;
esac
- name: "Interesting patterns in cases"
stdin: |
for word in "-a" "!b" "*c" "(d" "{e" ":f" "'g"; do
case "${word}" in
\!*) echo "starts with exclamation" ;;
-*) echo "starts with hyphen" ;;
\**) echo "starts with asterisk" ;;
\(*) echo "starts with open parenthesis" ;;
\{*) echo "starts with open curly brace" ;;
:*) echo "starts with colon" ;;
\'*) echo "starts with single quote" ;;
*) echo "unhandled case" ;;
esac
done
- name: "Empty case"
stdin: |
myfunc() {
case abc in
*b*) ;;
*) return 33;;
esac
echo "Dropped out"
}
myfunc
- name: "Case with non-dsemi"
stdin: |
case "b" in
a) echo "a";;
b) echo "b"
esac
- name: "Case with fall-through"
stdin: |
case "b" in
a) echo "a";;
b) echo "b";&
c) echo "c";;
d) echo "d";;
esac
- name: "Case with resuming switch"
stdin: |
case "b" in
a) echo "a";;
b) echo "b";;&
c) echo "c";;
*) echo "*";;
d) echo "d";;
esac
- name: "Case status values"
stdin: |
function yield() {
return $1
}
case "a" in
x) yield 10;;
a) yield 11;;
b) yield 12;;
esac
echo "1: $?"
case "a" in
b) yield 10;;
c) yield 11;;
esac
echo "2: $?"
case "a" in
a) yield 10;&
b) yield 11;;
c) yield 12;;
esac
echo "3: $?"
case "a" in
a) yield 10;;&
*) yield 11;;
x) yield 12;;
esac
echo "4: $?"
- name: "Case with reserved words"
stdin: |
case case in
case) echo "saw case";;
esac
- name: "Case with return in function"
stdin: |
f() {
case "a" in
a) echo "matched a"; return 42;;
b) echo "matched b";;
esac
echo "After case"
}
f
echo "Exit code: $?"
- name: "Case with exit"
stdin: |
case "a" in
a) echo "matched a"; exit 42;;
b) echo "matched b";;
esac
echo "After case"
- name: "Case with break in loop"
stdin: |
for i in 1 2 3; do
case "$i" in
1) echo "one";;
2) echo "two"; break;;
3) echo "three";;
esac
echo "After case iteration $i"
done
echo "After loop"
- name: "Case with continue in loop"
stdin: |
for i in 1 2 3; do
case "$i" in
1) echo "one";;
2) echo "two"; continue;;
3) echo "three";;
esac
echo "After case iteration $i"
done
echo "After loop"
- name: "Case with return and fall-through"
stdin: |
f() {
case "a" in
a) echo "matched a"; return 42;&
b) echo "matched b";;
esac
echo "After case"
}
f
echo "Exit code: $?"
@@ -0,0 +1,839 @@
name: "Compound commands: coproc"
# NOTE: Coproc tests must defend against TWO race conditions caused by bash's
# SIGCHLD handler:
#
# 1. The handler clears the COPROC array when the coproc child exits, so
# ${COPROC[0]} can become empty before the parent captures it.
# 2. The handler ALSO closes the underlying pipe FDs, so even after capturing
# the integer FD, reading from it returns EOF immediately.
#
# We defend against both by:
# (a) Having the coproc body block on `read -r _ < _sync` first, so it can't
# exit before the parent dups the FD (no SIGCHLD can fire yet).
# (b) Using `exec 9<&${COPROC[0]}` to dup the pipe to a fresh FD that bash
# does NOT manage as part of the coproc, so it survives SIGCHLD cleanup.
# (c) Then `echo x > _sync` releases the coproc to do its real work.
#
# We use explicit FD 9 (rather than `{fd}<&...` auto-assignment) for portability.
cases:
#
# Basic invocation
#
- name: "Unnamed coproc with brace group"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; echo "hello from coproc"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Unnamed coproc with subshell"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc ( read -r _ < _sync; echo "hello from subshell coproc" )
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Named coproc with brace group"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc MYCOPROC { read -r _ < _sync; echo "named coproc"; }
exec 9<&${MYCOPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Named coproc with subshell"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc MYSUB ( read -r _ < _sync; echo "named subshell coproc" )
exec 9<&${MYSUB[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc with simple command"
skip: true # TODO(coproc): hangs - wait never completes after bidirectional coproc I/O
test_files:
- path: "test.sh"
contents: |
coproc cat
rfd=${COPROC[0]}
wfd=${COPROC[1]}
echo "input line" >&$wfd
exec {wfd}>&-
read -r line <&$rfd
echo "got: $line"
wait
args: ["./test.sh"]
- name: "Coproc with multi-statement brace group"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo "line1"
echo "line2"
echo "line3"
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
#
# COPROC and COPROC_PID variable validation via functional use
#
- name: "COPROC array read FD is functional"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; echo "via read fd"; }
exec 9<&${COPROC[0]}
echo x > _sync
read -r line <&9
echo "got: $line"
wait
args: ["./test.sh"]
- name: "Named coproc FDs are functional"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc MYPROC { read -r _ < _sync; echo "named fd test"; }
exec 9<&${MYPROC[0]}
echo x > _sync
read -r line <&9
echo "got: $line"
wait
args: ["./test.sh"]
#
# Reading from coproc stdout
#
- name: "Read coproc output with cat"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; echo "output from coproc"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Read coproc output with read builtin"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; echo "readline test"; }
exec 9<&${COPROC[0]}
echo x > _sync
read -r line <&9
echo "got: $line"
wait
args: ["./test.sh"]
- name: "Read multiple lines from coproc"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo "first"
echo "second"
echo "third"
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Read multiple lines with read builtin"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo "line1"
echo "line2"
}
exec 9<&${COPROC[0]}
echo x > _sync
{
read -r first
read -r second
} <&9
echo "first=$first"
echo "second=$second"
wait
args: ["./test.sh"]
- name: "Named coproc read"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc READER { read -r _ < _sync; echo "from named"; }
exec 9<&${READER[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
#
# Writing to coproc stdin
#
- name: "Write to coproc stdin and read back"
skip: true # TODO(coproc): hangs - wait never completes after bidirectional coproc I/O
test_files:
- path: "test.sh"
contents: |
coproc cat
rfd=${COPROC[0]}
wfd=${COPROC[1]}
echo "hello coproc" >&$wfd
exec {wfd}>&-
read -r line <&$rfd
echo "got: $line"
wait
args: ["./test.sh"]
- name: "Bidirectional communication"
skip: true # TODO(coproc): hangs - wait never completes after bidirectional coproc I/O
test_files:
- path: "test.sh"
contents: |
coproc {
while read -r line; do
echo "processed: $line"
done
}
rfd=${COPROC[0]}
wfd=${COPROC[1]}
echo "msg1" >&$wfd
echo "msg2" >&$wfd
exec {wfd}>&-
{
read -r out1
read -r out2
} <&$rfd
echo "$out1"
echo "$out2"
wait
args: ["./test.sh"]
- name: "Named coproc bidirectional"
skip: true # TODO(coproc): hangs - wait never completes after bidirectional coproc I/O
test_files:
- path: "test.sh"
contents: |
coproc BIDIR {
while read -r line; do
echo "echo: $line"
done
}
rfd=${BIDIR[0]}
wfd=${BIDIR[1]}
echo "test1" >&$wfd
echo "test2" >&$wfd
exec {wfd}>&-
{
read -r out1
read -r out2
} <&$rfd
echo "$out1"
echo "$out2"
wait
args: ["./test.sh"]
- name: "Close write FD to signal EOF"
skip: true # TODO(coproc): hangs - wait never completes after bidirectional coproc I/O
test_files:
- path: "test.sh"
contents: |
coproc {
count=0
while read -r line; do
count=$((count + 1))
done
echo "read $count lines"
}
rfd=${COPROC[0]}
wfd=${COPROC[1]}
echo "a" >&$wfd
echo "b" >&$wfd
echo "c" >&$wfd
exec {wfd}>&-
read -r result <&$rfd
echo "$result"
wait
args: ["./test.sh"]
- name: "Bidirectional with cat for EOF"
skip: true # TODO(coproc): hangs - stdout pipe not fully closed after coproc exits
test_files:
- path: "test.sh"
contents: |
coproc {
while read -r line; do
echo "processed: $line"
done
}
rfd=${COPROC[0]}
wfd=${COPROC[1]}
echo "msg1" >&$wfd
echo "msg2" >&$wfd
exec {wfd}>&-
cat <&$rfd
wait
echo "DONE"
args: ["./test.sh"]
#
# Coproc with compound command bodies
#
- name: "Coproc running a for loop"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
for i in 1 2 3; do
echo "iteration $i"
done
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc running a while loop"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
n=1
while [ $n -le 3 ]; do
echo "count $n"
n=$((n + 1))
done
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc with conditionals"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
if true; then
echo "true branch"
else
echo "false branch"
fi
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc with case statement"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
val="hello"
case $val in
hello) echo "matched hello";;
*) echo "no match";;
esac
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc body with pipeline"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo -e "banana\napple\ncherry" | sort
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc body with command substitution"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
result=$(echo "hello" | tr a-z A-Z)
echo "$result"
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc with arithmetic operations"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
a=5
b=3
echo $((a + b))
echo $((a * b))
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
#
# Exit status and lifecycle
#
- name: "Coproc statement returns 0"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; echo done; }
status=$?
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
echo "exit status: $status"
args: ["./test.sh"]
- name: "Coproc with failing body returns 0 immediately"
test_files:
- path: "test.sh"
contents: |
coproc { exit 42; }
echo "coproc start status: $?"
wait
args: ["./test.sh"]
- name: "Wait with no args waits for coproc"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; echo "coproc done"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
echo "after wait"
args: ["./test.sh"]
#
# Error cases
#
- name: "Invalid coproc name"
ignore_stderr: true
test_files:
- path: "test.sh"
contents: |
coproc 123bad { echo x; }
echo "exit: $?"
args: ["./test.sh"]
#
# Coproc in different contexts
#
- name: "Coproc inside a function"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
start_coproc() {
coproc { read -r _ < _sync; echo "from function coproc"; }
}
start_coproc
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc inside a conditional"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
if true; then
coproc { read -r _ < _sync; echo "conditional coproc"; }
fi
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc inside a loop body"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
for i in 1; do
coproc { read -r _ < _sync; echo "loop coproc $i"; }
done
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Named coproc inside a function"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
f() {
coproc FUNCPROC { read -r _ < _sync; echo "named in func"; }
}
f
exec 9<&${FUNCPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
#
# Multiple coprocs and variable lifecycle
#
- name: "Second unnamed coproc replaces first"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; echo "first"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
exec 9<&-
wait
coproc { read -r _ < _sync; echo "second"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Sequential named coprocs with different names"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc PROC1 { read -r _ < _sync; echo "proc1"; }
exec 9<&${PROC1[0]}
echo x > _sync
cat <&9
exec 9<&-
wait
coproc PROC2 { read -r _ < _sync; echo "proc2"; }
exec 9<&${PROC2[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
#
# Environment and variable scope
#
- name: "Coproc inherits parent environment"
test_files:
- path: "test.sh"
contents: |
export MY_VAR="inherited"
mkfifo _sync
coproc { read -r _ < _sync; echo "MY_VAR=$MY_VAR"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc inherits non-exported variables"
test_files:
- path: "test.sh"
contents: |
LOCAL_VAR="visible"
mkfifo _sync
coproc { read -r _ < _sync; echo "LOCAL_VAR=$LOCAL_VAR"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc cannot modify parent variables"
test_files:
- path: "test.sh"
contents: |
PARENT_VAR="original"
mkfifo _sync
coproc { read -r _ < _sync; PARENT_VAR="modified"; echo "in coproc: $PARENT_VAR"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
echo "in parent: $PARENT_VAR"
args: ["./test.sh"]
- name: "Coproc inherits functions"
test_files:
- path: "test.sh"
contents: |
greet() { echo "hello $1"; }
export -f greet
mkfifo _sync
coproc { read -r _ < _sync; greet "world"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc inherits aliases"
test_files:
- path: "test.sh"
contents: |
shopt -s expand_aliases
alias myecho='echo ALIASED:'
mkfifo _sync
coproc { read -r _ < _sync; myecho "test"; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
#
# Coproc with redirections in body
#
- name: "Coproc body with stderr redirection"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo "stdout msg"
echo "stderr msg" >&2
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
- name: "Coproc body writing to a file"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo "file content" > /tmp/coproc_test_$$
echo "wrote file"
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
cat /tmp/coproc_test_$$
rm -f /tmp/coproc_test_$$
args: ["./test.sh"]
#
# Coproc with various data patterns
#
- name: "Coproc producing empty output"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc { read -r _ < _sync; true; }
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
echo "after empty coproc"
args: ["./test.sh"]
- name: "Coproc producing multiple lines of output"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
for i in 1 2 3 4 5 6 7 8 9 10; do
echo "line $i"
done
}
exec 9<&${COPROC[0]}
echo x > _sync
count=0
while read -r line; do
count=$((count + 1))
done <&9
echo "read $count lines"
wait
args: ["./test.sh"]
- name: "Coproc with multiline string output"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
cat <<'HEREDOC'
first line
second line
third line
HEREDOC
}
exec 9<&${COPROC[0]}
echo x > _sync
cat <&9
wait
args: ["./test.sh"]
#
# Coproc output transformation
#
- name: "Transform coproc output to uppercase"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo "hello"
echo "world"
}
exec 9<&${COPROC[0]}
echo x > _sync
while read -r line; do
echo "$line" | tr a-z A-Z
done <&9
wait
args: ["./test.sh"]
- name: "Coproc output filtered with case"
test_files:
- path: "test.sh"
contents: |
mkfifo _sync
coproc {
read -r _ < _sync
echo "apple"
echo "banana"
echo "avocado"
echo "cherry"
}
exec 9<&${COPROC[0]}
echo x > _sync
while read -r line; do
case $line in
a*) echo "starts with a: $line";;
esac
done <&9
wait
args: ["./test.sh"]
#
# Tests requiring wait with job specs (known failures)
#
- name: "Wait for specific coproc PID"
known_failure: true # TODO(wait): wait with job specs not implemented
test_files:
- path: "test.sh"
contents: |
coproc { echo "done"; }
fd=${COPROC[0]}
cat <&$fd
wait $COPROC_PID
echo "wait status: $?"
args: ["./test.sh"]
- name: "Coproc with failing body - wait captures exit status"
known_failure: true # TODO(wait): wait with job specs not implemented
test_files:
- path: "test.sh"
contents: |
coproc { exit 42; }
wait $COPROC_PID
echo "exit status: $?"
args: ["./test.sh"]
- name: "Wait for named coproc PID"
known_failure: true # TODO(wait): wait with job specs not implemented
test_files:
- path: "test.sh"
contents: |
coproc MYPROC { echo "done"; }
fd=${MYPROC[0]}
cat <&$fd
wait $MYPROC_PID
echo "status: $?"
args: ["./test.sh"]
@@ -0,0 +1,195 @@
name: "Compound commands: for"
cases:
- name: "Single-line for loop"
stdin: |
for f in 1 2 3; do echo $f; done
- name: "Multi-line for loop"
stdin: |
for f in 1 2 3; do
echo $f
done
- name: "Empty for loop"
stdin: |
for f in; do echo $f; done
- name: "for loop without in"
stdin: |
set -- a b c
echo "Loop 1"
for f; do echo $f; done
echo "Loop 2: no semicolon"
for f do echo $f; done
- name: "for loop without in but spaces"
stdin: |
set -- a "b c" d
echo "Loop 1"
for f; do echo $f; done
echo "Loop 2: no semicolon"
for f do echo $f; done
- name: "Break in for loop"
stdin: |
for f in 1 2 3; do
echo $f
break
done
- name: "Break in nested for loop"
stdin: |
for f in 1 2 3; do
for g in a b c; do
echo "f=$f g=$g"
true && break
done
echo "Left inner loop"
done
- name: "Break 1 in nested for loops"
stdin: |
for f in 1 2 3; do
for g in a b c; do
echo "f=$f g=$g"
break 1
done
done
- name: "Break 2 in nested for loops"
stdin: |
for f in 1 2 3; do
for g in a b c; do
echo "f=$f g=$g"
break 2
done
done
- name: "Break out of nested for/while loops"
stdin: |
for f in 1 2 3; do
while true; do
echo "f=$f"
break 2
done
done
- name: "Continue in for loop"
stdin: |
for f in 1 2 3; do
echo $f
continue
echo $f
done
- name: "Continue with bad N"
known_failure: true # Fails differently
stdin: |
for f in 1 2 3; do
echo $f
continue 0
echo result: $?
done
- name: "Continue in nested for loop"
stdin: |
for f in a b c; do
for g in 1 2 3; do
echo "f=$f g=$g"
continue
echo "Should not print"
done
echo "Left inner loop"
done
- name: "Continue in nested for loop with explicit N=1"
stdin: |
for f in a b c; do
for g in 1 2 3; do
echo "f=$f g=$g"
continue 1
echo "Should not print"
done
echo "Left inner loop"
done
- name: "Continue in nested for loop with explicit N=2"
stdin: |
for f in a b c; do
for g in 1 2 3; do
echo "f=$f g=$g"
continue 2
echo "Should not print"
done
echo "Left inner loop"
done
- name: "Continue in nested for loop with too large N"
known_failure: true
stdin: |
for f in a b c; do
for g in 1 2 3; do
echo "f=$f g=$g"
continue 3
echo "Should not print"
done
echo "Left inner loop"
done
- name: "Multi-line for loop"
test_files:
- path: "script.sh"
contents: |
for f in 1 2 3; do
echo $f
done
args: ["./script.sh"]
- name: "For loop piped"
stdin: |
for f in ab ac bd ef; do echo $f; done | grep b
- name: "For loop output redirection"
stdin: |
for f in a b c; do echo $f; done > ./out.txt
- name: "For reserved word handling"
stdin: |
for for in for; do echo $for; done
- name: "For with return in body"
stdin: |
f() {
for i in 1 2 3; do
echo "Iteration $i"
if [ "$i" = "2" ]; then
return 42
fi
done
echo "After for"
}
f
echo "Exit code: $?"
- name: "For with exit in body"
stdin: |
for i in 1 2 3; do
echo "Iteration $i"
if [ "$i" = "2" ]; then
exit 42
fi
done
echo "After for"
- name: "For loop with nameref variable"
stdin: |
target=""
declare -n ref=target
for ref in a b c; do
echo "loop: target=$target"
done
echo "final: target=$target"
@@ -0,0 +1,99 @@
name: "Compound commands: if"
cases:
- name: "Basic if"
stdin: |
if false; then echo 1; else echo 2; fi
if false; then echo 3; elif false; then echo 4; else echo 5; fi
if true; then echo 6; else echo 7; fi
if false; then echo 8; elif true; then echo 10; else echo 11; fi
- name: "Multi-line if"
test_files:
- path: "script.sh"
contents: |
if false; then
echo 1
else
echo 2
fi
if false; then
echo 3
elif false; then
echo 4
else
echo 5
fi
if true; then
echo 6
else
echo 7
fi
if false; then
echo 8
elif true; then
echo 10
else
echo 11
fi
args: ["./script.sh"]
- name: "If with return in condition"
stdin: |
f() {
if return 42; then
echo "Then branch"
else
echo "Else branch"
fi
echo "After if"
}
f
echo "Exit code: $?"
- name: "If with exit in condition"
stdin: |
if exit 42; then
echo "Then branch"
else
echo "Else branch"
fi
echo "After if"
- name: "If with return in elif condition"
stdin: |
f() {
if false; then
echo "Then branch"
elif return 42; then
echo "Elif then branch"
else
echo "Else branch"
fi
echo "After if"
}
f
echo "Exit code: $?"
- name: "If with break in condition inside loop"
stdin: |
for i in 1 2 3; do
if break; then
echo "Then branch"
fi
echo "After if"
done
echo "After loop"
- name: "If with continue in condition inside loop"
stdin: |
for i in 1 2 3; do
echo "Iteration $i"
if continue; then
echo "Then branch"
fi
echo "After if"
done
echo "After loop"
@@ -0,0 +1,10 @@
name: "Select statement"
cases:
- name: "basic"
known_failure: true # TODO(select): select statement not implemented
stdin: |
echo "1" | select opt in a b c; do
echo "selected: $opt"
break
done
echo "after select"
@@ -0,0 +1,38 @@
name: "Compound commands: subshell"
cases:
- name: "Basic subshell usage"
stdin: |
(echo hi)
- name: "Subshells in sequence"
ignore_stderr: true
stdin: |
(echo hi)(echo there)
- name: "Subshell env usage"
stdin: |
(subshell_var=value && echo "subshell_var: ${subshell_var}")
echo "subshell_var: ${subshell_var}"
- name: "Subshell output redirection"
stdin: |
(echo Hello; echo world) >out.txt
echo "Dumping out.txt..."
cat out.txt
- name: "Piped subshell usage"
stdin: |
(echo hi) | wc -l
- name: "Breaks in subshell"
stdin: |
for i in 1 2 3; do
echo $i
(for i in 1 2 3; do break 2; done)
done
- name: "Exec in subshell"
stdin: |
echo "Before subshell"
(exec cat <<<"Hello from exec in subshell")
echo "After subshell"
@@ -0,0 +1,101 @@
name: "Compound commands: until"
cases:
- name: "Single-line until loop"
stdin: |
until true; do echo 'In loop'; done
- name: "Arithmetic in until loop"
incompatible_configs: ["sh"]
stdin: |
i=5
until ((i == 0)); do echo $i; i=$((i - 1)); done
- name: "Until with return in condition"
stdin: |
f() {
until return 42; do
echo "Body"
done
echo "After until"
}
f
echo "Exit code: $?"
- name: "Until with exit in condition"
stdin: |
until exit 42; do
echo "Body"
done
echo "After until"
- name: "Until with break in condition inside outer loop"
stdin: |
for i in 1 2 3; do
echo "Outer iteration $i"
until break; do
echo "Until body"
done
echo "After until"
done
echo "After outer loop"
- name: "Until with break 2 in condition inside outer loop"
stdin: |
for i in 1 2 3; do
echo "Outer iteration $i"
until break 2; do
echo "Until body"
done
echo "After until"
done
echo "After outer loop"
- name: "Until with continue 2 in condition inside outer loop"
stdin: |
for i in 1 2 3; do
echo "Outer iteration $i"
until continue 2; do
echo "Until body"
done
echo "After until"
done
echo "After outer loop"
- name: "Until with continue in condition inside outer loop"
stdin: |
for i in 1 2 3; do
echo "Outer iteration $i"
until continue; do
echo "Until body"
done
echo "After until"
done
echo "After outer loop"
- name: "Until with return in body"
stdin: |
f() {
i=0
until [ $i -ge 3 ]; do
echo "Iteration $i"
if [ $i -eq 1 ]; then
return 42
fi
i=$((i + 1))
done
echo "After until"
}
f
echo "Exit code: $?"
- name: "Until with exit in body"
stdin: |
i=0
until [ $i -ge 3 ]; do
echo "Iteration $i"
if [ $i -eq 1 ]; then
exit 42
fi
i=$((i + 1))
done
echo "After until"
@@ -0,0 +1,127 @@
name: "Compound commands: while"
cases:
- name: "Single-line while loop"
stdin: |
while false; do echo 'In loop'; done
- name: "break in while loop"
stdin: |
while true; do
echo 'In loop'
break
done
- name: "break 2 in nested loops"
stdin: |
while false; do
echo 'Starting inner loop'
while true; do
echo 'In loop'
break 2
done
echo 'Finished inner loop'
done
- name: "Arithmetic in while loop"
stdin: |
i=5
while ((i > 0)); do echo $i; i=$((i - 1)); done
- name: "Alternative arithmetic in while loop"
stdin: |
c=0
limit=4
while [ $c -lt $limit ]; do
case "$c" in
0)
echo "0"
;;
1)
echo "1"
;;
*)
break
;;
esac
((c++))
done
echo "Done"
- name: "While with return in condition"
stdin: |
f() {
while return 42; do
echo "Body"
done
echo "After while"
}
f
echo "Exit code: $?"
- name: "While with exit in condition"
stdin: |
while exit 42; do
echo "Body"
done
echo "After while"
- name: "While with break in condition inside outer loop"
stdin: |
for i in 1 2 3; do
echo "Outer iteration $i"
while break; do
echo "While body"
done
echo "After while"
done
echo "After outer loop"
- name: "While with break 2 in condition inside outer loop"
stdin: |
for i in 1 2 3; do
echo "Outer iteration $i"
while break 2; do
echo "While body"
done
echo "After while"
done
echo "After outer loop"
- name: "While with continue 2 in condition inside outer loop"
stdin: |
for i in 1 2 3; do
echo "Outer iteration $i"
while continue 2; do
echo "While body"
done
echo "After while"
done
echo "After outer loop"
- name: "While with return in body"
stdin: |
f() {
i=0
while [ $i -lt 3 ]; do
echo "Iteration $i"
if [ $i -eq 1 ]; then
return 42
fi
i=$((i + 1))
done
echo "After while"
}
f
echo "Exit code: $?"
- name: "While with exit in body"
stdin: |
i=0
while [ $i -lt 3 ]; do
echo "Iteration $i"
if [ $i -eq 1 ]; then
exit 42
fi
i=$((i + 1))
done
echo "After while"