1######################################################################## 2# # 3# This software is part of the ast package # 4# Copyright (c) 1982-2009 AT&T Intellectual Property # 5# and is licensed under the # 6# Common Public License, Version 1.0 # 7# by AT&T Intellectual Property # 8# # 9# A copy of the License is available at # 10# http://www.opensource.org/licenses/cpl1.0.txt # 11# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # 12# # 13# Information and Software Systems Research # 14# AT&T Research # 15# Florham Park NJ # 16# # 17# David Korn <dgk@research.att.com> # 18# # 19######################################################################## 20function err_exit 21{ 22 print -u$Error_fd -n "\t" 23 print -u$Error_fd -r ${Command}[$1]: "${@:2}" 24 (( Errors+=1 )) 25} 26alias err_exit='err_exit $LINENO' 27 28Command=${0##*/} 29integer Errors=0 Error_fd=2 30 31tmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; } 32trap "cd /; rm -rf $tmp" EXIT 33 34bincat=$(PATH=$(getconf PATH) whence -p cat) 35 36z=() 37z.foo=( [one]=hello [two]=(x=3 y=4) [three]=hi) 38z.bar[0]=hello 39z.bar[2]=world 40z.bar[1]=(x=4 y=5) 41val='( 42 typeset -a bar=( 43 [0]=hello 44 [2]=world 45 [1]=( 46 x=4 47 y=5 48 ) 49 ) 50 typeset -A foo=( 51 [one]=hello 52 [three]=hi 53 [two]=( 54 x=3 55 y=4 56 ) 57 ) 58)' 59[[ $z == "$val" ]] || err_exit 'compound variable with mixed arrays not working' 60z.bar[1]=yesyes 61[[ ${z.bar[1]} == yesyes ]] || err_exit 'reassign of index array compound variable fails' 62z.bar[1]=(x=12 y=5) 63[[ ${z.bar[1]} == $'(\n\tx=12\n\ty=5\n)' ]] || err_exit 'reassign array simple to compound variable fails' 64eval val="$z" 65( 66 z.foo[three]=good 67 [[ ${z.foo[three]} == good ]] || err_exit 'associative array assignment in subshell not working' 68) 69[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment' 70eval val="$z" 71( 72false 73 z.foo[two]=ok 74 [[ ${z.foo[two]} == ok ]] || err_exit 'associative array assignment to compound variable in subshell not working' 75 z.bar[1]=yes 76 [[ ${z.bar[1]} == yes ]] || err_exit 'index array assignment to compound variable in subshell not working' 77) 78[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment' 79 80x=( 81 foo=( qqq=abc rrr=def) 82 bar=( zzz=no rst=fed) 83) 84eval val="$x" 85( 86 unset x.foo 87 [[ ${x.foo.qqq} ]] && err_exit 'x.foo.qqq should be unset' 88 x.foo=good 89 [[ ${x.foo} == good ]] || err_exit 'x.foo should be good' 90) 91[[ $x == "$val" ]] || err_exit 'compound variable changes after unset leaves' 92unset l 93( 94 l=( a=1 b="BE" ) 95) 96[[ ${l+foo} != foo ]] || err_exit 'l should be unset' 97 98Error_fd=9 99eval "exec $Error_fd>&2 2>/dev/null" 100 101TEST_notfound=notfound 102while whence $TEST_notfound >/dev/null 2>&1 103do TEST_notfound=notfound-$RANDOM 104done 105 106integer BS=1024 nb=64 ss=60 bs no 107for bs in $BS 1 108do $SHELL -c ' 109 { 110 sleep '$ss' 111 kill -KILL $$ 112 } & 113 set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs') 114 print ${#1} 115 kill $! 116 ' > $tmp/sub 2>/dev/null 117 no=$(<$tmp/sub) 118 (( no == (BS * nb) )) || err_exit "shell hangs on command substitution output size >= $BS*$nb with write size $bs -- expected $((BS*nb)), got ${no:-0}" 119done 120# this time with redirection on the trailing command 121for bs in $BS 1 122do $SHELL -c ' 123 { 124 sleep 2 125 sleep '$ss' 126 kill -KILL $$ 127 } & 128 set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs' 2>/dev/null) 129 print ${#1} 130 kill $! 131 ' > $tmp/sub 2>/dev/null 132 no=$(<$tmp/sub) 133 (( 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}" 134done 135 136# exercise command substitutuion trailing newline logic w.r.t. pipe vs. tmp file io 137 138set -- \ 139 'post-line print' \ 140 '$TEST_unset; ($TEST_fork; print 1); print' \ 141 1 \ 142 'pre-line print' \ 143 '$TEST_unset; ($TEST_fork; print); print 1' \ 144 $'\n1' \ 145 'multiple pre-line print' \ 146 '$TEST_unset; ($TEST_fork; print); print; ($TEST_fork; print 1); print' \ 147 $'\n\n1' \ 148 'multiple post-line print' \ 149 '$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print); print' \ 150 1 \ 151 'intermediate print' \ 152 '$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print 2); print' \ 153 $'1\n\n2' \ 154 'simple variable' \ 155 '$TEST_unset; ($TEST_fork; l=2; print "$l"); print $l' \ 156 2 \ 157 'compound variable' \ 158 '$TEST_unset; ($TEST_fork; l=(a=2 b="BE"); print "$l"); print $l' \ 159 $'(\n\ta=2\n\tb=BE\n)' \ 160 161export TEST_fork TEST_unset 162 163while (( $# >= 3 )) 164do txt=$1 165 cmd=$2 166 exp=$3 167 shift 3 168 for TEST_unset in '' 'unset var' 169 do for TEST_fork in '' 'ulimit -c 0' 170 do for TEST_shell in "eval" "$SHELL -c" 171 do if ! got=$($TEST_shell "$cmd") 172 then err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt print failed" 173 elif [[ "$got" != "$exp" ]] 174 then EXP=$(printf %q "$exp") 175 GOT=$(printf %q "$got") 176 err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt command substitution failed -- expected $EXP, got $GOT" 177 fi 178 done 179 done 180 done 181done 182 183r=$( ($SHELL -c ' 184 { 185 sleep 32 186 kill -KILL $$ 187 } & 188 for v in $(set | sed "s/=.*//") 189 do command unset $v 190 done 191 typeset -Z5 I 192 for ((I = 0; I < 1024; I++)) 193 do eval A$I=1234567890 194 done 195 a=$(set 2>&1) 196 print ok 197 kill -KILL $! 198') 2>/dev/null) 199[[ $r == ok ]] || err_exit "large subshell command substitution hangs" 200 201for TEST_command in '' $TEST_notfound 202do for TEST_exec in '' 'exec' 203 do for TEST_fork in '' 'ulimit -c 0;' 204 do for TEST_redirect in '' '>/dev/null' 205 do for TEST_substitute in '' ': $' 206 do 207 208 TEST_test="$TEST_substitute($TEST_fork $TEST_exec $TEST_command $TEST_redirect)" 209 [[ $TEST_test == '('*([[:space:]])')' ]] && continue 210 r=$($SHELL -c ' 211 { 212 sleep 2 213 kill -KILL $$ 214 } & 215 '"$TEST_test"' 216 kill $! 217 print ok 218 ') 219 [[ $r == ok ]] || err_exit "shell hangs on $TEST_test" 220 221 done 222 done 223 done 224 done 225done 226 227$SHELL -c '( autoload xxxxx);print -n' || err_exit 'autoloaded functions in subshells can cause failure' 228foo=$($SHELL <<- ++EOF++ 229 (trap 'print bar' EXIT;print -n foo) 230 ++EOF++ 231) 232[[ $foo == foobar ]] || err_exit 'trap on exit when last commands is subshell is not triggered' 233 234err=$( 235 $SHELL 2>&1 <<- \EOF 236 date=$(whence -p date) 237 function foo 238 { 239 x=$( $date > /dev/null 2>&1 ;:) 240 } 241 # consume almost all fds to push the test to the fd limit # 242 integer max=$(ulimit --nofile) 243 (( max -= 6 )) 244 for ((i=20; i < max; i++)) 245 do exec {i}>&1 246 done 247 for ((i=0; i < 20; i++)) 248 do y=$(foo) 249 done 250 EOF 251) || { 252 err=${err%%$'\n'*} 253 err=${err#*:} 254 err=${err##[[:space:]]} 255 err_exit "nested command substitution with redirections failed -- $err" 256} 257 258exp=0 259$SHELL -c $' 260 function foobar 261 { 262 print "hello world" 263 } 264 [[ $(getopts \'[+?X\ffoobar\fX]\' v --man 2>&1) == *"Xhello worldX"* ]] 265 exit '$exp$' 266' 267got=$? 268[[ $got == $exp ]] || err_exit "getopts --man runtime callout with nonzero exit terminates shell -- expected '$exp', got '$got'" 269exp=ok 270got=$($SHELL -c $' 271 function foobar 272 { 273 print "hello world" 274 } 275 [[ $(getopts \'[+?X\ffoobar\fX]\' v --man 2>&1) == *"Xhello worldX"* ]] 276 print '$exp$' 277') 278[[ $got == $exp ]] || err_exit "getopts --man runtime callout with nonzero exit terminates shell -- expected '$exp', got '$got'" 279 280# command substitution variations # 281set -- \ 282 '$(' ')' \ 283 '${ ' '; }' \ 284 '$(ulimit -c 0; ' ')' \ 285 '$( (' ') )' \ 286 '${ (' '); }' \ 287 '`' '`' \ 288 '`(' ')`' \ 289 '`ulimit -c 0; ' '`' \ 290 # end of table # 291exp=ok 292testcase[1]=' 293 if %sexpr "NOMATCH" : ".*Z" >/dev/null%s 294 then print error 295 else print ok 296 fi 297 exit %s 298' 299testcase[2]=' 300 function bar 301 { 302 pipeout=%1$sprintf Ok | tr O o%2$s 303 print $pipeout 304 return 0 305 } 306 foo=%1$sbar%2$s || foo="exit status $?" 307 print $foo 308 exit %3$s 309' 310while (( $# >= 2 )) 311do for ((TEST=1; TEST<=${#testcase[@]}; TEST++)) 312 do body=${testcase[TEST]} 313 for code in 0 2 314 do got=${ printf "$body" "$1" "$2" "$code" | $SHELL 2>&1 } 315 status=$? 316 if (( status != code )) 317 then err_exit "test $TEST '$1...$2 exit $code' failed -- exit status $status, expected $code" 318 elif [[ $got != $exp ]] 319 then err_exit "test $TEST '$1...$2 exit $code' failed -- got '$got', expected '$exp'" 320 fi 321 done 322 done 323 shift 2 324done 325 326# the next tests loop on all combinations of 327# { SUB CAT INS TST APP } X { file-sizes } 328# where the file size starts at 1Ki and doubles up to and including 1Mi 329# 330# the tests and timeouts are done in async subshells to prevent 331# the test harness from hanging 332 333SUB=( 334 ( BEG='$( ' END=' )' ) 335 ( BEG='${ ' END='; }' ) 336) 337CAT=( cat $bincat ) 338INS=( "" "builtin cat; " "builtin -d cat $bincat; " ": > /dev/null; " ) 339APP=( "" "; :" ) 340TST=( 341 ( CMD='print foo | $cat' EXP=3 ) 342 ( CMD='$cat < $tmp/lin' ) 343 ( CMD='cat $tmp/lin | $cat' ) 344 ( CMD='read v < $tmp/buf; print $v' LIM=4*1024 ) 345 ( CMD='cat $tmp/buf | read v; print $v' LIM=4*1024 ) 346) 347 348command exec 3<> /dev/null 349if cat /dev/fd/3 >/dev/null 2>&1 350then T=${#TST[@]} 351 TST[T].CMD='$cat <(print foo)' 352 TST[T].EXP=3 353fi 354 355# prime the two data files to 512 bytes each 356# $tmp/lin has newlines every 16 bytes and $tmp/buf has no newlines 357# the outer loop doubles the file size at top 358 359buf=$'1234567890abcdef' 360lin=$'\n1234567890abcde' 361for ((i=0; i<5; i++)) 362do buf=$buf$buf 363 lin=$lin$lin 364done 365print -n "$buf" > $tmp/buf 366print -n "$lin" > $tmp/lin 367 368unset SKIP 369for ((n=1024; n<=1024*1024; n*=2)) 370do cat $tmp/buf $tmp/buf > $tmp/tmp 371 mv $tmp/tmp $tmp/buf 372 cat $tmp/lin $tmp/lin > $tmp/tmp 373 mv $tmp/tmp $tmp/lin 374 for ((S=0; S<${#SUB[@]}; S++)) 375 do for ((C=0; C<${#CAT[@]}; C++)) 376 do cat=${CAT[C]} 377 for ((I=0; I<${#INS[@]}; I++)) 378 do for ((A=0; A<${#APP[@]}; A++)) 379 do for ((T=0; T<${#TST[@]}; T++)) 380 do #undent...# 381 382 if [[ ! ${SKIP[S][C][I][A][T]} ]] 383 then eval "{ x=${SUB[S].BEG}${INS[I]}${TST[T].CMD}${APP[A]}${SUB[S].END}; print \${#x}; } >\$tmp/out &" 384 m=$! 385 { sleep 4; kill -9 $m; } & 386 k=$! 387 wait $m 388 h=$? 389 kill -9 $k 390 wait $k 391 got=$(<$tmp/out) 392 if [[ ! $got ]] && (( h )) 393 then got=HUNG 394 fi 395 if [[ ${TST[T].EXP} ]] 396 then exp=${TST[T].EXP} 397 else exp=$n 398 fi 399 if [[ $got != $exp ]] 400 then # on failure skip similar tests on larger files sizes # 401 SKIP[S][C][I][A][T]=1 402 siz=$(printf $'%#i' $exp) 403 cmd=${TST[T].CMD//\$cat/$cat} 404 cmd=${cmd//\$tmp\/buf/$siz.buf} 405 cmd=${cmd//\$tmp\/lin/$siz.lin} 406 err_exit "'x=${SUB[S].BEG}${INS[I]}${cmd}${APP[A]}${SUB[S].END} && print \${#x}' failed -- expected '$exp', got '$got'" 407 elif [[ ${TST[T].EXP} ]] || (( TST[T].LIM >= n )) 408 then SKIP[S][C][I][A][T]=1 409 fi 410 fi 411 412 #...indent# 413 done 414 done 415 done 416 done 417 done 418done 419 420# specifics -- there's more? 421 422{ 423 cmd='{ exec 5>/dev/null; print "$(eval ls -d . 2>&1 1>&5)"; } >$tmp/out &' 424 eval $cmd 425 m=$! 426 { sleep 4; kill -9 $m; } & 427 k=$! 428 wait $m 429 h=$? 430 kill -9 $k 431 wait $k 432 got=$(<$tmp/out) 433} 2>/dev/null 434exp='' 435if [[ ! $got ]] && (( h )) 436then got=HUNG 437fi 438if [[ $got != $exp ]] 439then err_exit "eval '$cmd' failed -- expected '$exp', got '$got'" 440fi 441 442float t1=$SECONDS 443sleep=$(whence -p sleep) 444if [[ $sleep ]] 445then 446 $SHELL -c "( $sleep 5 </dev/null >/dev/null 2>&1 & );exit 0" | cat 447 (( (SECONDS-t1) > 4 )) && err_exit '/bin/sleep& in subshell hanging' 448 ((t1=SECONDS)) 449fi 450$SHELL -c '( sleep 5 </dev/null >/dev/null 2>&1 & );exit 0' | cat 451(( (SECONDS-t1) > 4 )) && err_exit 'sleep& in subshell hanging' 452 453exit $Errors 454