Bash · 4634 bytes Raw Blame History
1 #!/bin/sh
2 TEST_PREFIX="[int-function]"
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 in/with functions
9
10 section "1. scope interaction"
11 compare_output "local variable scope" 'x=global; f() { local x=local; echo $x; }; f; echo $x'
12 compare_output "declare scope in function" 'f() { declare -i n=3+4; echo $n; }; f; echo ${n:-unset}'
13 compare_output "export from function persists" 'f() { export FE=val; }; f; echo $FE'
14 compare_output "local array scope" 'f() { local -a arr=(1 2 3); echo ${arr[@]}; }; f; echo ${arr:-unset}'
15 compare_output "readonly from function persists" 'f() { readonly FR=frozen; }; f; echo $FR'
16 compare_output "declare -g in function" 'f() { declare -g GV=global_val; }; f; echo $GV'
17 compare_output "local doesnt affect global" 'X=outer; f() { local X=inner; }; f; echo $X'
18 compare_output "unset local reveals global" 'X=global; f() { local X=local; unset X; echo ${X:-gone}; }; f; echo $X'
19 compare_output "multiple locals" 'f() { local A=1 B=2 C=3; echo $A $B $C; }; f; echo ${A:-x} ${B:-y} ${C:-z}'
20
21 section "2. return and exit status"
22 compare_output "return 0" 'f() { return 0; }; f; echo $?'
23 compare_output "return 42" 'f() { return 42; }; f; echo $?'
24 compare_output "early return" 'f() { echo before; return; echo after; }; f'
25 compare_output "implicit return status" 'f() { false; }; f; echo $?'
26 compare_output "return in conditional" 'f() { return 0; }; f && echo yes'
27 compare_output "return from nested if" 'f() { if true; then return 5; fi; echo unreachable; }; f; echo $?'
28 compare_output "return preserves value" 'f() { true; return; }; f; echo $?'
29
30 section "3. nested functions"
31 compare_output "simple nested call" 'a() { echo a; }; b() { a; echo b; }; b'
32 compare_output "inner function def" 'outer() { inner() { echo inner; }; inner; }; outer'
33 compare_output "scope in nested" 'f() { local x=1; g() { echo $x; }; g; }; f'
34 compare_output "chain of three" 'a() { echo 1; }; b() { a; echo 2; }; c() { b; echo 3; }; c'
35 compare_output "mutual call" 'a() { echo a; }; b() { echo b; a; }; b'
36 compare_output "function overwrite" 'f() { echo old; }; f; f() { echo new; }; f'
37
38 section "4. recursion"
39 compare_output "countdown" 'countdown() { [ $1 -le 0 ] && return; echo $1; countdown $(($1 - 1)); }; countdown 5'
40 compare_output "recursive sum" 'sum() { [ $1 -le 0 ] && echo 0 && return; echo $(( $1 + $(sum $(($1 - 1))) )); }; sum 5'
41 compare_output "fibonacci" 'fib() { [ $1 -le 1 ] && echo $1 && return; echo $(( $(fib $(($1 - 1))) + $(fib $(($1 - 2))) )); }; fib 6'
42
43 section "5. positional parameters"
44 compare_output "dollar-hash dollar-1 dollar-2" 'f() { echo $# $1 $2; }; f a b c'
45 compare_output "shift in function" 'f() { echo $1; shift; echo $1; }; f first second'
46 compare_output "set in function" 'f() { set -- x y z; echo $@; }; f a b c'
47 compare_output "set doesnt leak" 'set -- a b; f() { set -- x y z; echo $@; }; f; echo $@'
48 compare_output "dollar-at quoted" 'f() { for arg in "$@"; do echo "arg:$arg"; done; }; f "hello world" foo'
49 compare_output "dollar-star" 'f() { echo $*; }; f a b c'
50
51 section "6. trap in functions"
52 compare_output "RETURN trap" 'f() { trap "echo cleaned" RETURN; echo body; }; f'
53 compare_output "ERR trap in function" 'f() { trap "echo err" ERR; false; }; f 2>/dev/null'
54 compare_output "function as trap handler" 'cleanup() { echo bye; }; trap cleanup EXIT; echo main'
55 compare_output "trap reset in function" 'f() { trap "echo trap1" RETURN; trap - RETURN; echo body; }; f'
56
57 section "7. functions with builtins"
58 compare_output "cd in function affects caller" 'ORIG=$(pwd); f() { cd /tmp && pwd; }; f; cd "$ORIG"'
59 compare_output "eval in function" 'f() { eval "echo hello"; }; f'
60 compare_output "source in function" "echo 'echo sourced' > $TEST_TMPDIR/fsrc.sh; f() { source $TEST_TMPDIR/fsrc.sh; }; f"
61 compare_output "hash in function" 'f() { hash -r 2>/dev/null; echo ok; }; f'
62 compare_output "type in function" 'f() { type echo 2>/dev/null | head -1; }; f'
63 compare_output "printf in function" 'f() { printf "%d %d %d\n" 1 2 3; }; f'
64 compare_output "test in function" 'f() { test 5 -gt 3 && echo yes; }; f'
65
66 section "8. getopts in functions"
67 compare_output "getopts basic in function" 'f() { OPTIND=1; while getopts "ab:" opt; do echo "$opt $OPTARG"; done; }; f -a -b val'
68 compare_output "getopts called twice" 'f() { OPTIND=1; while getopts "x" opt; do echo $opt; done; }; f -x; f -x'
69 compare_output "getopts with args after" 'f() { OPTIND=1; while getopts "a" opt; do echo $opt; done; shift $((OPTIND-1)); echo $1; }; f -a rest'
70
71 print_summary