Merge nucleic/sleek-thistle-egret-fyej into dev
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
name: "BASH_XTRACEFD tests"
|
||||
cases:
|
||||
- name: "basic redirection to fd 3"
|
||||
stdin: |
|
||||
exec 3>trace.txt
|
||||
BASH_XTRACEFD=3
|
||||
|
||||
set -x
|
||||
echo "hello"
|
||||
echo "world"
|
||||
|
||||
set +x
|
||||
cat trace.txt
|
||||
|
||||
- name: "redirection takes effect immediately"
|
||||
stdin: |
|
||||
exec 3>trace.txt
|
||||
|
||||
set -x
|
||||
echo "before setting BASH_XTRACEFD"
|
||||
|
||||
BASH_XTRACEFD=3
|
||||
echo "after setting BASH_XTRACEFD"
|
||||
|
||||
set +x
|
||||
|
||||
echo "Content of trace file:"
|
||||
cat trace.txt
|
||||
|
||||
- name: "unsetting BASH_XTRACEFD returns to stderr"
|
||||
stdin: |
|
||||
exec 3>trace.txt
|
||||
BASH_XTRACEFD=3
|
||||
|
||||
set -x
|
||||
echo "redirected"
|
||||
|
||||
unset BASH_XTRACEFD
|
||||
echo "back to stderr"
|
||||
|
||||
set +x
|
||||
|
||||
echo "Content of trace file:"
|
||||
cat trace.txt
|
||||
|
||||
- name: "invalid fd value falls back to stderr"
|
||||
known_failure: true # bash validates BASH_XTRACEFD at assignment time, brush doesn't have that mechanism
|
||||
stdin: |
|
||||
BASH_XTRACEFD=999
|
||||
|
||||
set -x
|
||||
echo "should go to stderr"
|
||||
|
||||
set +x
|
||||
|
||||
- name: "non-numeric value falls back to stderr"
|
||||
known_failure: true # bash validates BASH_XTRACEFD at assignment time, brush doesn't have that mechanism
|
||||
stdin: |
|
||||
BASH_XTRACEFD=invalid
|
||||
|
||||
set -x
|
||||
echo "should go to stderr"
|
||||
|
||||
set +x
|
||||
|
||||
- name: "empty value falls back to stderr"
|
||||
stdin: |
|
||||
BASH_XTRACEFD=
|
||||
|
||||
set -x
|
||||
echo "should go to stderr"
|
||||
|
||||
- name: "redirect to file that fails writes"
|
||||
stdin: |
|
||||
# This test relies on /dev/full being present. We degrade gracefully
|
||||
# if it doesn't.
|
||||
[[ -e /dev/full ]] || exit
|
||||
|
||||
exec 3>/dev/full
|
||||
BASH_XTRACEFD=3
|
||||
|
||||
set -x
|
||||
echo "first echo"
|
||||
echo "first echo result: $?"
|
||||
@@ -0,0 +1,8 @@
|
||||
name: "Default options"
|
||||
cases:
|
||||
- name: "Default options"
|
||||
stdin: |
|
||||
echo "Default options: $-"
|
||||
|
||||
- name: "Default options (-c)"
|
||||
args: ["-c", 'echo "Default options: $-"']
|
||||
@@ -0,0 +1,213 @@
|
||||
name: "Options: errtrace (-E)"
|
||||
cases:
|
||||
#
|
||||
# Basic errtrace option behavior
|
||||
#
|
||||
- name: "errtrace disabled by default"
|
||||
stdin: |
|
||||
set +E 2>/dev/null || true
|
||||
echo "errtrace test"
|
||||
|
||||
- name: "errtrace can be enabled and disabled"
|
||||
stdin: |
|
||||
set -E
|
||||
echo "enabled: $-" | grep -q E && echo "E is set"
|
||||
set +E
|
||||
echo "disabled: $-" | grep -qv E && echo "E is unset"
|
||||
|
||||
#
|
||||
# ERR trap inheritance in functions (core errtrace behavior)
|
||||
#
|
||||
- name: "errtrace disabled - functions don't inherit ERR trap"
|
||||
stdin: |
|
||||
set +E
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
myfunc() { false; }
|
||||
myfunc
|
||||
echo "after"
|
||||
|
||||
- name: "errtrace enabled - functions inherit ERR trap"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
myfunc() { false; }
|
||||
myfunc
|
||||
echo "after"
|
||||
|
||||
- name: "errtrace with nested functions"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
inner() { false; }
|
||||
outer() { inner; }
|
||||
outer
|
||||
echo "after"
|
||||
|
||||
- name: "errtrace toggling mid-script"
|
||||
stdin: |
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
set -E
|
||||
myfunc1() { false; }
|
||||
myfunc1
|
||||
echo "after first call"
|
||||
set +E
|
||||
myfunc2() { false; }
|
||||
myfunc2
|
||||
echo "after second call"
|
||||
|
||||
#
|
||||
# ERR trap inheritance in subshells
|
||||
#
|
||||
- name: "errtrace disabled - subshell doesn't inherit ERR trap"
|
||||
stdin: |
|
||||
set +E
|
||||
trap 'echo "ERR"' ERR
|
||||
(
|
||||
trap -p ERR
|
||||
false
|
||||
echo "no ERR trap fired"
|
||||
)
|
||||
echo "after"
|
||||
|
||||
- name: "errtrace enabled - subshell inherits ERR trap"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "ERR"' ERR
|
||||
(
|
||||
trap -p ERR | grep -q ERR && echo "ERR trap inherited"
|
||||
false
|
||||
echo "after false in subshell"
|
||||
)
|
||||
echo "after"
|
||||
|
||||
#
|
||||
# ERR trap inheritance in command substitution
|
||||
#
|
||||
- name: "errtrace disabled - command substitution doesn't inherit ERR trap"
|
||||
stdin: |
|
||||
set +E
|
||||
trap 'echo "[ERR trapped]" >&2' ERR
|
||||
result=$(trap -p ERR; false; echo "no trap inherited")
|
||||
echo "result: $result"
|
||||
|
||||
- name: "errtrace enabled - command substitution inherits ERR trap"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "[ERR in subshell]" >&2' ERR
|
||||
result=$(false; echo "after false in subshell")
|
||||
echo "result: $result"
|
||||
|
||||
#
|
||||
# ERR trap inheritance in pipelines
|
||||
#
|
||||
- name: "errtrace with pipeline - last command fails"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "[ERR: $?]" >&2' ERR
|
||||
echo "data" | cat | false
|
||||
echo "after pipeline"
|
||||
|
||||
- name: "errtrace with pipeline - middle command fails (pipefail)"
|
||||
stdin: |
|
||||
set -E
|
||||
set -o pipefail
|
||||
trap 'echo "[ERR: $?]" >&2' ERR
|
||||
echo "data" | false | cat
|
||||
echo "after pipeline"
|
||||
|
||||
- name: "errtrace - ERR trap in pipeline subshells"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "[ERR: $BASH_COMMAND]" >&2' ERR
|
||||
echo "input" | { false; echo "after false in brace"; }
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# ERR trap with errexit interaction
|
||||
#
|
||||
- name: "errtrace with errexit - ERR trap fires before exit"
|
||||
stdin: |
|
||||
set -e
|
||||
set -E
|
||||
trap 'echo "ERR trap fired"' ERR
|
||||
false
|
||||
echo "should not print"
|
||||
|
||||
- name: "errtrace with errexit in function"
|
||||
known_failure: true # TODO(traps): ERR trap not firing in function with errexit
|
||||
stdin: |
|
||||
set -e
|
||||
set -E
|
||||
trap 'echo "ERR trap"' ERR
|
||||
myfunc() { false; }
|
||||
myfunc
|
||||
echo "should not print"
|
||||
|
||||
#
|
||||
# ERR trap inheritance in deeply nested functions
|
||||
#
|
||||
- name: "errtrace - ERR trap in deeply nested function"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "ERR at level ${#FUNCNAME[@]}"' ERR
|
||||
level3() { false; }
|
||||
level2() { level3; }
|
||||
level1() { level2; }
|
||||
level1
|
||||
echo "after"
|
||||
|
||||
#
|
||||
# ERR trap modification within functions
|
||||
#
|
||||
- name: "errtrace - ERR trap can be changed inside function"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "outer ERR"' ERR
|
||||
myfunc() {
|
||||
trap 'echo "inner ERR"' ERR
|
||||
false
|
||||
}
|
||||
myfunc
|
||||
false
|
||||
echo "after"
|
||||
|
||||
- name: "errtrace - ERR trap can be cleared inside function"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "ERR"' ERR
|
||||
myfunc() {
|
||||
trap - ERR
|
||||
false
|
||||
echo "no ERR in func"
|
||||
}
|
||||
myfunc
|
||||
false
|
||||
echo "after"
|
||||
|
||||
#
|
||||
# ERR trap in background jobs with errtrace
|
||||
#
|
||||
- name: "errtrace - ERR trap in background job"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "ERR" >&2' ERR
|
||||
{ false; echo "bg done"; } &
|
||||
wait
|
||||
echo "main done"
|
||||
|
||||
#
|
||||
# Sourced scripts with errtrace
|
||||
#
|
||||
- name: "errtrace - ERR trap inherited in sourced script"
|
||||
test_files:
|
||||
- path: "failing.sh"
|
||||
contents: |
|
||||
echo "in sourced"
|
||||
false
|
||||
echo "after false in sourced"
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
. ./failing.sh
|
||||
echo "after source"
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
name: "Options: extdebug"
|
||||
cases:
|
||||
#
|
||||
# extdebug implies functrace (-T)
|
||||
#
|
||||
- name: "extdebug implies functrace - DEBUG inherited in functions"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
myfunc() {
|
||||
echo "in func"
|
||||
}
|
||||
echo "before"
|
||||
myfunc
|
||||
echo "after"
|
||||
|
||||
- name: "extdebug implies functrace - DEBUG inherited in subshells"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
echo "before"
|
||||
(echo "in subshell")
|
||||
echo "after"
|
||||
|
||||
#
|
||||
# DEBUG trap return value effects (extdebug-specific)
|
||||
#
|
||||
- name: "DEBUG trap - returns 0 command executes normally"
|
||||
known_failure: true # TODO(extdebug): return in trap handler not supported
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap 'return 0' DEBUG
|
||||
echo "line 1"
|
||||
echo "line 2"
|
||||
echo "line 3"
|
||||
|
||||
- name: "DEBUG trap - returns 1 command is skipped"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
skip_next=false
|
||||
trap '
|
||||
if $skip_next; then
|
||||
skip_next=false
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
' DEBUG
|
||||
echo "line 1"
|
||||
skip_next=true
|
||||
echo "this should be skipped"
|
||||
echo "line 3"
|
||||
|
||||
- name: "DEBUG trap - returns 2 simulates return from function"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap '
|
||||
if [[ "${FUNCNAME[0]}" == "myfunc" && "$BASH_COMMAND" == *"should not"* ]]; then
|
||||
return 2
|
||||
fi
|
||||
return 0
|
||||
' DEBUG
|
||||
myfunc() {
|
||||
echo "start of func"
|
||||
echo "this should not print"
|
||||
echo "also should not print"
|
||||
}
|
||||
myfunc
|
||||
echo "after func: $?"
|
||||
|
||||
- name: "DEBUG trap - return value ignored without extdebug"
|
||||
known_failure: true # TODO(extdebug): return in trap handler error message differs
|
||||
stdin: |
|
||||
shopt -u extdebug
|
||||
trap 'return 1' DEBUG
|
||||
echo "line 1"
|
||||
echo "line 2"
|
||||
echo "line 3"
|
||||
|
||||
#
|
||||
# Command skipping scenarios (extdebug-specific)
|
||||
#
|
||||
- name: "Skip simple command"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap '[[ "$BASH_COMMAND" == *"skip"* ]] && return 1; return 0' DEBUG
|
||||
echo "keep this"
|
||||
echo "skip this"
|
||||
echo "keep this too"
|
||||
|
||||
- name: "Skip in loop - iteration skipped but loop continues"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap '[[ "$BASH_COMMAND" == *"skip"* ]] && return 1; return 0' DEBUG
|
||||
for i in 1 2 3; do
|
||||
echo "iter $i"
|
||||
echo "skip $i"
|
||||
echo "after skip $i"
|
||||
done
|
||||
echo "done"
|
||||
|
||||
- name: "Skip in conditional"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap '[[ "$BASH_COMMAND" == *"skip"* ]] && return 1; return 0' DEBUG
|
||||
if true; then
|
||||
echo "in then"
|
||||
echo "skip in then"
|
||||
echo "after skip in then"
|
||||
fi
|
||||
echo "after if"
|
||||
|
||||
- name: "Return 2 in nested function - unwinds to caller"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap '
|
||||
if [[ "${FUNCNAME[0]}" == "inner" && "$BASH_COMMAND" == *"inner cmd"* ]]; then
|
||||
return 2
|
||||
fi
|
||||
return 0
|
||||
' DEBUG
|
||||
inner() {
|
||||
echo "inner start"
|
||||
echo "inner cmd"
|
||||
echo "inner end"
|
||||
}
|
||||
outer() {
|
||||
echo "outer start"
|
||||
inner
|
||||
echo "outer after inner"
|
||||
}
|
||||
outer
|
||||
echo "main after outer"
|
||||
|
||||
#
|
||||
# BASH_ARGC and BASH_ARGV (extdebug-specific)
|
||||
#
|
||||
- name: "BASH_ARGC - contains argument counts per call frame"
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
show_argc() {
|
||||
echo "BASH_ARGC: ${BASH_ARGC[*]}"
|
||||
}
|
||||
func_with_args() {
|
||||
show_argc
|
||||
}
|
||||
func_with_args a b c
|
||||
|
||||
- name: "BASH_ARGV - contains arguments in reverse order"
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
show_argv() {
|
||||
echo "BASH_ARGV: ${BASH_ARGV[*]}"
|
||||
}
|
||||
func_with_args() {
|
||||
show_argv
|
||||
}
|
||||
func_with_args a b c
|
||||
|
||||
- name: "BASH_ARGC/ARGV - in nested calls"
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
inner() {
|
||||
echo "inner ARGC: ${BASH_ARGC[*]}"
|
||||
echo "inner ARGV: ${BASH_ARGV[*]}"
|
||||
}
|
||||
outer() {
|
||||
inner x y
|
||||
}
|
||||
outer a b c
|
||||
|
||||
- name: "BASH_ARGC/ARGV - empty when no arguments"
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
no_args() {
|
||||
echo "BASH_ARGC: '${BASH_ARGC[*]}'"
|
||||
echo "BASH_ARGV: '${BASH_ARGV[*]}'"
|
||||
}
|
||||
no_args
|
||||
|
||||
#
|
||||
# declare -F shows source info (extdebug-specific)
|
||||
#
|
||||
- name: "declare -F - shows function name only without extdebug"
|
||||
stdin: |
|
||||
myfunc() { echo hi; }
|
||||
declare -F myfunc
|
||||
|
||||
- name: "declare -F - shows filename and line number with extdebug"
|
||||
known_failure: true # TODO(extdebug): declare -F not showing source info
|
||||
test_files:
|
||||
- path: "funcs.sh"
|
||||
contents: |
|
||||
myfunc() {
|
||||
echo hi
|
||||
}
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
. ./funcs.sh
|
||||
declare -F myfunc | sed 's/[0-9]*/N/'
|
||||
|
||||
#
|
||||
# Interaction with other options
|
||||
#
|
||||
- name: "extdebug with errexit - skipped command doesn't trigger errexit"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
set -e
|
||||
trap '[[ "$BASH_COMMAND" == "false" ]] && return 1; return 0' DEBUG
|
||||
echo "before"
|
||||
false
|
||||
echo "after - should print because false was skipped"
|
||||
|
||||
- name: "extdebug disabled after being enabled"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap 'return 1' DEBUG
|
||||
echo "line 1 - should be skipped"
|
||||
shopt -u extdebug
|
||||
echo "line 2 - should print"
|
||||
echo "line 3 - should print"
|
||||
|
||||
- name: "extdebug with set -T - both affect DEBUG inheritance"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
set -T
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
myfunc() {
|
||||
echo "in func"
|
||||
}
|
||||
myfunc
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# DEBUG trap with extdebug in various contexts
|
||||
#
|
||||
- name: "DEBUG trap with extdebug in function"
|
||||
known_failure: true # TODO(extdebug): return in trap handler not supported
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
count=0
|
||||
trap '
|
||||
((count++))
|
||||
echo "debug $count: $BASH_COMMAND"
|
||||
return 0
|
||||
' DEBUG
|
||||
myfunc() {
|
||||
local x=1
|
||||
echo "x=$x"
|
||||
}
|
||||
myfunc
|
||||
|
||||
- name: "DEBUG trap with extdebug controls command execution"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
should_skip=false
|
||||
trap '
|
||||
if $should_skip; then
|
||||
should_skip=false
|
||||
echo "[skipping: $BASH_COMMAND]"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
' DEBUG
|
||||
echo "first"
|
||||
should_skip=true
|
||||
echo "second - will be skipped"
|
||||
echo "third"
|
||||
|
||||
#
|
||||
# Edge cases
|
||||
#
|
||||
- name: "extdebug with recursive function"
|
||||
known_failure: true # TODO(extdebug): DEBUG trap firing count differs
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap 'echo "depth: ${#FUNCNAME[@]}"' DEBUG
|
||||
recurse() {
|
||||
local n=$1
|
||||
if ((n > 0)); then
|
||||
recurse $((n - 1))
|
||||
fi
|
||||
}
|
||||
recurse 3
|
||||
|
||||
- name: "BASH_COMMAND shows compound command"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
trap 'echo "cmd: $BASH_COMMAND"' DEBUG
|
||||
if true; then echo "in if"; fi
|
||||
|
||||
- name: "DEBUG trap can modify variables"
|
||||
known_failure: true # TODO(extdebug): return in trap handler not supported
|
||||
stdin: |
|
||||
shopt -s extdebug
|
||||
counter=0
|
||||
trap '((counter++)); return 0' DEBUG
|
||||
echo "a"
|
||||
echo "b"
|
||||
echo "c"
|
||||
echo "counter: $counter"
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
name: "Options: functrace (-T)"
|
||||
cases:
|
||||
#
|
||||
# Basic functrace option behavior
|
||||
#
|
||||
- name: "functrace can be enabled and disabled"
|
||||
stdin: |
|
||||
set -T
|
||||
echo "enabled: $-" | grep -q T && echo "T is set"
|
||||
set +T
|
||||
echo "disabled: $-" | grep -qv T && echo "T is unset"
|
||||
|
||||
#
|
||||
# DEBUG trap inheritance in functions
|
||||
#
|
||||
- name: "DEBUG trap - does not fire in function without set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
myfunc() {
|
||||
echo "in func"
|
||||
}
|
||||
echo "before func"
|
||||
myfunc
|
||||
echo "after func"
|
||||
|
||||
- name: "DEBUG trap - fires in function with set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
myfunc() {
|
||||
echo "in func"
|
||||
}
|
||||
echo "before func"
|
||||
myfunc
|
||||
echo "after func"
|
||||
|
||||
#
|
||||
# DEBUG trap inheritance in subshells
|
||||
#
|
||||
- name: "DEBUG trap - does not fire in subshell without set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
echo "before subshell"
|
||||
(echo "in subshell")
|
||||
echo "after subshell"
|
||||
|
||||
- name: "DEBUG trap - fires in subshell with set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
echo "before subshell"
|
||||
(echo "in subshell")
|
||||
echo "after subshell"
|
||||
|
||||
#
|
||||
# DEBUG trap inheritance in command substitution
|
||||
#
|
||||
- name: "DEBUG trap - does not fire in command substitution without set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
|
||||
echo "before"
|
||||
result=$(echo "in substitution")
|
||||
echo "result: $result"
|
||||
|
||||
- name: "DEBUG trap - fires in command substitution with set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
|
||||
echo "before"
|
||||
result=$(echo "in substitution")
|
||||
echo "result: $result"
|
||||
|
||||
#
|
||||
# DEBUG trap inheritance in pipelines
|
||||
#
|
||||
- name: "DEBUG trap - does not fire in pipeline without set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
|
||||
echo "before"
|
||||
echo "hello" | cat | cat
|
||||
echo "after"
|
||||
|
||||
- name: "DEBUG trap - fires in pipeline with set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
|
||||
echo "before"
|
||||
echo "hello" | cat | cat
|
||||
echo "after"
|
||||
|
||||
#
|
||||
# RETURN trap inheritance in functions
|
||||
#
|
||||
- name: "RETURN trap - does not fire in called function without set -T"
|
||||
stdin: |
|
||||
trap 'echo "[return: $FUNCNAME]"' RETURN
|
||||
inner() {
|
||||
echo "in inner"
|
||||
}
|
||||
outer() {
|
||||
echo "in outer"
|
||||
inner
|
||||
echo "outer continuing"
|
||||
}
|
||||
outer
|
||||
echo "after"
|
||||
|
||||
- name: "RETURN trap - fires in called function with set -T"
|
||||
known_failure: true # TODO(functrace): RETURN trap inheritance not implemented
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[return: ${FUNCNAME:-main}]"' RETURN
|
||||
inner() {
|
||||
echo "in inner"
|
||||
}
|
||||
outer() {
|
||||
echo "in outer"
|
||||
inner
|
||||
echo "outer continuing"
|
||||
}
|
||||
outer
|
||||
echo "after"
|
||||
|
||||
- name: "RETURN trap - fires for each nested function return with set -T"
|
||||
known_failure: true # TODO(functrace): RETURN trap inheritance not implemented
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[return: ${FUNCNAME:-main} -> $?]"' RETURN
|
||||
level3() { echo "level3"; return 3; }
|
||||
level2() { echo "level2"; level3; return 2; }
|
||||
level1() { echo "level1"; level2; return 1; }
|
||||
level1
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# Function-specific trace attribute (declare -t)
|
||||
#
|
||||
- name: "declare -t - enables trace for specific function"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
traced_func() {
|
||||
echo "in traced"
|
||||
}
|
||||
untraced_func() {
|
||||
echo "in untraced"
|
||||
}
|
||||
declare -t traced_func
|
||||
echo "calling untraced"
|
||||
untraced_func
|
||||
echo "calling traced"
|
||||
traced_func
|
||||
echo "done"
|
||||
|
||||
- name: "declare +t - disables trace for specific function"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
myfunc() {
|
||||
echo "in func"
|
||||
}
|
||||
declare +t myfunc
|
||||
echo "calling func"
|
||||
myfunc
|
||||
echo "done"
|
||||
|
||||
- name: "declare -f - shows trace attribute"
|
||||
stdin: |
|
||||
myfunc() { echo hi; }
|
||||
declare -t myfunc
|
||||
declare -f myfunc | head -1
|
||||
|
||||
#
|
||||
# Toggling set -T mid-script
|
||||
#
|
||||
- name: "set -T enabled mid-script affects subsequent calls"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
func1() { echo "in func1"; }
|
||||
func2() { echo "in func2"; }
|
||||
echo "calling func1 without -T"
|
||||
func1
|
||||
set -T
|
||||
echo "calling func2 with -T"
|
||||
func2
|
||||
echo "done"
|
||||
|
||||
- name: "set +T disables inheritance mid-script"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
func1() { echo "in func1"; }
|
||||
func2() { echo "in func2"; }
|
||||
echo "calling func1 with -T"
|
||||
func1
|
||||
set +T
|
||||
echo "calling func2 without -T"
|
||||
func2
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# DEBUG trap inheritance in background jobs
|
||||
#
|
||||
- name: "DEBUG trap - in background job with set -T"
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug]" >&2' DEBUG
|
||||
{
|
||||
echo "in background"
|
||||
} &
|
||||
wait
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# DEBUG trap inheritance in process substitution
|
||||
#
|
||||
- name: "DEBUG trap - in process substitution with set -T"
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[debug]" >&2' DEBUG
|
||||
cat <(echo "from procsub")
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# BASH_COMMAND visibility in DEBUG trap with functrace
|
||||
#
|
||||
- name: "BASH_COMMAND in function with set -T"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "cmd: $BASH_COMMAND"' DEBUG
|
||||
myfunc() {
|
||||
local v=10
|
||||
echo "v=$v"
|
||||
}
|
||||
myfunc
|
||||
|
||||
#
|
||||
# Nested functions with varying trace states
|
||||
#
|
||||
- name: "Outer function traced, inner function not traced"
|
||||
stdin: |
|
||||
trap 'echo "[debug in ${FUNCNAME:-main}]"' DEBUG
|
||||
inner() {
|
||||
echo "inner"
|
||||
}
|
||||
outer() {
|
||||
echo "outer start"
|
||||
inner
|
||||
echo "outer end"
|
||||
}
|
||||
declare -t outer
|
||||
outer
|
||||
echo "done"
|
||||
|
||||
- name: "Only inner function traced"
|
||||
stdin: |
|
||||
trap 'echo "[debug in ${FUNCNAME:-main}]"' DEBUG
|
||||
inner() {
|
||||
echo "inner"
|
||||
}
|
||||
outer() {
|
||||
echo "outer start"
|
||||
inner
|
||||
echo "outer end"
|
||||
}
|
||||
declare -t inner
|
||||
outer
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# RETURN trap with sourced scripts and set -T
|
||||
#
|
||||
- name: "RETURN trap - fires for function in sourced script with set -T"
|
||||
known_failure: true # TODO(functrace): RETURN trap inheritance not implemented
|
||||
test_files:
|
||||
- path: "sourced.sh"
|
||||
contents: |
|
||||
sourced_func() {
|
||||
echo "in sourced_func"
|
||||
}
|
||||
sourced_func
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[return: ${FUNCNAME:-main}]"' RETURN
|
||||
. ./sourced.sh
|
||||
echo "after source"
|
||||
|
||||
#
|
||||
# Edge cases
|
||||
#
|
||||
- name: "DEBUG trap - count in deeply nested function calls with set -T"
|
||||
known_failure: true # TODO(functrace): DEBUG trap count differs
|
||||
stdin: |
|
||||
set -T
|
||||
count=0
|
||||
trap 'count=$((count + 1))' DEBUG
|
||||
a() { b; }
|
||||
b() { c; }
|
||||
c() { echo "deep"; }
|
||||
a
|
||||
echo "debug count: $count"
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
name: "Options: inherit_errexit"
|
||||
cases:
|
||||
- name: "inherit_errexit disabled by default"
|
||||
stdin: |
|
||||
shopt -q inherit_errexit && echo "enabled" || echo "disabled"
|
||||
|
||||
- name: "inherit_errexit disabled - command substitution ignores errexit"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -u inherit_errexit
|
||||
result=$(false; echo "still running")
|
||||
echo "result: $result"
|
||||
echo "after"
|
||||
|
||||
- name: "inherit_errexit enabled - command substitution inherits errexit"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -s inherit_errexit
|
||||
result=$(false; echo "should not print")
|
||||
echo "should not print"
|
||||
|
||||
- name: "inherit_errexit with nested command substitution"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -s inherit_errexit
|
||||
result=$(echo "outer: $(false; echo inner)")
|
||||
echo "should not print"
|
||||
|
||||
- name: "inherit_errexit disabled - nested substitution continues"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -u inherit_errexit
|
||||
result=$(echo "outer: $(false; echo inner)")
|
||||
echo "result: $result"
|
||||
echo "after"
|
||||
|
||||
- name: "inherit_errexit with backtick substitution"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -s inherit_errexit
|
||||
result=`false; echo "should not print"`
|
||||
echo "should not print"
|
||||
|
||||
- name: "inherit_errexit with assignment"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -s inherit_errexit
|
||||
var=$(false)
|
||||
echo "should not print"
|
||||
|
||||
- name: "inherit_errexit in function"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -s inherit_errexit
|
||||
myfunc() {
|
||||
local result=$(false; echo "should not print")
|
||||
echo "after local"
|
||||
}
|
||||
myfunc
|
||||
echo "should not print"
|
||||
|
||||
- name: "inherit_errexit toggling"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -s inherit_errexit
|
||||
result1=$(false; echo "should not print in first")
|
||||
echo "should not print 1"
|
||||
ignore_stderr: true
|
||||
|
||||
- name: "inherit_errexit with multiple commands in substitution"
|
||||
stdin: |
|
||||
set -e
|
||||
shopt -u inherit_errexit
|
||||
result=$(echo "start"; false; echo "after false")
|
||||
echo "result: $result"
|
||||
echo "after"
|
||||
@@ -0,0 +1,9 @@
|
||||
name: "set -B"
|
||||
cases:
|
||||
- name: "set -B"
|
||||
stdin: |
|
||||
set +B
|
||||
echo "+B: " ${a,b}
|
||||
|
||||
set -B
|
||||
echo "-B: " ${a,b}
|
||||
@@ -0,0 +1,27 @@
|
||||
name: "set -C"
|
||||
cases:
|
||||
- name: "set -C"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
touch existing-file
|
||||
|
||||
set -C
|
||||
|
||||
echo hi > non-existing-file
|
||||
echo "Result (non existing): $?"
|
||||
echo "File contents: $(cat non-existing-file)"
|
||||
echo
|
||||
|
||||
echo hi > /dev/null
|
||||
echo "Result (device file): $?"
|
||||
echo
|
||||
|
||||
echo hi > existing-file
|
||||
echo "Result (existing file): $?"
|
||||
echo "File contents: $(cat existing-file)"
|
||||
echo
|
||||
|
||||
echo hi >| existing-file
|
||||
echo "Result (clobber): $?"
|
||||
echo "File contents: $(cat existing-file)"
|
||||
echo
|
||||
@@ -0,0 +1,20 @@
|
||||
name: "set -a"
|
||||
cases:
|
||||
- name: "set -a"
|
||||
stdin: |
|
||||
v1=1 v2=2 v3=3 v4=4 v5=(a b c)
|
||||
declare -p v1 v2 v3 v4
|
||||
|
||||
set -a
|
||||
v1=reassigned
|
||||
v2+=appended
|
||||
declare -i v3
|
||||
v4+=(appended)
|
||||
v5[2]=updated
|
||||
v6=new
|
||||
v7[0]=new
|
||||
v8=(new)
|
||||
declare v9=new
|
||||
declare -a v10=(new)
|
||||
|
||||
declare -p v1 v2 v3 v4 v5 v6 v7 v8 v9 v10
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
name: "set -n"
|
||||
cases:
|
||||
- name: "set -n"
|
||||
stdin: |
|
||||
set -n
|
||||
touch somefile.txt
|
||||
ls
|
||||
@@ -0,0 +1,7 @@
|
||||
name: "set -t"
|
||||
cases:
|
||||
- name: "set -t"
|
||||
args: ["-t"]
|
||||
stdin: |
|
||||
echo first
|
||||
echo second
|
||||
@@ -0,0 +1,534 @@
|
||||
name: "Options: set -u"
|
||||
cases:
|
||||
# Basic behavior
|
||||
- name: "Basic set variable access"
|
||||
stdin: |
|
||||
set -u
|
||||
var="value"
|
||||
echo "$var"
|
||||
|
||||
- name: "Unset variable triggers error"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
echo "$unset_var"
|
||||
echo "should not print"
|
||||
|
||||
- name: "Command line -u"
|
||||
args: ["-u"]
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
echo "$unset_var"
|
||||
echo "should not print"
|
||||
|
||||
# Special parameters - should NOT error even when unset
|
||||
- name: "Special parameter $@ does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "args: $@"
|
||||
|
||||
- name: "Special parameter $* does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "args: $*"
|
||||
|
||||
- name: "Special parameter $# does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "count: $#"
|
||||
|
||||
- name: "Special parameter $? does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
true
|
||||
echo "status: $?"
|
||||
|
||||
- name: "Special parameter $$ does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "pid: $$" > /dev/null
|
||||
echo "ok"
|
||||
|
||||
- name: "Special parameter $! does not error when no background jobs"
|
||||
known_failure: true
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
echo "last bg: ${!}" || echo "ok"
|
||||
|
||||
- name: "Special parameter $0 does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "shell: $0" > /dev/null
|
||||
echo "ok"
|
||||
|
||||
- name: "Special parameter $- does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "flags: $-" > /dev/null
|
||||
echo "ok"
|
||||
|
||||
# Positional parameters
|
||||
- name: "Unset positional parameter triggers error"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
echo "$1"
|
||||
}
|
||||
myfunc
|
||||
echo "should not print"
|
||||
|
||||
- name: "Set positional parameter does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
echo "$1"
|
||||
}
|
||||
myfunc "arg"
|
||||
|
||||
- name: "Positional parameters beyond provided args error"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
echo "$2"
|
||||
}
|
||||
myfunc "only_one_arg"
|
||||
echo "should not print"
|
||||
|
||||
# Parameter expansion forms
|
||||
- name: "Unset variable with default does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "${unset_var:-default}"
|
||||
|
||||
- name: "Empty vs unset with :- operator"
|
||||
stdin: |
|
||||
set -u
|
||||
empty=""
|
||||
echo "${empty:-default}"
|
||||
echo "${unset:-default}"
|
||||
|
||||
- name: "Empty vs unset with - operator"
|
||||
stdin: |
|
||||
set -u
|
||||
empty=""
|
||||
echo "${empty-default}"
|
||||
echo "${unset-default}"
|
||||
|
||||
# Arrays (if supported)
|
||||
- name: "Unset array element with default"
|
||||
stdin: |
|
||||
set -u
|
||||
arr=(a b c)
|
||||
echo "${arr[10]:-default}"
|
||||
|
||||
- name: "Unset array treated as unset variable"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "${unset_array[@]}"
|
||||
echo "should not print"
|
||||
|
||||
- name: "Unset array with default does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "${unset_array[@]:-default}"
|
||||
|
||||
# Conditionals
|
||||
- name: "Unset variable in conditional test errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
if [ "$unset_var" = "value" ]; then
|
||||
echo "then"
|
||||
fi
|
||||
echo "should not print"
|
||||
|
||||
- name: "Unset variable in [[ test errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
if [[ $unset_var = "value" ]]; then
|
||||
echo "then"
|
||||
fi
|
||||
echo "should not print"
|
||||
|
||||
- name: "Unset variable with default in conditional works"
|
||||
stdin: |
|
||||
set -u
|
||||
if [ "${unset_var:-}" = "" ]; then
|
||||
echo "empty"
|
||||
fi
|
||||
|
||||
- name: "test -v for unset variable"
|
||||
stdin: |
|
||||
set -u
|
||||
if test -v unset_var; then
|
||||
echo "set"
|
||||
else
|
||||
echo "unset"
|
||||
fi
|
||||
|
||||
- name: "test -z with unset variable errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
if test -z "$unset_var"; then
|
||||
echo "empty"
|
||||
fi
|
||||
echo "should not print"
|
||||
|
||||
# Command substitution
|
||||
- name: "Unset variable in command substitution errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
result=$(echo "$unset_var")
|
||||
echo "should not print"
|
||||
|
||||
- name: "Unset variable with default in command substitution"
|
||||
stdin: |
|
||||
set -u
|
||||
result=$(echo "${unset_var:-default}")
|
||||
echo "$result"
|
||||
|
||||
# Arithmetic expansion
|
||||
- name: "Unset variable in arithmetic defaults to zero"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
result=$((unset_var + 5))
|
||||
echo "$result"
|
||||
|
||||
# set +u to disable
|
||||
- name: "set +u disables unset variable check"
|
||||
stdin: |
|
||||
set -u
|
||||
set +u
|
||||
echo "$unset_var"
|
||||
echo "ok"
|
||||
|
||||
- name: "set -u can be re-enabled"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
set +u
|
||||
echo "$unset_var"
|
||||
set -u
|
||||
echo "$another_unset"
|
||||
echo "should not print"
|
||||
|
||||
- name: "set +u in function does not affect parent"
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
set +u
|
||||
echo "$unset_in_func"
|
||||
}
|
||||
myfunc
|
||||
echo "$unset_in_parent"
|
||||
echo "should not print"
|
||||
|
||||
# Functions
|
||||
- name: "Unset variable in function body errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
echo "$unset_var"
|
||||
}
|
||||
myfunc
|
||||
echo "should not print"
|
||||
|
||||
- name: "Local unset variable errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
local unset_local
|
||||
echo "$unset_local"
|
||||
}
|
||||
myfunc
|
||||
echo "should not print"
|
||||
|
||||
- name: "Local variable declaration with value works"
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
local my_var="value"
|
||||
echo "$my_var"
|
||||
}
|
||||
myfunc
|
||||
|
||||
- name: "Function parameter shadows unset global"
|
||||
stdin: |
|
||||
set -u
|
||||
myfunc() {
|
||||
local var="$1"
|
||||
echo "$var"
|
||||
}
|
||||
myfunc "arg"
|
||||
|
||||
# Subshells
|
||||
- name: "Unset variable in subshell errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
(echo "$unset_var")
|
||||
echo "should not print"
|
||||
|
||||
- name: "set +u in subshell does not affect parent"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
(
|
||||
set +u
|
||||
echo "$unset_in_subshell"
|
||||
)
|
||||
echo "$unset_in_parent"
|
||||
echo "should not print"
|
||||
|
||||
# Variable assignment and unset
|
||||
- name: "Variable set then unset errors on access"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
var="value"
|
||||
echo "$var"
|
||||
unset var
|
||||
echo "$var"
|
||||
echo "should not print"
|
||||
|
||||
- name: "Variable set then unset with default works"
|
||||
stdin: |
|
||||
set -u
|
||||
var="value"
|
||||
unset var
|
||||
echo "${var:-default}"
|
||||
|
||||
- name: "Unsetting non-existent variable does not error"
|
||||
stdin: |
|
||||
set -u
|
||||
unset non_existent
|
||||
echo "ok"
|
||||
|
||||
# Export and environment
|
||||
- name: "Exported unset variable errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
export unset_exported
|
||||
echo "$unset_exported"
|
||||
echo "should not print"
|
||||
|
||||
- name: "Exported variable with value works"
|
||||
stdin: |
|
||||
set -u
|
||||
export exported_var="value"
|
||||
echo "$exported_var"
|
||||
|
||||
# Pattern expansion
|
||||
- name: "Unset variable in pattern errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
echo ${unset_var#prefix}
|
||||
echo "should not print"
|
||||
|
||||
# Length operator
|
||||
- name: "Length of unset variable errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
echo ${#unset_var}
|
||||
echo "should not print"
|
||||
|
||||
- name: "Length of empty variable works"
|
||||
stdin: |
|
||||
set -u
|
||||
empty=""
|
||||
echo ${#empty}
|
||||
|
||||
# Indirect expansion
|
||||
- name: "Indirect expansion of unset variable errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
ref="unset_target"
|
||||
echo ${!ref}
|
||||
echo "should not print"
|
||||
|
||||
- name: "Indirect expansion to set variable works"
|
||||
stdin: |
|
||||
set -u
|
||||
target="value"
|
||||
ref="target"
|
||||
echo ${!ref}
|
||||
|
||||
# Case statements
|
||||
- name: "Unset variable in case pattern errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
case "$unset_var" in
|
||||
*) echo "matched" ;;
|
||||
esac
|
||||
echo "should not print"
|
||||
|
||||
- name: "Unset variable with default in case works"
|
||||
stdin: |
|
||||
set -u
|
||||
case "${unset_var:-default}" in
|
||||
default) echo "matched" ;;
|
||||
esac
|
||||
|
||||
# For loops
|
||||
- name: "Unset variable in for loop errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
for item in $unset_var; do
|
||||
echo "$item"
|
||||
done
|
||||
echo "should not print"
|
||||
|
||||
- name: "Unset variable with default in for loop works"
|
||||
stdin: |
|
||||
set -u
|
||||
for item in ${unset_var:-a b c}; do
|
||||
echo "$item"
|
||||
done
|
||||
|
||||
# While loops with unset variables
|
||||
- name: "Unset variable in while condition errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
while [ "$unset_var" != "stop" ]; do
|
||||
echo "loop"
|
||||
break
|
||||
done
|
||||
echo "should not print"
|
||||
|
||||
# Read command
|
||||
- name: "read into variable then access works"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "value" | read my_var || true
|
||||
echo "${my_var:-default}"
|
||||
|
||||
- name: "Variable unset before read then set works"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "test" | {
|
||||
read input
|
||||
echo "$input"
|
||||
}
|
||||
|
||||
# Compound assignments
|
||||
- name: "Associative array unset key with default"
|
||||
stdin: |
|
||||
set -u
|
||||
declare -A assoc
|
||||
echo "${assoc[key]:-default}"
|
||||
|
||||
# Pipelines
|
||||
- name: "Unset variable with default in pipeline works"
|
||||
stdin: |
|
||||
set -u
|
||||
echo "${unset_var:-default}" | cat
|
||||
|
||||
# Here documents and here strings
|
||||
- name: "Unset variable in here doc errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
cat <<EOF
|
||||
$unset_var
|
||||
EOF
|
||||
echo "should not print"
|
||||
|
||||
- name: "Unset variable with default in here doc works"
|
||||
stdin: |
|
||||
set -u
|
||||
cat <<EOF
|
||||
${unset_var:-default}
|
||||
EOF
|
||||
|
||||
# Edge cases
|
||||
- name: "Empty string is not unset"
|
||||
stdin: |
|
||||
set -u
|
||||
var=""
|
||||
echo "var is: '$var'"
|
||||
|
||||
- name: "Null value vs unset"
|
||||
stdin: |
|
||||
set -u
|
||||
null=""
|
||||
echo "${null-default}"
|
||||
echo "${unset-default}"
|
||||
|
||||
- name: "IFS unset behavior"
|
||||
stdin: |
|
||||
set -u
|
||||
unset IFS
|
||||
echo "ok"
|
||||
|
||||
- name: "PATH unset would error"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
saved_path="$PATH"
|
||||
unset PATH
|
||||
echo "$PATH"
|
||||
echo "should not print"
|
||||
|
||||
# Combination with other options
|
||||
- name: "set -u with set -e"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -eu
|
||||
echo "$unset_var"
|
||||
echo "should not print"
|
||||
|
||||
# Declare without assignment
|
||||
- name: "declare without value then access errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
declare my_var
|
||||
echo "$my_var"
|
||||
echo "should not print"
|
||||
|
||||
- name: "declare with value then access works"
|
||||
stdin: |
|
||||
set -u
|
||||
declare my_var="value"
|
||||
echo "$my_var"
|
||||
|
||||
- name: "readonly without value then access errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
readonly my_var
|
||||
echo "$my_var"
|
||||
echo "should not print"
|
||||
|
||||
- name: "Nameref to set variable with set -u"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
set -u
|
||||
target="value"
|
||||
declare -n ref=target
|
||||
echo "$ref"
|
||||
|
||||
- name: "Nameref to unset variable with set -u errors"
|
||||
known_failure: true
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
set -u
|
||||
declare -n ref=unset_target
|
||||
echo "$ref"
|
||||
echo "should not print"
|
||||
@@ -0,0 +1,81 @@
|
||||
name: "set -x"
|
||||
cases:
|
||||
- name: "basic"
|
||||
stdin: |
|
||||
set -x
|
||||
|
||||
ls
|
||||
|
||||
for f in 1 2 3; do echo ${f}; done
|
||||
|
||||
case 1 in
|
||||
1) echo "1";;
|
||||
*) echo "not";;
|
||||
esac
|
||||
|
||||
while false; do
|
||||
echo body
|
||||
done
|
||||
|
||||
newvar=$(echo "new")
|
||||
|
||||
x=$((3 + 7))
|
||||
|
||||
if [[ x == 10 && ! 0 ]]; then
|
||||
echo "Math checks"
|
||||
fi
|
||||
|
||||
var="x"
|
||||
[[ ${var} && ${var//[[:space:]]/} ]]
|
||||
|
||||
for ((i = 0; i < 3; i++)); do
|
||||
echo $i
|
||||
done
|
||||
|
||||
((x = 3)) || ((x = 4))
|
||||
|
||||
override=value echo some_output
|
||||
|
||||
- name: "sourced script"
|
||||
test_files:
|
||||
- path: script.sh
|
||||
contents: |
|
||||
echo "Entered script"
|
||||
for f in a b c; do echo $f; done
|
||||
echo "Exiting script"
|
||||
stdin: |
|
||||
set -x
|
||||
|
||||
echo "About to source script"
|
||||
source script.sh
|
||||
echo "Back from script"
|
||||
|
||||
- name: "functions"
|
||||
stdin: |
|
||||
set -x
|
||||
|
||||
myfunc() {
|
||||
echo "In myfunc"
|
||||
for i in 1 2; do echo "i is $i"; done
|
||||
}
|
||||
|
||||
echo "Before function call"
|
||||
myfunc
|
||||
echo "After function call"
|
||||
|
||||
- name: "command line"
|
||||
args: ["-x", "script.sh"]
|
||||
test_files:
|
||||
- path: script.sh
|
||||
contents: |
|
||||
echo "In command line script"
|
||||
for n in x y z; do echo $n; done
|
||||
|
||||
- name: "trap"
|
||||
known_failure: true # TODO(traps): review this
|
||||
stdin: |
|
||||
set -x
|
||||
|
||||
trap 'echo "In trap"' DEBUG
|
||||
|
||||
echo "Doing something"
|
||||
@@ -0,0 +1,79 @@
|
||||
name: "Options: failglob"
|
||||
cases:
|
||||
- name: "failglob errors on unmatched glob"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
echo non-existent-*.txt 2>/dev/null
|
||||
echo "exit=$?"
|
||||
|
||||
- name: "failglob does not affect matching glob"
|
||||
stdin: |
|
||||
touch realfile.txt
|
||||
shopt -s failglob
|
||||
echo real*.txt
|
||||
|
||||
- name: "failglob does not affect quoted glob chars"
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
echo 'non-existent-*.txt'
|
||||
|
||||
- name: "failglob with negation"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
! echo non-existent-*.txt 2>/dev/null
|
||||
echo "exit=$?"
|
||||
|
||||
- name: "failglob in subshell"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
(echo non-existent-*.txt; echo "inner=$?")
|
||||
echo "outer=$?"
|
||||
|
||||
- name: "failglob lone ] is not a glob"
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
echo foo]
|
||||
|
||||
- name: "failglob lone [ is not a glob"
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
echo [abc
|
||||
|
||||
- name: "failglob aborts compound list"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
echo *.nomatch; echo "after"
|
||||
|
||||
- name: "failglob in multi-member pipeline"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
echo *.nomatch | cat
|
||||
echo "after"
|
||||
|
||||
- name: "failglob extglob pattern"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
shopt -s extglob
|
||||
echo @(nomatch_pattern_xyz)
|
||||
echo "after"
|
||||
|
||||
- name: "failglob takes precedence over nullglob"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob nullglob
|
||||
echo non-existent-*.txt 2>/dev/null
|
||||
echo "exit=$?"
|
||||
|
||||
- name: "failglob on unquoted variable with glob chars"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
shopt -s failglob
|
||||
x='*.nomatch_xyz'
|
||||
echo $x 2>/dev/null
|
||||
echo "exit=$?"
|
||||
@@ -0,0 +1,11 @@
|
||||
name: "shopt lastpipe"
|
||||
cases:
|
||||
- name: "shopt -s lastpipe"
|
||||
stdin: |
|
||||
echo ignored | read var
|
||||
echo "1. var='${var}'"
|
||||
|
||||
shopt -s lastpipe
|
||||
set +o monitor
|
||||
echo ignored | read var
|
||||
echo "2. var='${var}'"
|
||||
@@ -0,0 +1,12 @@
|
||||
name: "shopt login_shell"
|
||||
cases:
|
||||
- name: "login_shell is on for login shells"
|
||||
args:
|
||||
- "--login"
|
||||
- "-c"
|
||||
- "shopt -q login_shell"
|
||||
|
||||
- name: "login_shell is off for non-login shells"
|
||||
args:
|
||||
- "-c"
|
||||
- "shopt -q login_shell"
|
||||
@@ -0,0 +1,79 @@
|
||||
name: "Options: nullglob"
|
||||
cases:
|
||||
- name: "nullglob removes unmatched glob"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo "result:" non-existent-*.txt
|
||||
|
||||
- name: "nullglob does not affect matching glob"
|
||||
stdin: |
|
||||
touch realfile.txt
|
||||
shopt -s nullglob
|
||||
echo real*.txt
|
||||
|
||||
- name: "nullglob does not affect quoted glob chars"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo 'non-existent-*.txt'
|
||||
|
||||
- name: "nullglob does not affect literal text without glob chars"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo hello world
|
||||
|
||||
- name: "nullglob with multiple unmatched patterns"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo "result:" *.zzz *.yyy *.xxx
|
||||
|
||||
- name: "nullglob with mix of matched and unmatched"
|
||||
stdin: |
|
||||
touch realfile.txt
|
||||
shopt -s nullglob
|
||||
echo real*.txt *.zzz
|
||||
|
||||
- name: "nullglob with question mark glob"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo "result:" ?.nonexistent
|
||||
|
||||
- name: "nullglob with bracket glob"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo "result:" [abc].nonexistent
|
||||
|
||||
- name: "nullglob disabled by default"
|
||||
stdin: |
|
||||
echo *.nonexistent
|
||||
|
||||
- name: "nullglob can be toggled"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo "on:" *.nonexistent
|
||||
shopt -u nullglob
|
||||
echo "off:" *.nonexistent
|
||||
|
||||
- name: "nullglob in subshell"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
(echo "inner:" *.nonexistent)
|
||||
echo "outer:" *.nonexistent
|
||||
|
||||
- name: "nullglob with for loop"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
for f in *.nonexistent; do
|
||||
echo "unexpected: $f"
|
||||
done
|
||||
echo "done"
|
||||
|
||||
- name: "nullglob with array assignment"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
arr=(*.nonexistent)
|
||||
echo "count: ${#arr[@]}"
|
||||
|
||||
- name: "nullglob with directory glob"
|
||||
stdin: |
|
||||
shopt -s nullglob
|
||||
echo "result:" nonexistent_dir/*/
|
||||
Reference in New Issue
Block a user