summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/tests/subshell.sh
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/tests/subshell.sh
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/tests/subshell.sh')
-rwxr-xr-xsrc/cmd/ksh93/tests/subshell.sh582
1 files changed, 582 insertions, 0 deletions
diff --git a/src/cmd/ksh93/tests/subshell.sh b/src/cmd/ksh93/tests/subshell.sh
new file mode 100755
index 0000000..819811a
--- /dev/null
+++ b/src/cmd/ksh93/tests/subshell.sh
@@ -0,0 +1,582 @@
+########################################################################
+# #
+# This software is part of the ast package #
+# Copyright (c) 1982-2012 AT&T Intellectual Property #
+# and is licensed under the #
+# Eclipse Public License, Version 1.0 #
+# by AT&T Intellectual Property #
+# #
+# A copy of the License is available at #
+# http://www.eclipse.org/org/documents/epl-v10.html #
+# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
+# #
+# Information and Software Systems Research #
+# AT&T Research #
+# Florham Park NJ #
+# #
+# David Korn <dgk@research.att.com> #
+# #
+########################################################################
+function err_exit
+{
+ print -u$Error_fd -n "\t"
+ print -u$Error_fd -r ${Command}[$1]: "${@:2}"
+ (( Errors+=1 ))
+}
+alias err_exit='err_exit $LINENO'
+
+Command=${0##*/}
+integer Errors=0 Error_fd=2
+
+tmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; }
+trap "cd /; rm -rf $tmp" EXIT
+
+bincat=$(PATH=$(getconf PATH) whence -p cat)
+
+z=()
+z.foo=( [one]=hello [two]=(x=3 y=4) [three]=hi)
+z.bar[0]=hello
+z.bar[2]=world
+z.bar[1]=(x=4 y=5)
+val='(
+ typeset -a bar=(
+ [0]=hello
+ [2]=world
+ [1]=(
+ x=4
+ y=5
+ )
+ )
+ typeset -A foo=(
+ [one]=hello
+ [three]=hi
+ [two]=(
+ x=3
+ y=4
+ )
+ )
+)'
+[[ $z == "$val" ]] || err_exit 'compound variable with mixed arrays not working'
+z.bar[1]=yesyes
+[[ ${z.bar[1]} == yesyes ]] || err_exit 'reassign of index array compound variable fails'
+z.bar[1]=(x=12 y=5)
+[[ ${z.bar[1]} == $'(\n\tx=12\n\ty=5\n)' ]] || err_exit 'reassign array simple to compound variable fails'
+eval val="$z"
+(
+ z.foo[three]=good
+ [[ ${z.foo[three]} == good ]] || err_exit 'associative array assignment in subshell not working'
+)
+[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment'
+eval val="$z"
+(
+ z.foo[two]=ok
+ [[ ${z.foo[two]} == ok ]] || err_exit 'associative array assignment to compound variable in subshell not working'
+ z.bar[1]=yes
+ [[ ${z.bar[1]} == yes ]] || err_exit 'index array assignment to compound variable in subshell not working'
+)
+[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment'
+
+x=(
+ foo=( qqq=abc rrr=def)
+ bar=( zzz=no rst=fed)
+)
+eval val="$x"
+(
+ unset x.foo
+ [[ ${x.foo.qqq} ]] && err_exit 'x.foo.qqq should be unset'
+ x.foo=good
+ [[ ${x.foo} == good ]] || err_exit 'x.foo should be good'
+)
+[[ $x == "$val" ]] || err_exit 'compound variable changes after unset leaves'
+unset l
+(
+ l=( a=1 b="BE" )
+)
+[[ ${l+foo} != foo ]] || err_exit 'l should be unset'
+
+Error_fd=9
+eval "exec $Error_fd>&2 2>/dev/null"
+
+TEST_notfound=notfound
+while whence $TEST_notfound >/dev/null 2>&1
+do TEST_notfound=notfound-$RANDOM
+done
+
+
+integer BS=1024 nb=64 ss=60 bs no
+for bs in $BS 1
+do $SHELL -c '
+ {
+ sleep '$ss'
+ kill -KILL $$
+ } &
+ set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs')
+ print ${#1}
+ kill $!
+ ' > $tmp/sub 2>/dev/null
+ no=$(<$tmp/sub)
+ (( no == (BS * nb) )) || err_exit "shell hangs on command substitution output size >= $BS*$nb with write size $bs -- expected $((BS*nb)), got ${no:-0}"
+done
+# this time with redirection on the trailing command
+for bs in $BS 1
+do $SHELL -c '
+ {
+ sleep 2
+ sleep '$ss'
+ kill -KILL $$
+ } &
+ set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs' 2>/dev/null)
+ print ${#1}
+ kill $!
+ ' > $tmp/sub 2>/dev/null
+ no=$(<$tmp/sub)
+ (( no == (BS * nb) )) || err_exit "shell hangs on command substitution output size >= $BS*$nb with write size $bs and trailing redirection -- expected $((BS*nb)), got ${no:-0}"
+done
+
+# exercise command substitutuion trailing newline logic w.r.t. pipe vs. tmp file io
+
+set -- \
+ 'post-line print' \
+ '$TEST_unset; ($TEST_fork; print 1); print' \
+ 1 \
+ 'pre-line print' \
+ '$TEST_unset; ($TEST_fork; print); print 1' \
+ $'\n1' \
+ 'multiple pre-line print' \
+ '$TEST_unset; ($TEST_fork; print); print; ($TEST_fork; print 1); print' \
+ $'\n\n1' \
+ 'multiple post-line print' \
+ '$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print); print' \
+ 1 \
+ 'intermediate print' \
+ '$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print 2); print' \
+ $'1\n\n2' \
+ 'simple variable' \
+ '$TEST_unset; ($TEST_fork; l=2; print "$l"); print $l' \
+ 2 \
+ 'compound variable' \
+ '$TEST_unset; ($TEST_fork; l=(a=2 b="BE"); print "$l"); print $l' \
+ $'(\n\ta=2\n\tb=BE\n)' \
+
+export TEST_fork TEST_unset
+
+while (( $# >= 3 ))
+do txt=$1
+ cmd=$2
+ exp=$3
+ shift 3
+ for TEST_unset in '' 'unset var'
+ do for TEST_fork in '' 'ulimit -c 0'
+ do for TEST_shell in "eval" "$SHELL -c"
+ do if ! got=$($TEST_shell "$cmd")
+ then err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt print failed"
+ elif [[ "$got" != "$exp" ]]
+ then EXP=$(printf %q "$exp")
+ GOT=$(printf %q "$got")
+ err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt command substitution failed -- expected $EXP, got $GOT"
+ fi
+ done
+ done
+ done
+done
+
+r=$( ($SHELL -c '
+ {
+ sleep 32
+ kill -KILL $$
+ } &
+ for v in $(set | sed "s/=.*//")
+ do command unset $v
+ done
+ typeset -Z5 I
+ for ((I = 0; I < 1024; I++))
+ do eval A$I=1234567890
+ done
+ a=$(set 2>&1)
+ print ok
+ kill -KILL $!
+') 2>/dev/null)
+[[ $r == ok ]] || err_exit "large subshell command substitution hangs"
+
+for TEST_command in '' $TEST_notfound
+do for TEST_exec in '' 'exec'
+ do for TEST_fork in '' 'ulimit -c 0;'
+ do for TEST_redirect in '' '>/dev/null'
+ do for TEST_substitute in '' ': $'
+ do
+
+ TEST_test="$TEST_substitute($TEST_fork $TEST_exec $TEST_command $TEST_redirect)"
+ [[ $TEST_test == '('*([[:space:]])')' ]] && continue
+ r=$($SHELL -c '
+ {
+ sleep 2
+ kill -KILL $$
+ } &
+ '"$TEST_test"'
+ kill $!
+ print ok
+ ')
+ [[ $r == ok ]] || err_exit "shell hangs on $TEST_test"
+
+ done
+ done
+ done
+ done
+done
+
+$SHELL -c '( autoload xxxxx);print -n' || err_exit 'autoloaded functions in subshells can cause failure'
+foo=$($SHELL <<- ++EOF++
+ (trap 'print bar' EXIT;print -n foo)
+ ++EOF++
+)
+[[ $foo == foobar ]] || err_exit 'trap on exit when last commands is subshell is not triggered'
+
+err=$(
+ $SHELL 2>&1 <<- \EOF
+ date=$(whence -p date)
+ function foo
+ {
+ x=$( $date > /dev/null 2>&1 ;:)
+ }
+ # consume almost all fds to push the test to the fd limit #
+ integer max=$(ulimit --nofile)
+ (( max -= 6 ))
+ for ((i=20; i < max; i++))
+ do exec {i}>&1
+ done
+ for ((i=0; i < 20; i++))
+ do y=$(foo)
+ done
+ EOF
+) || {
+ err=${err%%$'\n'*}
+ err=${err#*:}
+ err=${err##[[:space:]]}
+ err_exit "nested command substitution with redirections failed -- $err"
+}
+
+exp=0
+$SHELL -c $'
+ function foobar
+ {
+ print "hello world"
+ }
+ [[ $(getopts \'[+?X\ffoobar\fX]\' v --man 2>&1) == *"Xhello worldX"* ]]
+ exit '$exp$'
+'
+got=$?
+[[ $got == $exp ]] || err_exit "getopts --man runtime callout with nonzero exit terminates shell -- expected '$exp', got '$got'"
+exp=ok
+got=$($SHELL -c $'
+ function foobar
+ {
+ print "hello world"
+ }
+ [[ $(getopts \'[+?X\ffoobar\fX]\' v --man 2>&1) == *"Xhello worldX"* ]]
+ print '$exp$'
+')
+[[ $got == $exp ]] || err_exit "getopts --man runtime callout with nonzero exit terminates shell -- expected '$exp', got '$got'"
+
+# command substitution variations #
+set -- \
+ '$(' ')' \
+ '${ ' '; }' \
+ '$(ulimit -c 0; ' ')' \
+ '$( (' ') )' \
+ '${ (' '); }' \
+ '`' '`' \
+ '`(' ')`' \
+ '`ulimit -c 0; ' '`' \
+ # end of table #
+exp=ok
+testcase[1]='
+ if %sexpr "NOMATCH" : ".*Z" >/dev/null%s
+ then print error
+ else print ok
+ fi
+ exit %s
+'
+testcase[2]='
+ function bar
+ {
+ pipeout=%1$sprintf Ok | tr O o%2$s
+ print $pipeout
+ return 0
+ }
+ foo=%1$sbar%2$s || foo="exit status $?"
+ print $foo
+ exit %3$s
+'
+while (( $# >= 2 ))
+do for ((TEST=1; TEST<=${#testcase[@]}; TEST++))
+ do body=${testcase[TEST]}
+ for code in 0 2
+ do got=${ printf "$body" "$1" "$2" "$code" | $SHELL 2>&1 }
+ status=$?
+ if (( status != code ))
+ then err_exit "test $TEST '$1...$2 exit $code' failed -- exit status $status, expected $code"
+ elif [[ $got != $exp ]]
+ then err_exit "test $TEST '$1...$2 exit $code' failed -- got '$got', expected '$exp'"
+ fi
+ done
+ done
+ shift 2
+done
+
+# the next tests loop on all combinations of
+# { SUB CAT INS TST APP } X { file-sizes }
+# where the file size starts at 1Ki and doubles up to and including 1Mi
+#
+# the tests and timeouts are done in async subshells to prevent
+# the test harness from hanging
+
+SUB=(
+ ( BEG='$( ' END=' )' )
+ ( BEG='${ ' END='; }' )
+)
+CAT=( cat $bincat )
+INS=( "" "builtin cat; " "builtin -d cat $bincat; " ": > /dev/null; " )
+APP=( "" "; :" )
+TST=(
+ ( CMD='print foo | $cat' EXP=3 )
+ ( CMD='$cat < $tmp/lin' )
+ ( CMD='cat $tmp/lin | $cat' )
+ ( CMD='read v < $tmp/buf; print $v' LIM=4*1024 )
+ ( CMD='cat $tmp/buf | read v; print $v' LIM=4*1024 )
+)
+
+if cat /dev/fd/3 3</dev/null >/dev/null 2>&1 || whence mkfifo > /dev/null
+then T=${#TST[@]}
+ TST[T].CMD='$cat <(print foo)'
+ TST[T].EXP=3
+fi
+
+# prime the two data files to 512 bytes each
+# $tmp/lin has newlines every 16 bytes and $tmp/buf has no newlines
+# the outer loop doubles the file size at top
+
+buf=$'1234567890abcdef'
+lin=$'\n1234567890abcde'
+for ((i=0; i<5; i++))
+do buf=$buf$buf
+ lin=$lin$lin
+done
+print -n "$buf" > $tmp/buf
+print -n "$lin" > $tmp/lin
+
+unset SKIP
+for ((n=1024; n<=1024*1024; n*=2))
+do cat $tmp/buf $tmp/buf > $tmp/tmp
+ mv $tmp/tmp $tmp/buf
+ cat $tmp/lin $tmp/lin > $tmp/tmp
+ mv $tmp/tmp $tmp/lin
+ for ((S=0; S<${#SUB[@]}; S++))
+ do for ((C=0; C<${#CAT[@]}; C++))
+ do cat=${CAT[C]}
+ for ((I=0; I<${#INS[@]}; I++))
+ do for ((A=0; A<${#APP[@]}; A++))
+ do for ((T=0; T<${#TST[@]}; T++))
+ do #undent...#
+
+ if [[ ! ${SKIP[S][C][I][A][T]} ]]
+ then eval "{ x=${SUB[S].BEG}${INS[I]}${TST[T].CMD}${APP[A]}${SUB[S].END}; print \${#x}; } >\$tmp/out &"
+ m=$!
+ { sleep 4; kill -9 $m; } &
+ k=$!
+ wait $m
+ h=$?
+ kill -9 $k
+ wait $k
+ got=$(<$tmp/out)
+ if [[ ! $got ]] && (( h ))
+ then got=HUNG
+ fi
+ if [[ ${TST[T].EXP} ]]
+ then exp=${TST[T].EXP}
+ else exp=$n
+ fi
+ if [[ $got != $exp ]]
+ then # on failure skip similar tests on larger files sizes #
+ SKIP[S][C][I][A][T]=1
+ siz=$(printf $'%#i' $exp)
+ cmd=${TST[T].CMD//\$cat/$cat}
+ cmd=${cmd//\$tmp\/buf/$siz.buf}
+ cmd=${cmd//\$tmp\/lin/$siz.lin}
+ err_exit "'x=${SUB[S].BEG}${INS[I]}${cmd}${APP[A]}${SUB[S].END} && print \${#x}' failed -- expected '$exp', got '$got'"
+ elif [[ ${TST[T].EXP} ]] || (( TST[T].LIM >= n ))
+ then SKIP[S][C][I][A][T]=1
+ fi
+ fi
+
+ #...indent#
+ done
+ done
+ done
+ done
+ done
+done
+
+# specifics -- there's more?
+
+{
+ cmd='{ exec 5>/dev/null; print "$(eval ls -d . 2>&1 1>&5)"; } >$tmp/out &'
+ eval $cmd
+ m=$!
+ { sleep 4; kill -9 $m; } &
+ k=$!
+ wait $m
+ h=$?
+ kill -9 $k
+ wait $k
+ got=$(<$tmp/out)
+} 2>/dev/null
+exp=''
+if [[ ! $got ]] && (( h ))
+then got=HUNG
+fi
+if [[ $got != $exp ]]
+then err_exit "eval '$cmd' failed -- expected '$exp', got '$got'"
+fi
+
+float t1=$SECONDS
+sleep=$(whence -p sleep)
+if [[ $sleep ]]
+then
+ $SHELL -c "( $sleep 5 </dev/null >/dev/null 2>&1 & );exit 0" | cat
+ (( (SECONDS-t1) > 4 )) && err_exit '/bin/sleep& in subshell hanging'
+ ((t1=SECONDS))
+fi
+$SHELL -c '( sleep 5 </dev/null >/dev/null 2>&1 & );exit 0' | cat
+(( (SECONDS-t1) > 4 )) && err_exit 'sleep& in subshell hanging'
+
+exp=HOME=$HOME
+( HOME=/bin/sh )
+got=$(env | grep ^HOME=)
+[[ $got == "$exp" ]] || err_exit "( HOME=/bin/sh ) cleanup failed -- expected '$exp', got '$got'"
+
+cmd='echo $((case x in x)echo ok;esac);:)'
+exp=ok
+got=$($SHELL -c "$cmd" 2>&1)
+[[ $got == "$exp" ]] || err_exit "'$cmd' failed -- expected '$exp', got '$got'"
+
+cmd='eval "for i in 1 2; do eval /bin/echo x; done"'
+exp=$'x\nx'
+got=$($SHELL -c "$cmd")
+if [[ $got != "$exp" ]]
+then EXP=$(printf %q "$exp")
+ GOT=$(printf %q "$got")
+ err_exit "'$cmd' failed -- expected $EXP, got $GOT"
+fi
+
+(
+$SHELL -c 'sleep 20 & pid=$!; { x=$( ( seq 60000 ) );kill -9 $pid;}&;wait $pid'
+) 2> /dev/null
+(( $? )) || err_exit 'nested command substitution with large output hangs'
+
+(.sh.foo=foobar)
+[[ ${.sh.foo} == foobar ]] && err_exit '.sh subvariables in subshells remain set'
+[[ $($SHELL -c 'print 1 | : "$(/bin/cat <(/bin/cat))"') ]] && err_exit 'process substitution not working correctly in subshells'
+
+# config hang bug
+integer i
+for ((i=1; i < 1000; i++))
+do typeset foo$i=$i
+done
+{
+ : $( (ac_space=' '; set | grep ac_space) 2>&1)
+} < /dev/null | cat > /dev/null &
+sleep 1.5
+if kill -KILL $! 2> /dev/null
+then err_exit 'process timed out with hung comsub'
+fi
+wait $! 2> /dev/null
+(( $? > 128 )) && err_exit 'incorrect exit status with comsub'
+
+$SHELL 2> /dev/null -c '[[ ${ print foo },${ print bar } == foo,bar ]]' || err_exit '${ print foo },${ print bar } not working'
+$SHELL 2> /dev/null -c '[[ ${ print foo; },${ print bar } == foo,bar ]]' || err_exit '${ print foo; },${ print bar } not working'
+
+src=$'true 2>&1\n: $(true | true)\n: $(true | true)\n: $(true | true)\n'$(whence -p true)
+exp=ok
+got=$( $SHELL -c "(eval '$src'); echo $exp" )
+[[ $got == "$exp" ]] || err_exit 'subshell eval of pipeline clobbers stdout'
+
+x=$( { time $SHELL -c date >| /dev/null;} 2>&1)
+[[ $x == *real*user*sys* ]] || err_exit 'time { ...;} 2>&1 in $(...) fails'
+
+x=$($SHELL -c '( function fx { export X=123; } ; fx; ); echo $X')
+[[ $x == 123 ]] && err_exit 'global variables set from with functions inside a
+subshell can leave side effects in parent shell'
+
+date=$(whence -p date)
+err() { return $1; }
+( err 12 ) & pid=$!
+: $( $date)
+wait $pid
+[[ $? == 12 ]] || err_exit 'exit status from subshells not being preserved'
+
+if cat /dev/fd/3 3</dev/null >/dev/null 2>&1 || whence mkfifo > /dev/null
+then x="$(sed 's/^/Hello /' <(print "Fred" | sort))"
+ [[ $x == 'Hello Fred' ]] || err_exit "process substitution of pipeline in command substitution not working"
+fi
+
+{
+$SHELL <<- \EOF
+ function foo
+ {
+ integer i
+ print -u2 foobar
+ for ((i=0; i < 8000; i++))
+ do print abcdefghijk
+ done
+ print -u2 done
+ }
+ out=$(eval "foo | cat" 2>&1)
+ (( ${#out} == 96011 )) || err_exit "\${#out} is ${#out} should be 96011"
+EOF
+} & pid=$!
+$SHELL -c "{ sleep 2 && kill $pid ;}" 2> /dev/null
+(( $? == 0 )) && err_exit 'process has hung'
+
+{
+x=$( $SHELL <<- \EOF
+ function func1 { typeset IFS; : $(func2); print END ;}
+ function func2 { IFS="BAR"; }
+ func1
+ func1
+EOF
+)
+} 2> /dev/null
+[[ $x == $'END\nEND' ]] || err_exit 'bug in save/restore of IFS in subshell'
+
+true=$(whence -p true)
+date=$(whence -p date)
+tmpf=$tmp/foo
+function fun1
+{
+ $true
+ cd - >/dev/null 2>&1
+ print -u2 -- "$($date) SUCCESS"
+}
+
+print -n $(fun1 2> $tmpf)
+[[ $(< $tmpf) == *SUCCESS ]] || err_exit 'standard error output lost with command substitution'
+
+
+tmpfile=$tmp/foo
+cat > $tmpfile <<-\EOF
+ $SHELL -c 'function g { IFS= ;};function f { typeset IFS;(g);: $V;};f;f'
+ EOF
+$SHELL 2> /dev/null "$tmpfile" || err_exit 'IFS in subshell causes core dump'
+
+unset i
+if [[ -d /dev/fd ]]
+then integer i
+ for ((i=11; i < 29; i++))
+ do if ! [[ -r /dev/fd/$i || -w /dev/fd/$i ]]
+ then a=$($SHELL -c "[[ -r /dev/fd/$i || -w /dev/fd/$i ]]")
+ (( $? )) || err_exit "file descriptor $i not close on exec"
+ fi
+ done
+fi
+
+exit $((Errors<125?Errors:125))