1# Tests for both trap builtin and TRAP* functions. 2 3%prep 4 5 setopt localtraps 6 mkdir traps.tmp && cd traps.tmp 7 8%test 9 10 fn1() { 11 trap 'print EXIT1' EXIT 12 fn2() { trap 'print EXIT2' EXIT; } 13 fn2 14 } 15 fn1 160:Nested `trap ... EXIT' 17>EXIT2 18>EXIT1 19 20 fn1() { 21 TRAPEXIT() { print EXIT1; } 22 fn2() { TRAPEXIT() { print EXIT2; }; } 23 fn2 24 } 25 fn1 260: Nested TRAPEXIT 27>EXIT2 28>EXIT1 29 30 fn1() { 31 trap 'print EXIT1' EXIT 32 fn2() { trap - EXIT; } 33 fn2 34 } 35 fn1 360:Nested `trap - EXIT' on `trap ... EXIT' 37>EXIT1 38 39 fn1() { 40 TRAPEXIT() { print EXIT1; } 41 fn2() { trap - EXIT; } 42 fn2 43 } 44 fn1 450:Nested `trap - EXIT' on `TRAPEXIT' 46>EXIT1 47 48# We can't test an EXIT trap for the shell as a whole, because 49# we're inside a function scope which we don't leave when the 50# subshell exits. Not sure if that's the correct behaviour, but 51# it's sort of consistent. 52 ( fn1() { trap 'print Function 1 going' EXIT; exit; print Not reached; } 53 fn2() { trap 'print Function 2 going' EXIT; fn1; print Not reached; } 54 fn2 55 ) 560:EXIT traps on functions when exiting from function 57>Function 1 going 58>Function 2 going 59 60# $ZTST_exe is relative to the parent directory. 61# We ought to fix this in ztst.zsh... 62 (cd .. 63 $ZTST_exe -fc 'TRAPEXIT() { print Exited.; }') 640:EXIT traps on a script 65>Exited. 66 67 fn1() { 68 trap - 69 trap 70 trap 'print INT1' INT 71 fn2() { trap 'print INT2' INT; trap; } 72 trap 73 fn2 74 trap 75 } 76 fn1 770: Nested `trap ... INT', not triggered 78>trap -- 'print INT1' INT 79>trap -- 'print INT2' INT 80>trap -- 'print INT1' INT 81 82 fn1() { 83 trap - 84 trap 85 TRAPINT() { print INT1; } 86 fn2() { TRAPINT() { print INT2; }; trap; } 87 trap 88 fn2 89 trap 90 } 91 fn1 920: Nested TRAPINT, not triggered 93>TRAPINT () { 94> print INT1 95>} 96>TRAPINT () { 97> print INT2 98>} 99>TRAPINT () { 100> print INT1 101>} 102 103 fn1() { 104 trap - 105 trap 'print INT1' INT 106 fn2() { trap - INT; trap; } 107 trap 108 fn2 109 trap 110 } 111 fn1 1120: Nested `trap - INT' on untriggered `trap ... INT' 113>trap -- 'print INT1' INT 114>trap -- 'print INT1' INT 115 116# Testing the triggering of traps here is very unpleasant. 117# The delays are attempts to avoid race conditions, though there is 118# no guarantee that they will work. Note the subtlety that the 119# `sleep' in the function which receives the trap does *not* get the 120# signal, only the parent shell, which is waiting for a SIGCHILD. 121# (At least, that's what I think is happening.) Thus we have to wait at 122# least the full two seconds to make sure we have got the output from the 123# execution of the trap. 124 125 print -u $ZTST_fd 'This test takes at least three seconds...' 126 fn1() { 127 trap 'print TERM1' TERM 128 fn2() { trap 'print TERM2; return 1' TERM; sleep 2; } 129 fn2 & 130 sleep 1 131 kill -TERM $! 132 sleep 2 133 } 134 fn1 1350: Nested `trap ... TERM', triggered on inner loop 136>TERM2 137 138 print -u $ZTST_fd 'This test, too, takes at least three seconds...' 139 fn1() { 140 trap 'print TERM1; return 1' TERM 141 fn2() { trap 'print TERM2; return 1' TERM; } 142 fn2 143 sleep 2 144 } 145 fn1 & 146 sleep 1 147 kill -TERM $! 148 sleep 2 1490: Nested `trap ... TERM', triggered on outer loop 150>TERM1 151 152 TRAPZERR() { print error activated; } 153 fn() { print start of fn; false; print end of fn; } 154 fn 155 fn() { 156 setopt localoptions localtraps 157 unfunction TRAPZERR 158 print start of fn 159 false 160 print end of fn 161 } 162 fn 163 unfunction TRAPZERR 164 print finish 1650: basic localtraps handling 166>start of fn 167>error activated 168>end of fn 169>start of fn 170>end of fn 171>finish 172 173 TRAPZERR() { print 'ERR-or!'; } 174 f() { print f; false; } 175 t() { print t; } 176 f 177 f && t 178 t && f && true 179 t && f 180 testunset() { 181 setopt localtraps 182 unset -f TRAPZERR 183 print testunset 184 false 185 true 186 } 187 testunset 188 f 189 print status $? 190 unfunction TRAPZERR 1910: more sophisticated error trapping 192>f 193>ERR-or! 194>f 195>t 196>f 197>t 198>f 199>ERR-or! 200>testunset 201>f 202>ERR-or! 203>status 1 204 205 f() { 206 setopt localtraps 207 TRAPWINCH() { print "Window changed. That wrecked the test."; } 208 } 209 f 210 f 211 functions TRAPWINCH 2121:Unsetting ordinary traps with localtraps. 213 214# 215# Returns from within traps are a perennial problem. 216# The following two apply to returns in and around standard 217# ksh-style traps. The intention is that a return value from 218# within the function is preserved (i.e. statuses set by the trap 219# are ignored) unless the trap explicitly executes `return', which makes 220# it return from the enclosing function. 221# 222 fn() { trap 'true' EXIT; return 1; } 223 fn 2241: ksh-style EXIT traps preserve return value 225 226 inner() { trap 'return 3' EXIT; return 2; } 227 outer() { inner; return 1; } 228 outer 2293: ksh-style EXIT traps can force return status of enclosing function 230 231# Autoloaded traps are horrid, but unfortunately people expect 232# them to work if we support them. 233 echo "print Running exit trap" >TRAPEXIT 234 ${${ZTST_exe##[^/]*}:-$ZTST_testdir/$ZTST_exe} -fc ' 235 fpath=(. $fpath) 236 autoload TRAPEXIT 237 print "Exiting, attempt 1" 238 exit 239 print "What?" 240 ' 241 ${${ZTST_exe##[^/]*}:-$ZTST_testdir/$ZTST_exe} -fc ' 242 fpath=(. $fpath) 243 autoload TRAPEXIT; 244 fn() { print Some function } 245 fn 246 print "Exiting, attempt 2" 247 exit 248 ' 2490: autoloaded TRAPEXIT (exit status > 128 indicates an old bug is back) 250>Exiting, attempt 1 251>Running exit trap 252>Some function 253>Exiting, attempt 2 254>Running exit trap 255 256 print -u $ZTST_fd Another test that takes three seconds 257 gotsig=0 258 signal_handler() { 259 echo "parent received signal" 260 gotsig=1 261 } 262 child() { 263 sleep 1 264 echo "child sending signal" 265 kill -15 $parentpid 266 sleep 2 267 echo "child exiting" 268 exit 33 269 } 270 parentpid=$$ 271 child & 272 childpid=$! 273 trap signal_handler 15 274 echo "parent waiting" 275 wait $childpid 276 cstatus=$? 277 echo "wait #1 finished, gotsig=$gotsig, status=$cstatus" 278 gotsig=0 279 wait $childpid 280 cstatus=$? 281 echo "wait #2 finished, gotsig=$gotsig, status=$cstatus" 2820:waiting for trapped signal 283>parent waiting 284>child sending signal 285>parent received signal 286>wait #1 finished, gotsig=1, status=143 287>child exiting 288>wait #2 finished, gotsig=0, status=33 289 290 fn1() { 291 setopt errexit 292 trap 'echo error1' ZERR 293 false 294 print Shouldn\'t get here 1a 295 } 296 fn2() { 297 setopt errexit 298 trap 'echo error2' ZERR 299 return 1 300 print Shouldn\'t get here 2a 301 } 302 fn3() { 303 setopt errexit 304 TRAPZERR() { echo error3; } 305 false 306 print Shouldn\'t get here 3a 307 } 308 fn4() { 309 setopt errexit 310 TRAPZERR() { echo error4; } 311 return 1 312 print Shouldn\'t get here 4a 313 } 314 (fn1; print Shouldn\'t get here 1b) 315 (fn2; print Shouldn\'t get here 2b) 316 (fn3; print Shouldn\'t get here 3b) 317 (fn4; print Shouldn\'t get here 4b) 3181: Combination of ERR_EXIT and ZERR trap 319>error1 320>error2 321>error3 322>error4 323 324 fn1() { TRAPZERR() { print trap; return 42; }; false; print Broken; } 325 (fn1) 326 print Working $? 3270: Force return of containing function from TRAPZERR. 328>trap 329>Working 42 330 331 fn2() { trap 'print trap; return 42' ZERR; false; print Broken } 332 (fn2) 333 print Working $? 3340: Return with non-zero status triggered from within trap '...' ZERR. 335>trap 336>Working 42 337 338 fn3() { TRAPZERR() { print trap; return 0; }; false; print OK this time; } 339 (fn3) 340 print Working $? 3410: Normal return from TRAPZERR. 342>trap 343>OK this time 344>Working 0 345 346 fn4() { trap 'print trap; return 0' ZERR; false; print Broken; } 347 (fn4) 348 print Working $? 3490: Return with zero status triggered from within trap '...' ZERR. 350>trap 351>Working 0 352 353 { trap 'echo This subshell is exiting' EXIT; } | cat 3540: EXIT trap set in current shell at left of pipeline 355>This subshell is exiting 356 357 ( trap 'echo This subshell is also exiting' EXIT; ) | cat 3580: EXIT trap set in subshell at left of pipeline 359>This subshell is also exiting 360 361 ( trap 'echo Should only appear once at the end' EXIT 362 ( : trap reset here ) | cat 363 : trap not reset but not part of shell command list | cat 364 echo nothing after this should appear $( : trap reset here too) 365 ) 3660: EXIT trap set in subshell reset in subsubshell 367>nothing after this should appear 368>Should only appear once at the end 369 370 echo $( trap 'echo command substitution exited' EXIT ) 3710: EXIT trap set in command substitution 372>command substitution exited 373 374 (cd ..; $ZTST_exe -fc 'setopt posixtraps; 375 TRAPEXIT() { print Exited; } 376 fn1() { trap; } 377 setopt localtraps # should be ignored by EXIT 378 fn2() { TRAPEXIT() { print No, really exited; } } 379 fn1 380 fn2 381 fn1') 3820:POSIX_TRAPS option 383>TRAPEXIT () { 384> print Exited 385>} 386>TRAPEXIT () { 387> print No, really exited 388>} 389>No, really exited 390 391 (set -e 392 printf "a\nb\n" | while read line 393 do 394 [[ $line = a* ]] || continue 395 ((ctr++)) 396 [[ $line = foo ]] 397 done 398 echo "ctr = $ctr" 399 ) 4001:ERREXIT in loop with simple commands 401 402%clean 403 404 rm -f TRAPEXIT 405