| 1 | #!/bin/sh |
| 2 | TEST_PREFIX="[int-scope-env]" |
| 3 | SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) |
| 4 | SHELL_BIN="${SHELL_BIN:?ERROR: SHELL_BIN must be set}" |
| 5 | export SHELL_BIN |
| 6 | . "$SCRIPT_DIR/../test_harness.sh" |
| 7 | |
| 8 | # Integration tests: variable scope, export, source, environment inheritance |
| 9 | |
| 10 | section "1. export inheritance" |
| 11 | compare_output "exported var in subshell" 'export SEX=hello; (echo $SEX)' |
| 12 | compare_output "non-exported not in subshell cmd" 'SEX2=hello; echo ${SEX2:-unset}' |
| 13 | compare_output "export from function" 'f() { export SEF=val; }; f; echo $SEF' |
| 14 | compare_output "export -n removes export" 'export SEN=val; export -n SEN; echo $SEN' |
| 15 | compare_output "export with value" 'export SEV=hello; echo $SEV' |
| 16 | compare_output "export existing var" 'SEE=existing; export SEE; echo $SEE' |
| 17 | |
| 18 | section "2. source/dot scope" |
| 19 | compare_output "var from source" "echo 'SSV=sourced' > $TEST_TMPDIR/scope1.sh; source $TEST_TMPDIR/scope1.sh; echo \$SSV" |
| 20 | compare_output "source overwrites var" "X_SC=before; echo 'X_SC=after' > $TEST_TMPDIR/scope2.sh; source $TEST_TMPDIR/scope2.sh; echo \$X_SC" |
| 21 | compare_output "return from source" "echo 'return 42' > $TEST_TMPDIR/scope3.sh; source $TEST_TMPDIR/scope3.sh; echo \$?" |
| 22 | compare_output "cd from source" "echo 'cd /tmp' > $TEST_TMPDIR/scope4.sh; ORIG=\$(pwd); source $TEST_TMPDIR/scope4.sh; pwd; cd \"\$ORIG\"" |
| 23 | compare_output "function def from source" "echo 'sf() { echo sourced_func; }' > $TEST_TMPDIR/scope5.sh; source $TEST_TMPDIR/scope5.sh; sf" |
| 24 | compare_output "multiple source" "echo 'A_SC=1' > $TEST_TMPDIR/scope6a.sh; echo 'B_SC=2' > $TEST_TMPDIR/scope6b.sh; source $TEST_TMPDIR/scope6a.sh; source $TEST_TMPDIR/scope6b.sh; echo \$A_SC \$B_SC" |
| 25 | compare_output "source with args" "echo 'echo \$1 \$2' > $TEST_TMPDIR/scope7.sh; source $TEST_TMPDIR/scope7.sh hello world" |
| 26 | compare_output "dot command same as source" "echo 'DSV=dotted' > $TEST_TMPDIR/scope8.sh; . $TEST_TMPDIR/scope8.sh; echo \$DSV" |
| 27 | |
| 28 | section "3. eval scope" |
| 29 | compare_output "eval modifies scope" 'eval "ESV=from_eval"; echo $ESV' |
| 30 | compare_output "eval export" 'eval "export ESE=eval_export"; echo $ESE' |
| 31 | compare_output "eval readonly" 'eval "readonly ESR=eval_readonly"; echo $ESR' |
| 32 | compare_output "eval function def" 'eval "esf() { echo eval_func; }"; esf' |
| 33 | compare_output "eval preserves vars" 'X_EV=before; eval "echo \$X_EV"' |
| 34 | compare_output "nested eval" 'eval "eval \"echo nested_eval\""' |
| 35 | |
| 36 | section "4. declare -g" |
| 37 | compare_output "declare -g from function" 'f() { declare -g DGV=global_val; }; f; echo $DGV' |
| 38 | compare_output "declare without -g stays local" 'f() { declare DLV=local_val; }; f; echo ${DLV:-unset}' |
| 39 | compare_output "declare -g overwrites" 'DGO=old; f() { declare -g DGO=new; }; f; echo $DGO' |
| 40 | |
| 41 | section "5. IFS manipulation scope" |
| 42 | compare_output "IFS in function local" 'f() { local IFS=:; read A B <<< "x:y"; echo $A $B; }; f; echo "${IFS:-default}" | cat -v' |
| 43 | compare_output "IFS change persists" 'IFS=:; read A B <<< "x:y"; echo $A; IFS=" "' |
| 44 | compare_output "IFS in subshell" '(IFS=:; read A B <<< "x:y"; echo $A $B)' |
| 45 | compare_output "IFS reset" 'IFS=:; unset IFS; echo "a b c" | { read A B C; echo $A; }' |
| 46 | |
| 47 | section "6. readonly scope" |
| 48 | compare_output "readonly visible in functions" 'readonly SRV=fixed; f() { echo $SRV; }; f' |
| 49 | compare_output "readonly visible in subshells" 'readonly SRS=fixed; (echo $SRS)' |
| 50 | compare_output "readonly from function persists" 'f() { readonly SRF=in_func; }; f; echo $SRF' |
| 51 | compare_output "readonly blocks reassignment" 'readonly SRB=fixed; { SRB=other; } 2>/dev/null; echo $SRB' |
| 52 | compare_output "readonly blocks unset" 'readonly SRU=fixed; unset SRU 2>/dev/null; echo $SRU' |
| 53 | |
| 54 | section "7. variable cleanup" |
| 55 | compare_output "unset removes var" 'SUV=temp; unset SUV; echo ${SUV:-gone}' |
| 56 | compare_output "unset array" 'SUA=(1 2 3); unset SUA; echo ${SUA:-gone}' |
| 57 | compare_output "unset function" 'suf() { echo hi; }; unset -f suf; suf 2>/dev/null; echo $?' |
| 58 | compare_output "unset in function" 'SUF2=outer; f() { unset SUF2; }; f; echo ${SUF2:-gone}' |
| 59 | compare_output "unset exported var" 'export SUE=val; unset SUE; echo ${SUE:-gone}' |
| 60 | |
| 61 | print_summary |