| 1 | #!/bin/sh |
| 2 | TEST_PREFIX="[int-expansion]" |
| 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: builtins with parameter, arithmetic, and command substitution |
| 9 | |
| 10 | section "1. parameter expansion with builtins" |
| 11 | compare_output "default value" 'X=hello; echo ${X:-default}' |
| 12 | compare_output "unset default" 'echo ${UNSET_VAR_XYZ:-fallback}' |
| 13 | compare_output "alternate value set" 'X=hello; echo ${X:+alternate}' |
| 14 | compare_output "alternate value unset" 'echo ${UNSET_VAR_XYZ:+alternate}' |
| 15 | compare_output "prefix strip" 'X=hello; echo ${X#h}' |
| 16 | compare_output "prefix strip greedy" 'X=hello; echo ${X##*l}' |
| 17 | compare_output "suffix strip" 'X=hello; echo ${X%o}' |
| 18 | compare_output "suffix strip greedy" 'X=hello; echo ${X%%l*}' |
| 19 | compare_output "substitution" 'X=hello_world; echo ${X/world/earth}' |
| 20 | compare_output "substitution global" 'X=ababa; echo ${X//a/x}' |
| 21 | compare_output "string length" 'X=hello; echo ${#X}' |
| 22 | compare_output "assign default" 'unset ADEF; echo ${ADEF:=assigned}; echo $ADEF' |
| 23 | compare_output "substring" 'X=hello; echo ${X:1:3}' |
| 24 | compare_output "uppercase" 'X=hello; echo ${X^^}' |
| 25 | compare_output "lowercase" 'X=HELLO; echo ${X,,}' |
| 26 | compare_output "first char upper" 'X=hello; echo ${X^}' |
| 27 | compare_output "first char lower" 'X=HELLO; echo ${X,}' |
| 28 | |
| 29 | section "2. array expansion with builtins" |
| 30 | compare_output "all elements" 'arr=(a b c); echo ${arr[@]}' |
| 31 | compare_output "element count" 'arr=(a b c); echo ${#arr[@]}' |
| 32 | compare_output "index access" 'arr=(a b c); echo ${arr[1]}' |
| 33 | compare_output "first element" 'arr=(a b c); echo ${arr[0]}' |
| 34 | compare_output "last element" 'arr=(a b c); echo ${arr[-1]}' |
| 35 | compare_output "array slice" 'arr=(a b c d e); echo ${arr[@]:1:3}' |
| 36 | compare_output "iterate array" 'arr=(a b c); for x in "${arr[@]}"; do echo $x; done' |
| 37 | compare_output "array length of element" 'arr=(hello world); echo ${#arr[0]}' |
| 38 | compare_output "assoc array access" 'declare -A map; map[key]=value; echo ${map[key]}' |
| 39 | compare_output "assoc array keys" 'declare -A m; m[a]=1; m[b]=2; echo ${!m[@]} | tr " " "\n" | sort | tr "\n" " "; echo' |
| 40 | compare_output "array append" 'arr=(a b); arr+=(c); echo ${arr[@]}' |
| 41 | compare_output "array unset element" 'arr=(a b c); unset arr[1]; echo ${arr[@]}' |
| 42 | |
| 43 | section "3. arithmetic expansion" |
| 44 | compare_output "basic add" 'echo $((3 + 4))' |
| 45 | compare_output "variable in arithmetic" 'X=5; echo $((X * 2))' |
| 46 | compare_output "exponent" 'echo $((2 ** 10))' |
| 47 | compare_output "grouping" 'echo $(( (3 + 4) * 2 ))' |
| 48 | compare_output "modulo" 'echo $((17 % 5))' |
| 49 | compare_output "division" 'echo $((10 / 3))' |
| 50 | compare_output "comparison in arithmetic" 'echo $((5 > 3))' |
| 51 | compare_output "ternary" 'X=5; echo $(( X > 3 ? 1 : 0 ))' |
| 52 | compare_output "increment" 'X=5; echo $((++X)); echo $X' |
| 53 | compare_output "decrement" 'X=5; echo $((--X)); echo $X' |
| 54 | compare_output "compound assignment" 'X=10; echo $((X += 5)); echo $X' |
| 55 | compare_output "bitwise and" 'echo $((12 & 10))' |
| 56 | compare_output "bitwise or" 'echo $((12 | 10))' |
| 57 | compare_output "left shift" 'echo $((1 << 4))' |
| 58 | |
| 59 | section "4. command substitution" |
| 60 | compare_output "basic cmd sub" 'echo "$(echo hello)"' |
| 61 | compare_output "nested cmd sub" 'echo "$(echo $(echo nested))"' |
| 62 | compare_output "cd in cmd sub" 'X=$(cd /tmp && pwd); echo $X' |
| 63 | compare_output "exit status from cmd sub" 'X=$(true); echo $?' |
| 64 | compare_output "false status from cmd sub" 'X=$(false); echo $?' |
| 65 | compare_output "backtick form" 'echo `echo backtick`' |
| 66 | compare_output "cmd sub preserves newlines in var" 'X=$(printf "a\nb"); echo "$X"' |
| 67 | compare_output "cmd sub strips trailing newline" 'X=$(echo hello); echo $X' |
| 68 | compare_output "cmd sub in arithmetic" 'echo $(( $(echo 3) + $(echo 4) ))' |
| 69 | compare_output "nested cmd sub with processing" 'echo $(echo $(echo hello) | tr a-z A-Z)' |
| 70 | |
| 71 | section "5. tilde expansion" |
| 72 | compare_output "tilde expands to HOME" 'echo ~ | grep -q / && echo yes' |
| 73 | compare_output "tilde with path" 'echo ~/subdir | grep -q / && echo yes' |
| 74 | compare_output "cd with tilde" 'cd ~ && echo $? || echo failed' |
| 75 | |
| 76 | section "6. word splitting and IFS" |
| 77 | compare_output "IFS colon split" 'IFS=:; read A B C <<< "one:two:three"; echo "$A $B $C"' |
| 78 | compare_output "custom IFS in for" 'IFS=,; for x in $(echo "a,b,c"); do echo $x; done' |
| 79 | compare_output "word splitting with set" 'X="a b c"; set -- $X; echo $#' |
| 80 | compare_output "IFS default splitting" 'X="a b c"; set -- $X; echo $1 $2 $3' |
| 81 | compare_output "IFS empty no splitting" 'IFS=""; X="a b c"; set -- $X; echo $#' |
| 82 | |
| 83 | section "7. brace expansion" |
| 84 | compare_output "comma brace" 'echo {a,b,c}' |
| 85 | compare_output "range brace" 'echo {1..5}' |
| 86 | compare_output "range with step" 'echo {1..10..2}' |
| 87 | compare_output "brace with prefix" 'echo file.{txt,md,sh}' |
| 88 | compare_output "nested brace" 'echo {a,b}{1,2}' |
| 89 | |
| 90 | section "8. quoting interaction" |
| 91 | compare_output "double quotes preserve" 'X="hello world"; echo "$X"' |
| 92 | compare_output "single quotes literal" 'X="hello world"; echo '"'"'$X'"'"'' |
| 93 | compare_output "nested quoting in cmd sub" 'echo "$(echo "nested quotes")"' |
| 94 | compare_output "escaped dollar" 'echo \$HOME' |
| 95 | compare_output "escaped backtick" 'echo \`echo hi\`' |
| 96 | compare_output "mixed quoting" "echo 'single' \"double\" plain" |
| 97 | compare_output "quote in variable" 'X="it'"'"'s"; echo "$X"' |
| 98 | |
| 99 | print_summary |