1# $NetBSD: t_option.sh,v 1.6 2017/11/16 19:41:41 kre Exp $ 2# 3# Copyright (c) 2016 The NetBSD Foundation, Inc. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 16# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25# POSSIBILITY OF SUCH DAMAGE. 26# 27# the implementation of "sh" to test 28: ${TEST_SH:="/bin/sh"} 29 30# The standard 31# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html 32# says: 33# ...[lots] 34 35test_option_on_off() 36{ 37 atf_require_prog tr 38 39 for opt 40 do 41 # t is needed, as inside $()` $- appears to lose 42 # the 'e' option if it happened to already be 43 # set. Must check if that is what should 44 # happen, but that is a different issue. 45 46 test -z "${opt}" && continue 47 48 # if we are playing with more that one option at a 49 # time, the code below requires that we start with no 50 # options set, or it will mis-diagnose the situation 51 CLEAR='' 52 test "${#opt}" -gt 1 && 53 CLEAR='xx="$-" && xx=$(echo "$xx" | tr -d cs) && test -n "$xx" && set +"$xx";' 54 55 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 56 "opt=${opt}"' 57 x() { 58 echo "ERROR: Unable to $1 option $2" >&2 59 exit 1 60 } 61 s() { 62 set -"$1" 63 t="$-" 64 x=$(echo "$t" | tr -d "$1") 65 test "$t" = "$x" && x set "$1" 66 return 0 67 } 68 c() { 69 set +"$1" 70 t="$-" 71 x=$(echo "$t" | tr -d "$1") 72 test "$t" != "$x" && x clear "$1" 73 return 0 74 } 75 '"${CLEAR}"' 76 77 # if we do not do this, -x tracing splatters stderr 78 # for some shells, -v does as well (is that correct?) 79 case "${opt}" in 80 (*[xXv]*) exec 2>/dev/null;; 81 esac 82 83 o="$-" 84 x=$(echo "$o" | tr -d "$opt") 85 86 if [ "$o" = "$x" ]; then # option was off 87 s "${opt}" 88 c "${opt}" 89 else 90 c "${opt}" 91 s "${opt}" 92 fi 93 ' 94 done 95} 96 97test_optional_on_off() 98{ 99 RET=0 100 OPTS= 101 for opt 102 do 103 test "${opt}" = n && continue 104 ${TEST_SH} -c "set -${opt}" 2>/dev/null && 105 OPTS="${OPTS} ${opt}" || RET=1 106 done 107 108 test -n "${OPTS}" && test_option_on_off ${OPTS} 109 110 return "${RET}" 111} 112 113atf_test_case set_a 114set_a_head() { 115 atf_set "descr" "Tests that 'set -a' turns on all var export " \ 116 "and that it behaves as defined by the standard" 117} 118set_a_body() { 119 atf_require_prog env 120 atf_require_prog grep 121 122 test_option_on_off a 123 124 # without -a, new variables should not be exported (so grep "fails") 125 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -ce \ 126 'unset VAR; set +a; VAR=value; env | grep "^VAR="' 127 128 # with -a, they should be 129 atf_check -s exit:0 -o match:VAR=value -e empty ${TEST_SH} -ce \ 130 'unset VAR; set -a; VAR=value; env | grep "^VAR="' 131} 132 133atf_test_case set_C 134set_C_head() { 135 atf_set "descr" "Tests that 'set -C' turns on no clobber mode " \ 136 "and that it behaves as defined by the standard" 137} 138set_C_body() { 139 atf_require_prog ls 140 141 test_option_on_off C 142 143 # Check that the environment to use for the tests is sane ... 144 # we assume current dir is a new tempory directory & is empty 145 146 test -z "$(ls)" || atf_skip "Test execution directory not clean" 147 test -c "/dev/null" || atf_skip "Problem with /dev/null" 148 149 echo Dummy_Content > Junk_File 150 echo Precious_Content > Important_File 151 152 # Check that we can redirect onto file when -C is not set 153 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 154 ' 155 D=$(ls -l Junk_File) || exit 1 156 set +C 157 echo "Overwrite it now" > Junk_File 158 A=$(ls -l Junk_File) || exit 1 159 test "${A}" != "${D}" 160 ' 161 162 # Check that we cannot redirect onto file when -C is set 163 atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \ 164 ' 165 D=$(ls -l Important_File) || exit 1 166 set -C 167 echo "Fail to Overwrite it now" > Important_File 168 A=$(ls -l Important_File) || exit 1 169 test "${A}" = "${D}" 170 ' 171 172 # Check that we can append to file, even when -C is set 173 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 174 ' 175 D=$(ls -l Junk_File) || exit 1 176 set -C 177 echo "Append to it now" >> Junk_File 178 A=$(ls -l Junk_File) || exit 1 179 test "${A}" != "${D}" 180 ' 181 182 # Check that we abort on attempt to redirect onto file when -Ce is set 183 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ 184 ' 185 set -Ce 186 echo "Fail to Overwrite it now" > Important_File 187 echo "Should not reach this point" 188 ' 189 190 # Last check that we can override -C for when we really need to 191 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 192 ' 193 D=$(ls -l Junk_File) || exit 1 194 set -C 195 echo "Change the poor bugger again" >| Junk_File 196 A=$(ls -l Junk_File) || exit 1 197 test "${A}" != "${D}" 198 ' 199} 200 201atf_test_case set_e 202set_e_head() { 203 atf_set "descr" "Tests that 'set -e' turns on error detection " \ 204 "and that a simple case behaves as defined by the standard" 205} 206set_e_body() { 207 test_option_on_off e 208 209 # Check that -e does nothing if no commands fail 210 atf_check -s exit:0 -o match:I_am_OK -e empty \ 211 ${TEST_SH} -c \ 212 'false; printf "%s" I_am; set -e; true; printf "%s\n" _OK' 213 214 # and that it (silently, but with exit status) aborts if cmd fails 215 atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \ 216 ${TEST_SH} -c \ 217 'false; printf "%s" I_am; set -e; false; printf "%s\n" _Broken' 218 219 # same, except -e this time is on from the beginning 220 atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \ 221 ${TEST_SH} -ec 'printf "%s" I_am; false; printf "%s\n" _Broken' 222 223 # More checking of -e in other places, there is lots to deal with. 224} 225 226atf_test_case set_f 227set_f_head() { 228 atf_set "descr" "Tests that 'set -f' turns off pathname expansion " \ 229 "and that it behaves as defined by the standard" 230} 231set_f_body() { 232 atf_require_prog ls 233 234 test_option_on_off f 235 236 # Check that the environment to use for the tests is sane ... 237 # we assume current dir is a new tempory directory & is empty 238 239 test -z "$(ls)" || atf_skip "Test execution directory not clean" 240 241 # we will assume that atf will clean up this junk directory 242 # when we are done. But for testing pathname expansion 243 # we need files 244 245 for f in a b c d e f aa ab ac ad ae aaa aab aac aad aba abc bbb ccc 246 do 247 echo "$f" > "$f" 248 done 249 250 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \ 251 'X=$(echo b*); Y=$(echo b*); test "${X}" != "a*"; 252 test "${X}" = "${Y}"' 253 254 # now test expansion is different when -f is set 255 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \ 256 'X=$(echo b*); Y=$(set -f; echo b*); test "${X}" != "${Y}"' 257} 258 259atf_test_case set_n 260set_n_head() { 261 atf_set "descr" "Tests that 'set -n' supresses command execution " \ 262 "and that it behaves as defined by the standard" 263} 264set_n_body() { 265 # pointless to test this, if it turns on, it stays on... 266 # test_option_on_off n 267 # so just allow the tests below to verify it can be turned on 268 269 # nothing should be executed, hence no output... 270 atf_check -s exit:0 -o empty -e empty \ 271 ${TEST_SH} -enc 'echo ABANDON HOPE; echo ALL YE; echo ...' 272 273 # this is true even when the "commands" do not exist 274 atf_check -s exit:0 -o empty -e empty \ 275 ${TEST_SH} -enc 'ERR; FAIL; ABANDON HOPE' 276 277 # but if there is a syntax error, it should be detected (w or w/o -e) 278 atf_check -s not-exit:0 -o empty -e not-empty \ 279 ${TEST_SH} -enc 'echo JUMP; for frogs swim; echo in puddles' 280 atf_check -s not-exit:0 -o empty -e not-empty \ 281 ${TEST_SH} -nc 'echo ABANDON HOPE; echo "ALL YE; echo ...' 282 atf_check -s not-exit:0 -o empty -e not-empty \ 283 ${TEST_SH} -enc 'echo ABANDON HOPE;; echo ALL YE; echo ...' 284 atf_check -s not-exit:0 -o empty -e not-empty \ 285 ${TEST_SH} -nc 'do YOU ABANDON HOPE; for all eternity?' 286 287 # now test enabling -n in the middle of a script 288 # note that once turned on, it cannot be turned off again. 289 # 290 # omit more complex cases, as those can send some shells 291 # into infinite loops, and believe it or not, that might be OK! 292 293 atf_check -s exit:0 -o match:first -o not-match:second -e empty \ 294 ${TEST_SH} -c 'echo first; set -n; echo second' 295 atf_check -s exit:0 -o match:first -o not-match:third -e empty \ 296 ${TEST_SH} -c 'echo first; set -n; echo second; set +n; echo third' 297 atf_check -s exit:0 -o inline:'a\nb\n' -e empty \ 298 ${TEST_SH} -c 'for x in a b c d 299 do 300 case "$x" in 301 a);; b);; c) set -n;; d);; 302 esac 303 printf "%s\n" "$x" 304 done' 305 306 # This last one is a bit more complex to explain, so I will not try 307 308 # First, we need to know what signal number is used for SIGUSR1 on 309 # the local (testing) system (signal number is $(( $XIT - 128 )) ) 310 311 # this will take slightly over 1 second elapsed time (the sleep 1) 312 # The "10" for the first sleep just needs to be something big enough 313 # that the rest of the commands have time to complete, even on 314 # very slow testing systems. 10 should be enough. Otherwise irrelevant 315 316 # The shell will usually blather to stderr about the sleep 10 being 317 # killed, but it affects nothing, so just allow it to cry. 318 319 (sleep 10 & sleep 1; kill -USR1 $!; wait $!) 320 XIT="$?" 321 322 # The exit value should be an integer > 128 and < 256 (often 158) 323 # If it is not just skip the test 324 325 # If we do run the test, it should take (slightly over) either 1 or 2 326 # seconds to complete, depending upon the shell being tested. 327 328 case "${XIT}" in 329 ( 129 | 1[3-9][0-9] | 2[0-4][0-9] | 25[0-5] ) 330 331 # The script below should exit with the same code - no output 332 333 # Or that is the result that seems best explanable. 334 # "set -n" in uses like this is not exactly well defined... 335 336 # This script comes from a member of the austin group 337 # (they author changes to the posix shell spec - and more.) 338 # The author is also an (occasional?) NetBSD user. 339 atf_check -s exit:${XIT} -o empty -e empty ${TEST_SH} -c ' 340 trap "set -n" USR1 341 { sleep 1; kill -USR1 $$; sleep 1; } & 342 false 343 wait && echo t || echo f 344 wait 345 echo foo 346 ' 347 ;; 348 esac 349} 350 351atf_test_case set_u 352set_u_head() { 353 atf_set "descr" "Tests that 'set -u' turns on unset var detection " \ 354 "and that it behaves as defined by the standard" 355} 356set_u_body() { 357 test_option_on_off u 358 359 unset ENV # make sure there is nothing there to cause problems 360 361 # first make sure it is OK to unset an unset variable 362 atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ 363 'unset _UNSET_VARIABLE_; echo OK' 364 # even if -u is set 365 atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -cue \ 366 'unset _UNSET_VARIABLE_; echo OK' 367 368 # and that without -u accessing an unset variable is harmless 369 atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ 370 'unset X; echo ${X}; echo OK' 371 # and that the unset variable test expansion works properly 372 atf_check -s exit:0 -o match:OKOK -e empty ${TEST_SH} -ce \ 373 'unset X; printf "%s" ${X-OK}; echo OK' 374 375 # Next test that with -u set, the shell aborts on access to unset var 376 # do not use -e, want to make sure it is -u that causes abort 377 atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ 378 'unset X; set -u; echo ${X}; echo ERR' 379 # quoting should make no difference... 380 atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ 381 'unset X; set -u; echo "${X}"; echo ERR' 382 383 # Now a bunch of accesses to unset vars, with -u, in ways that are OK 384 atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ 385 'unset X; set -u; echo ${X-GOOD}; echo OK' 386 atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ 387 'unset X; set -u; echo ${X-OK}' 388 atf_check -s exit:0 -o not-match:ERR -o match:OK -e empty \ 389 ${TEST_SH} -ce 'unset X; set -u; echo ${X+ERR}; echo OK' 390 391 # and some more ways that are not OK 392 atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ 393 'unset X; set -u; echo ${X#foo}; echo ERR' 394 atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ 395 'unset X; set -u; echo ${X%%bar}; echo ERR' 396 397 # lastly, just while we are checking unset vars, test aborts w/o -u 398 atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ 399 'unset X; echo ${X?}; echo ERR' 400 atf_check -s not-exit:0 -o not-match:ERR -e match:X_NOT_SET \ 401 ${TEST_SH} -c 'unset X; echo ${X?X_NOT_SET}; echo ERR' 402} 403 404atf_test_case set_v 405set_v_head() { 406 atf_set "descr" "Tests that 'set -v' turns on input read echoing " \ 407 "and that it behaves as defined by the standard" 408} 409set_v_body() { 410 test_option_on_off v 411 412 # check that -v does nothing if no later input line is read 413 atf_check -s exit:0 \ 414 -o match:OKOK -o not-match:echo -o not-match:printf \ 415 -e empty \ 416 ${TEST_SH} -ec 'printf "%s" OK; set -v; echo OK; exit 0' 417 418 # but that it does when there are multiple lines 419 cat <<- 'EOF' | 420 set -v 421 printf %s OK 422 echo OK 423 exit 0 424 EOF 425 atf_check -s exit:0 \ 426 -o match:OKOK -o not-match:echo -o not-match:printf \ 427 -e match:printf -e match:OK -e match:echo \ 428 -e not-match:set ${TEST_SH} 429 430 # and that it can be disabled again 431 cat <<- 'EOF' | 432 set -v 433 printf %s OK 434 set +v 435 echo OK 436 exit 0 437 EOF 438 atf_check -s exit:0 \ 439 -o match:OKOK -o not-match:echo -o not-match:printf \ 440 -e match:printf -e match:OK -e not-match:echo \ 441 ${TEST_SH} 442 443 # and lastly, that shell keywords do get output when "read" 444 cat <<- 'EOF' | 445 set -v 446 for i in 111 222 333 447 do 448 printf %s $i 449 done 450 exit 0 451 EOF 452 atf_check -s exit:0 \ 453 -o match:111222333 -o not-match:printf \ 454 -o not-match:for -o not-match:do -o not-match:done \ 455 -e match:printf -e match:111 -e not-match:111222 \ 456 -e match:for -e match:do -e match:done \ 457 ${TEST_SH} 458} 459 460atf_test_case set_x 461set_x_head() { 462 atf_set "descr" "Tests that 'set -x' turns on command exec logging " \ 463 "and that it behaves as defined by the standard" 464} 465set_x_body() { 466 test_option_on_off x 467 468 # check that cmd output appears after -x is enabled 469 atf_check -s exit:0 \ 470 -o match:OKOK -o not-match:echo -o not-match:printf \ 471 -e not-match:printf -e match:OK -e match:echo \ 472 ${TEST_SH} -ec 'printf "%s" OK; set -x; echo OK; exit 0' 473 474 # and that it stops again afer -x is disabled 475 atf_check -s exit:0 \ 476 -o match:OKOK -o not-match:echo -o not-match:printf \ 477 -e match:printf -e match:OK -e not-match:echo \ 478 ${TEST_SH} -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0' 479 480 # also check that PS4 is output correctly 481 atf_check -s exit:0 \ 482 -o match:OK -o not-match:echo \ 483 -e match:OK -e match:Run:echo \ 484 ${TEST_SH} -ec 'PS4=Run:; set -x; echo OK; exit 0' 485 486 return 0 487 488 # This one seems controversial... I suspect it is NetBSD's sh 489 # that is wrong to not output "for" "while" "if" ... etc 490 491 # and lastly, that shell keywords do not get output when "executed" 492 atf_check -s exit:0 \ 493 -o match:111222333 -o not-match:printf \ 494 -o not-match:for \ 495 -e match:printf -e match:111 -e not-match:111222 \ 496 -e not-match:for -e not-match:do -e not-match:done \ 497 ${TEST_SH} -ec \ 498 'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0' 499} 500 501atf_test_case set_X 502set_X_head() { 503 atf_set "descr" "Tests that 'set -X' turns on command exec logging " \ 504 "and that it enables set -x and retains a single fd" 505} 506set_X_body() { 507 508 # First we need to verify that $TEST_SH supports -X 509 test_optional_on_off X || 510 atf_skip "$TEST_SH does not support -X" 511 512 # and that the -X it implements is the -X we expect 513 $TEST_SH -c 'exec 2>/dev/null; 514 set +x; set -X; 515 case "$-" in (*x*) exit 0;; esac; 516 exit 1' || 517 atf_skip "$TEST_SH supports -X but not 'the' -X" 518 519 # Above has already tested that set -X => set -x 520 # Now test that set +X => set +x 521 # and that set -x and set +x do not affect -X 522 523 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 524 'set -x; set +X; case "$-" in (*x*) echo FAIL; exit 1;; esac' 525 526 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 527 'set -X; set +x; 528 case "$-" in (*x*) echo FAIL; exit 1;; esac 529 case "$-" in (*X*) exit 0;; esac; echo ERROR; exit 2' 530 531 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 532 'set -X; set +x; set -x; 533 case "$-" in (*x*X*|*X*x*) exit 0;; esac; echo ERROR; exit 2' 534 535 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 536 'set +X; set -x; 537 case "$-" in (*X*) echo FAIL; exit 1;; esac 538 case "$-" in (*x*) exit 0;; esac; echo ERROR; exit 2' 539 540 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 541 'set +X; set -x; set +x; 542 case "$-" in (*[xX]*) echo FAULT; exit 3;; esac' 543 544 # The following just verify regular tracing using -X instead of -x 545 # These are the same tests as the -x test (set_x) performs. 546 547 # check that cmd output appears after -X is enabled 548 atf_check -s exit:0 \ 549 -o match:OKOK -o not-match:echo -o not-match:printf \ 550 -e not-match:printf -e match:OK -e match:echo \ 551 ${TEST_SH} -ec 'printf "%s" OK; set -X; echo OK; exit 0' 552 553 # and that it stops again afer -X is disabled 554 atf_check -s exit:0 \ 555 -o match:OKOK -o not-match:echo -o not-match:printf \ 556 -e match:printf -e match:OK -e not-match:echo \ 557 ${TEST_SH} -ec 'set -X; printf "%s" OK; set +X; echo OK; exit 0' 558 559 # also check that PS4 is output correctly 560 atf_check -s exit:0 \ 561 -o match:OK -o not-match:echo \ 562 -e match:OK -e match:Run:echo \ 563 ${TEST_SH} -ec 'PS4=Run:; set -X; echo OK; exit 0' 564 565 # end copies of -x tests ... 566 567 # now check that we can move stderr around without affecting -X output 568 569 atf_check -s exit:0 \ 570 -o match:OKOK -o not-match:echo -o not-match:printf \ 571 -e match:printf -e match:OK -e match:echo \ 572 ${TEST_SH} -ecX 'printf "%s" OK; exec 2>/dev/null; echo OK' 573 atf_check -s exit:0 \ 574 -o match:OKOK -o not-match:echo -o not-match:printf \ 575 -e match:printf -e match:OK -e match:echo \ 576 ${TEST_SH} -ecX 'printf "%s" OK; exec 2>&1; echo OK' 577 atf_check -s exit:0 \ 578 -o match:OKOK -o not-match:echo -o not-match:printf \ 579 -e match:printf -e match:OK -e match:echo \ 580 ${TEST_SH} -ecX 'printf "%s" OK; exec 2>&-; echo OK' 581 582 # and that we can put tracing on an external file, leaving stderr alone 583 584 atf_require_prog grep 585 586 rm -f X-trace 587 atf_check -s exit:0 \ 588 -o match:OKOK -o not-match:echo -o not-match:printf \ 589 -e empty \ 590 ${TEST_SH} -ec 'PS4=; set -X 2>X-trace; printf "%s" OK; echo OK' 591 test -s X-trace || atf_fail "T1: Failed to create trace output file" 592 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 593 atf_fail "T1: -X tracing missing printf" 594 grep >/dev/null 2>&1 'echo.*OK' X-trace || 595 atf_fail "T1: -X tracing missing echo" 596 597 rm -f X-trace 598 atf_check -s exit:0 \ 599 -o match:OKOK -o not-match:echo -o not-match:printf \ 600 -e empty \ 601 ${TEST_SH} -ec \ 602 'PS4=; set -X 2>X-trace; 603 printf "%s" OK; 604 exec 2>/dev/null; 605 echo OK' 606 test -s X-trace || atf_fail "T2: Failed to create trace output file" 607 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 608 atf_fail "T2: -X tracing missing printf" 609 grep >/dev/null 2>&1 'exec' X-trace || 610 atf_fail "T2: -X tracing missing exec" 611 grep >/dev/null 2>&1 'echo.*OK' X-trace || 612 atf_fail "T2: -X tracing missing echo after stderr redirect" 613 614 rm -f X-trace 615 atf_check -s exit:0 \ 616 -o match:OKOK -o not-match:echo -o not-match:printf \ 617 -e empty \ 618 ${TEST_SH} -ec \ 619 'PS4=; set -X 2>X-trace; 620 printf "%s" OK; 621 set -X 2>/dev/null; 622 echo OK' 623 test -s X-trace || atf_fail "T3: Failed to create trace output file" 624 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 625 atf_fail "T3: -X tracing missing printf" 626 grep >/dev/null 2>&1 'set.*-X' X-trace || 627 atf_fail "T3: -X tracing missing set -X" 628 grep >/dev/null 2>&1 'echo.*OK' X-trace && 629 atf_fail "T3: -X tracing included echo after set -X redirect" 630 631 rm -f X-trace 632 atf_check -s exit:0 \ 633 -o match:OKOK -o not-match:echo -o not-match:printf \ 634 -e match:echo -e match:OK -e not-match:printf \ 635 ${TEST_SH} -ec \ 636 'PS4=; set -X 2>X-trace; 637 printf "%s" OK; 638 set -X; 639 echo OK' 640 test -s X-trace || atf_fail "T4: Failed to create trace output file" 641 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 642 atf_fail "T4: -X tracing missing printf" 643 grep >/dev/null 2>&1 'set.*-X' X-trace || 644 atf_fail "T4: -X tracing missing set -X" 645 grep >/dev/null 2>&1 'echo.*OK' X-trace && 646 atf_fail "T4: -X tracing included echo after set -X redirect" 647 648 # Now check that -X and the tracing files work properly wrt functions 649 650 # a shell that supports -X should support "local -" ... but verify 651 652 ( ${TEST_SH} -c 'fn() { local - || exit 2; set -f; }; set +f; fn; 653 case "$-" in ("*f*") exit 1;; esac; exit 0' ) 2>/dev/null || 654 atf_skip "-X function test: 'local -' unsupported" 655 656 rm -f X-trace X-trace-fn 657 atf_check -s exit:0 \ 658 -o match:OKhelloGOOD \ 659 -e empty \ 660 ${TEST_SH} -c ' 661 say() { 662 printf "%s" "$*" 663 } 664 funct() { 665 local - 666 667 set -X 2>X-trace-fn 668 say hello 669 } 670 671 set -X 2>X-trace 672 673 printf OK 674 funct 675 echo GOOD 676 ' 677 test -s X-trace || atf_fail "T5: Failed to create trace output file" 678 test -s X-trace-fn || atf_fail "T5: Failed to create fn trace output" 679 grep >/dev/null 2>&1 'printf.*OK' X-trace || 680 atf_fail "T5: -X tracing missing printf" 681 grep >/dev/null 2>&1 funct X-trace || 682 atf_fail "T5: -X tracing missing funct" 683 grep >/dev/null 2>&1 'set.*-X' X-trace || 684 atf_fail "T5: -X tracing missing set -X from in funct" 685 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 686 atf_fail "T5: -X tracing missing echo after funct redirect" 687 grep >/dev/null 2>&1 'say.*hello' X-trace && 688 atf_fail "T5: -X tracing included 'say' after funct redirect" 689 grep >/dev/null 2>&1 'say.*hello' X-trace-fn || 690 atf_fail "T5: -X funct tracing missed 'say'" 691 692 rm -f X-trace X-trace-fn 693 694 atf_check -s exit:0 \ 695 -o match:OKhelloGOOD \ 696 -e empty \ 697 ${TEST_SH} -c ' 698 say() { 699 printf "%s" "$*" 700 } 701 funct() { 702 local - 703 704 set +X 705 say hello 706 } 707 708 set -X 2>X-trace 709 710 printf OK 711 funct 712 echo GOOD 713 ' 714 test -s X-trace || atf_fail "T6: Failed to create trace output file" 715 grep >/dev/null 2>&1 'printf.*OK' X-trace || 716 atf_fail "T6: -X tracing missing printf" 717 grep >/dev/null 2>&1 funct X-trace || 718 atf_fail "T6: -X tracing missing funct" 719 grep >/dev/null 2>&1 'set.*+X' X-trace || 720 atf_fail "T6: -X tracing missing set +X from in funct" 721 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 722 atf_fail "T6: -X tracing missing echo after funct redirect" 723 grep >/dev/null 2>&1 'say.*hello' X-trace && 724 atf_fail "T6: -X tracing included 'say' after funct redirect" 725 726 rm -f X-trace 727 728 atf_check -s exit:0 \ 729 -o match:OKtracednotraceGOOD \ 730 -e match:say -e match:traced -e not-match:notrace \ 731 ${TEST_SH} -c ' 732 say() { 733 printf "%s" "$*" 734 } 735 funct() { 736 local - 737 738 set +X -x 739 740 say traced 741 exec 2>/dev/null 742 say notrace 743 744 } 745 746 set -X 2>X-trace 747 748 printf OK 749 funct 750 echo GOOD 751 ' 752 test -s X-trace || atf_fail "T7: Failed to create trace output file" 753 grep >/dev/null 2>&1 'printf.*OK' X-trace || 754 atf_fail "T7: -X tracing missing printf" 755 grep >/dev/null 2>&1 funct X-trace || 756 atf_fail "T7: -X tracing missing funct" 757 grep >/dev/null 2>&1 'set.*+X.*-x' X-trace || 758 atf_fail "T7: -X tracing missing set +X -x from in funct" 759 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 760 atf_fail "T7: -X tracing missing echo after funct +X" 761 grep >/dev/null 2>&1 'say.*hello' X-trace && 762 atf_fail "T7: -X tracing included 'say' after funct +X" 763 764 rm -f X-trace X-trace-fn 765 atf_check -s exit:0 \ 766 -o "match:OKg'daybye-bye.*hello.*GOOD" \ 767 -e empty \ 768 ${TEST_SH} -c ' 769 say() { 770 printf "%s" "$*" 771 } 772 fn1() { 773 local - 774 775 set -X 2>>X-trace-fn 776 say "g'\''day" 777 "$@" 778 say bye-bye 779 } 780 fn2() { 781 set +X 782 say hello 783 "$@" 784 say goodbye 785 } 786 787 set -X 2>X-trace 788 789 printf OK 790 fn1 791 fn1 fn2 792 fn1 fn1 fn2 793 fn1 fn2 fn1 fn2 fn1 794 fn1 fn1 fn2 fn2 fn1 795 echo GOOD 796 ' 797 798 # That test generally succeeds if the earlier ones did 799 # and if it did not dump core! 800 801 # But we can check a few things... 802 803 test -s X-trace || atf_fail "T8: Failed to create trace output file" 804 test -s X-trace-fn || atf_fail "T8: Failed to create trace output file" 805 grep >/dev/null 2>&1 'printf.*OK' X-trace || 806 atf_fail "T8: -X tracing missing printf" 807 grep >/dev/null 2>&1 fn1 X-trace || 808 atf_fail "T8: -X tracing missing fn1" 809 grep >/dev/null 2>&1 'set.*-X' X-trace || 810 atf_fail "T8: -X tracing missing set -X from in fn1" 811 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 812 atf_fail "T8: -X tracing missing echo after fn1 redirect" 813 grep >/dev/null 2>&1 'say.*hello' X-trace && 814 atf_fail "T8: -X tracing included 'say' after fn2 +X" 815 grep >/dev/null 2>&1 'say.*hello' X-trace-fn && 816 atf_fail "T8: -X fn tracing included 'say' after fn2 +X" 817 818 819 rm -f X-trace 820 821 return 0 822} 823 824opt_test_setup() 825{ 826 test -n "$1" || { echo >&2 "Internal error"; exit 1; } 827 828 cat > "$1" << 'END_OF_FUNCTIONS' 829local_opt_check() 830{ 831 local - 832} 833 834instr() 835{ 836 expr "$2" : "\(.*$1\)" >/dev/null 837} 838 839save_opts() 840{ 841 local - 842 843 set -e 844 set -u 845 846 instr e "$-" && instr u "$-" && return 0 847 echo ERR 848} 849 850fiddle_opts() 851{ 852 set -e 853 set -u 854 855 instr e "$-" && instr u "$-" && return 0 856 echo ERR 857} 858 859local_test() 860{ 861 set +eu 862 863 save_opts 864 instr '[eu]' "$-" || printf %s "OK" 865 866 fiddle_opts 867 instr e "$-" && instr u "$-" && printf %s "OK" 868 869 set +eu 870} 871END_OF_FUNCTIONS 872} 873 874atf_test_case restore_local_opts 875restore_local_opts_head() { 876 atf_set "descr" "Tests that 'local -' saves and restores options. " \ 877 "Note that "local" is a local shell addition" 878} 879restore_local_opts_body() { 880 atf_require_prog cat 881 atf_require_prog expr 882 883 FN="test-funcs.$$" 884 opt_test_setup "${FN}" || atf_skip "Cannot setup test environment" 885 886 ${TEST_SH} -ec ". './${FN}'; local_opt_check" 2>/dev/null || 887 atf_skip "sh extension 'local -' not supported by ${TEST_SH}" 888 889 atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \ 890 ${TEST_SH} -ec ". './${FN}'; local_test" 891} 892 893atf_test_case vi_emacs_VE_toggle 894vi_emacs_VE_toggle_head() { 895 atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\ 896 " Note that -V and -E are local shell additions" 897} 898vi_emacs_VE_toggle_body() { 899 900 test_optional_on_off V E || 901 atf_skip "One or both V & E opts unsupported by ${TEST_SH}" 902 903 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c ' 904 q() { 905 eval "case \"$-\" in 906 (*${2}*) return 1;; 907 (*${1}*) return 0;; 908 esac" 909 return 1 910 } 911 x() { 912 echo >&2 "Option set or toggle failure:" \ 913 " on=$1 off=$2 set=$-" 914 exit 1 915 } 916 set -V; q V E || x V E 917 set -E; q E V || x E V 918 set -V; q V E || x V E 919 set +EV; q "" "[VE]" || x "" VE 920 exit 0 921 ' 922} 923 924atf_test_case pipefail 925pipefail_head() { 926 atf_set "descr" "Basic tests of the pipefail option" 927} 928pipefail_body() { 929 ${TEST_SH} -c 'set -o pipefail' 2>/dev/null || 930 atf_skip "pipefail option not supported by ${TEST_SH}" 931 932 atf_check -s exit:0 -o match:'pipefail.*off' -e empty ${TEST_SH} -c \ 933 'set -o | grep pipefail' 934 atf_check -s exit:0 -o match:'pipefail.*on' -e empty ${TEST_SH} -c \ 935 'set -o pipefail; set -o | grep pipefail' 936 937 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 938 '(exit 1) | (exit 2) | (exit 0)' 939 atf_check -s exit:2 -o empty -e empty ${TEST_SH} -c \ 940 'set -o pipefail; (exit 1) | (exit 2) | (exit 0)' 941 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ 942 'set -o pipefail; (exit 1) | (exit 0) | (exit 0)' 943 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 944 'set -o pipefail; (exit 0) | (exit 0) | (exit 0)' 945 946 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ 947 '! (exit 1) | (exit 2) | (exit 0)' 948 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 949 'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0)' 950 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 951 'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0)' 952 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ 953 'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0)' 954 955 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 956 '(exit 1) | (exit 2) | (exit 0); echo $?' 957 atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ 958 'set -o pipefail; (exit 1) | (exit 2) | (exit 0); echo $?' 959 atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ 960 'set -o pipefail; (exit 1) | (exit 0) | (exit 0); echo $?' 961 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 962 'set -o pipefail; (exit 0) | (exit 0) | (exit 0); echo $?' 963 964 atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ 965 '! (exit 1) | (exit 2) | (exit 0); echo $?' 966 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 967 'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0); echo $?' 968 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 969 'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0); echo $?' 970 atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ 971 'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0); echo $?' 972} 973 974atf_test_case xx_bogus 975xx_bogus_head() { 976 atf_set "descr" "Tests that attempting to set a nonsense option fails." 977} 978xx_bogus_body() { 979 # Biggest problem here is picking a "nonsense option" that is 980 # not implemented by any shell, anywhere. Hopefully this will do. 981 982 # 'set' is a special builtin, so a conforming shell should exit 983 # on an arg error, and the ERR should not be printed. 984 atf_check -s not-exit:0 -o empty -e not-empty \ 985 ${TEST_SH} -c 'set -% ; echo ERR' 986} 987 988atf_test_case Option_switching 989Option_switching_head() { 990 atf_set "descr" "options can be enabled and disabled" 991} 992Option_switching_body() { 993 994 # Cannot test -m, setting it causes test shell to fail... 995 # (test shell gets SIGKILL!) Wonder why ... something related to atf 996 # That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'" 997 998 # Don't bother testing toggling -n, once on, it stays on... 999 # (and because the test fn refuses to allow us to try) 1000 1001 # Cannot test -o or -c here, or the extension -s 1002 # they can only be used, not switched 1003 1004 # these are the posix options, that all shells should implement 1005 test_option_on_off a b C e f h u v x # m 1006 1007 # and these are extensions that might not exist (non-fatal to test) 1008 # -i and -s (and -c) are posix options, but are not required to 1009 # be accessable via the "set" command, just the command line. 1010 # We allow for -i to work with set, as that makes some sense, 1011 # -c and -s do not. 1012 test_optional_on_off E i I p q V X || true 1013 1014 # Also test (some) option combinations ... 1015 # only testing posix options here, because it is easier... 1016 test_option_on_off aeu vx Ca aCefux 1017} 1018 1019atf_init_test_cases() { 1020 # tests are run in order sort of names produces, so choose names wisely 1021 1022 # this one tests turning on/off all the mandatory. and extra flags 1023 atf_add_test_case Option_switching 1024 # and this tests the NetBSD "local -" functionality in functions. 1025 atf_add_test_case restore_local_opts 1026 1027 # no tests for -m (no idea how to do that one) 1028 # -I (no easy way to generate the EOF it ignores) 1029 # -i (not sure how to test that one at the minute) 1030 # -p (because we aren't going to run tests setuid) 1031 # -V/-E (too much effort, and a real test would be huge) 1032 # -c (because almost all the other tests test it anyway) 1033 # -q (because, for now, I am lazy) 1034 # -s (coming soon, hopefully) 1035 # -o (really +o: again, hopefully soon) 1036 # -o longname (again, just laziness, don't wait...) 1037 # -h/-b (because NetBSD doesn't implement them) 1038 atf_add_test_case set_a 1039 atf_add_test_case set_C 1040 atf_add_test_case set_e 1041 atf_add_test_case set_f 1042 atf_add_test_case set_n 1043 atf_add_test_case set_u 1044 atf_add_test_case set_v 1045 atf_add_test_case set_x 1046 atf_add_test_case set_X 1047 1048 atf_add_test_case vi_emacs_VE_toggle 1049 1050 atf_add_test_case pipefail 1051 1052 atf_add_test_case xx_bogus 1053} 1054