1# $NetBSD: t_option.sh,v 1.9 2022/05/22 11:27:36 andvar 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' suppresses 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 atf_fail '111 222 333 test failure' 459} 460 461atf_test_case set_x 462set_x_head() { 463 atf_set "descr" "Tests that 'set -x' turns on command exec logging " \ 464 "and that it behaves as defined by the standard" 465} 466set_x_body() { 467 test_option_on_off x 468 469 # check that cmd output appears after -x is enabled 470 atf_check -s exit:0 \ 471 -o match:OKOK -o not-match:echo -o not-match:printf \ 472 -e not-match:printf -e match:OK -e match:echo \ 473 ${TEST_SH} -ec 'printf "%s" OK; set -x; echo OK; exit 0' 474 475 # and that it stops again afer -x is disabled 476 atf_check -s exit:0 \ 477 -o match:OKOK -o not-match:echo -o not-match:printf \ 478 -e match:printf -e match:OK -e not-match:echo \ 479 ${TEST_SH} -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0' 480 481 # also check that PS4 is output correctly 482 atf_check -s exit:0 \ 483 -o match:OK -o not-match:echo \ 484 -e match:OK -e match:Run:echo \ 485 ${TEST_SH} -ec 'PS4=Run:; set -x; echo OK; exit 0' 486 487 return 0 488 489 # This one seems controversial... I suspect it is NetBSD's sh 490 # that is wrong to not output "for" "while" "if" ... etc 491 492 # and lastly, that shell keywords do not get output when "executed" 493 atf_check -s exit:0 \ 494 -o match:111222333 -o not-match:printf \ 495 -o not-match:for \ 496 -e match:printf -e match:111 -e not-match:111222 \ 497 -e not-match:for -e not-match:do -e not-match:done \ 498 ${TEST_SH} -ec \ 499 'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0' 500} 501 502atf_test_case set_X 503set_X_head() { 504 atf_set "descr" "Tests that 'set -X' turns on command exec logging " \ 505 "and that it enables set -x and retains a single fd" 506} 507set_X_body() { 508 509 # First we need to verify that $TEST_SH supports -X 510 test_optional_on_off X || 511 atf_skip "$TEST_SH does not support -X" 512 513 # and that the -X it implements is the -X we expect 514 $TEST_SH -c 'exec 2>/dev/null; 515 set +x; set -X; 516 case "$-" in (*x*) exit 0;; esac; 517 exit 1' || 518 atf_skip "$TEST_SH supports -X but not 'the' -X" 519 520 # Above has already tested that set -X => set -x 521 # Now test that set +X => set +x 522 # and that set -x and set +x do not affect -X 523 524 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 525 'set -x; set +X; case "$-" in (*x*) echo FAIL; exit 1;; esac' 526 527 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 528 'set -X; set +x; 529 case "$-" in (*x*) echo FAIL; exit 1;; esac 530 case "$-" in (*X*) exit 0;; esac; echo ERROR; exit 2' 531 532 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 533 'set -X; set +x; set -x; 534 case "$-" in (*x*X*|*X*x*) exit 0;; esac; echo ERROR; exit 2' 535 536 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 537 'set +X; set -x; 538 case "$-" in (*X*) echo FAIL; exit 1;; esac 539 case "$-" in (*x*) exit 0;; esac; echo ERROR; exit 2' 540 541 atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \ 542 'set +X; set -x; set +x; 543 case "$-" in (*[xX]*) echo FAULT; exit 3;; esac' 544 545 # The following just verify regular tracing using -X instead of -x 546 # These are the same tests as the -x test (set_x) performs. 547 548 # check that cmd output appears after -X is enabled 549 atf_check -s exit:0 \ 550 -o match:OKOK -o not-match:echo -o not-match:printf \ 551 -e not-match:printf -e match:OK -e match:echo \ 552 ${TEST_SH} -ec 'printf "%s" OK; set -X; echo OK; exit 0' 553 554 # and that it stops again afer -X is disabled 555 atf_check -s exit:0 \ 556 -o match:OKOK -o not-match:echo -o not-match:printf \ 557 -e match:printf -e match:OK -e not-match:echo \ 558 ${TEST_SH} -ec 'set -X; printf "%s" OK; set +X; echo OK; exit 0' 559 560 # also check that PS4 is output correctly 561 atf_check -s exit:0 \ 562 -o match:OK -o not-match:echo \ 563 -e match:OK -e match:Run:echo \ 564 ${TEST_SH} -ec 'PS4=Run:; set -X; echo OK; exit 0' 565 566 # end copies of -x tests ... 567 568 # now check that we can move stderr around without affecting -X output 569 570 atf_check -s exit:0 \ 571 -o match:OKOK -o not-match:echo -o not-match:printf \ 572 -e match:printf -e match:OK -e match:echo \ 573 ${TEST_SH} -ecX 'printf "%s" OK; exec 2>/dev/null; echo OK' 574 atf_check -s exit:0 \ 575 -o match:OKOK -o not-match:echo -o not-match:printf \ 576 -e match:printf -e match:OK -e match:echo \ 577 ${TEST_SH} -ecX 'printf "%s" OK; exec 2>&1; echo OK' 578 atf_check -s exit:0 \ 579 -o match:OKOK -o not-match:echo -o not-match:printf \ 580 -e match:printf -e match:OK -e match:echo \ 581 ${TEST_SH} -ecX 'printf "%s" OK; exec 2>&-; echo OK' 582 583 # and that we can put tracing on an external file, leaving stderr alone 584 585 atf_require_prog grep 586 587 rm -f X-trace 588 atf_check -s exit:0 \ 589 -o match:OKOK -o not-match:echo -o not-match:printf \ 590 -e empty \ 591 ${TEST_SH} -ec 'PS4=; set -X 2>X-trace; printf "%s" OK; echo OK' 592 test -s X-trace || atf_fail "T1: Failed to create trace output file" 593 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 594 atf_fail "T1: -X tracing missing printf" 595 grep >/dev/null 2>&1 'echo.*OK' X-trace || 596 atf_fail "T1: -X tracing missing echo" 597 598 rm -f X-trace 599 atf_check -s exit:0 \ 600 -o match:OKOK -o not-match:echo -o not-match:printf \ 601 -e empty \ 602 ${TEST_SH} -ec \ 603 'PS4=; set -X 2>X-trace; 604 printf "%s" OK; 605 exec 2>/dev/null; 606 echo OK' 607 test -s X-trace || atf_fail "T2: Failed to create trace output file" 608 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 609 atf_fail "T2: -X tracing missing printf" 610 grep >/dev/null 2>&1 'exec' X-trace || 611 atf_fail "T2: -X tracing missing exec" 612 grep >/dev/null 2>&1 'echo.*OK' X-trace || 613 atf_fail "T2: -X tracing missing echo after stderr redirect" 614 615 rm -f X-trace 616 atf_check -s exit:0 \ 617 -o match:OKOK -o not-match:echo -o not-match:printf \ 618 -e empty \ 619 ${TEST_SH} -ec \ 620 'PS4=; set -X 2>X-trace; 621 printf "%s" OK; 622 set -X 2>/dev/null; 623 echo OK' 624 test -s X-trace || atf_fail "T3: Failed to create trace output file" 625 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 626 atf_fail "T3: -X tracing missing printf" 627 grep >/dev/null 2>&1 'set.*-X' X-trace || 628 atf_fail "T3: -X tracing missing set -X" 629 grep >/dev/null 2>&1 'echo.*OK' X-trace && 630 atf_fail "T3: -X tracing included echo after set -X redirect" 631 632 rm -f X-trace 633 atf_check -s exit:0 \ 634 -o match:OKOK -o not-match:echo -o not-match:printf \ 635 -e match:echo -e match:OK -e not-match:printf \ 636 ${TEST_SH} -ec \ 637 'PS4=; set -X 2>X-trace; 638 printf "%s" OK; 639 set -X; 640 echo OK' 641 test -s X-trace || atf_fail "T4: Failed to create trace output file" 642 grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace || 643 atf_fail "T4: -X tracing missing printf" 644 grep >/dev/null 2>&1 'set.*-X' X-trace || 645 atf_fail "T4: -X tracing missing set -X" 646 grep >/dev/null 2>&1 'echo.*OK' X-trace && 647 atf_fail "T4: -X tracing included echo after set -X redirect" 648 649 # Now check that -X and the tracing files work properly wrt functions 650 651 # a shell that supports -X should support "local -" ... but verify 652 653 ( ${TEST_SH} -c 'fn() { local - || exit 2; set -f; }; set +f; fn; 654 case "$-" in ("*f*") exit 1;; esac; exit 0' ) 2>/dev/null || 655 atf_skip "-X function test: 'local -' unsupported" 656 657 rm -f X-trace X-trace-fn 658 atf_check -s exit:0 \ 659 -o match:OKhelloGOOD \ 660 -e empty \ 661 ${TEST_SH} -c ' 662 say() { 663 printf "%s" "$*" 664 } 665 funct() { 666 local - 667 668 set -X 2>X-trace-fn 669 say hello 670 } 671 672 set -X 2>X-trace 673 674 printf OK 675 funct 676 echo GOOD 677 ' 678 test -s X-trace || atf_fail "T5: Failed to create trace output file" 679 test -s X-trace-fn || atf_fail "T5: Failed to create fn trace output" 680 grep >/dev/null 2>&1 'printf.*OK' X-trace || 681 atf_fail "T5: -X tracing missing printf" 682 grep >/dev/null 2>&1 funct X-trace || 683 atf_fail "T5: -X tracing missing funct" 684 grep >/dev/null 2>&1 'set.*-X' X-trace || 685 atf_fail "T5: -X tracing missing set -X from in funct" 686 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 687 atf_fail "T5: -X tracing missing echo after funct redirect" 688 grep >/dev/null 2>&1 'say.*hello' X-trace && 689 atf_fail "T5: -X tracing included 'say' after funct redirect" 690 grep >/dev/null 2>&1 'say.*hello' X-trace-fn || 691 atf_fail "T5: -X funct tracing missed 'say'" 692 693 rm -f X-trace X-trace-fn 694 695 atf_check -s exit:0 \ 696 -o match:OKhelloGOOD \ 697 -e empty \ 698 ${TEST_SH} -c ' 699 say() { 700 printf "%s" "$*" 701 } 702 funct() { 703 local - 704 705 set +X 706 say hello 707 } 708 709 set -X 2>X-trace 710 711 printf OK 712 funct 713 echo GOOD 714 ' 715 test -s X-trace || atf_fail "T6: Failed to create trace output file" 716 grep >/dev/null 2>&1 'printf.*OK' X-trace || 717 atf_fail "T6: -X tracing missing printf" 718 grep >/dev/null 2>&1 funct X-trace || 719 atf_fail "T6: -X tracing missing funct" 720 grep >/dev/null 2>&1 'set.*+X' X-trace || 721 atf_fail "T6: -X tracing missing set +X from in funct" 722 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 723 atf_fail "T6: -X tracing missing echo after funct redirect" 724 grep >/dev/null 2>&1 'say.*hello' X-trace && 725 atf_fail "T6: -X tracing included 'say' after funct redirect" 726 727 rm -f X-trace 728 729 atf_check -s exit:0 \ 730 -o match:OKtracednotraceGOOD \ 731 -e match:say -e match:traced -e not-match:notrace \ 732 ${TEST_SH} -c ' 733 say() { 734 printf "%s" "$*" 735 } 736 funct() { 737 local - 738 739 set +X -x 740 741 say traced 742 exec 2>/dev/null 743 say notrace 744 745 } 746 747 set -X 2>X-trace 748 749 printf OK 750 funct 751 echo GOOD 752 ' 753 test -s X-trace || atf_fail "T7: Failed to create trace output file" 754 grep >/dev/null 2>&1 'printf.*OK' X-trace || 755 atf_fail "T7: -X tracing missing printf" 756 grep >/dev/null 2>&1 funct X-trace || 757 atf_fail "T7: -X tracing missing funct" 758 grep >/dev/null 2>&1 'set.*+X.*-x' X-trace || 759 atf_fail "T7: -X tracing missing set +X -x from in funct" 760 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 761 atf_fail "T7: -X tracing missing echo after funct +X" 762 grep >/dev/null 2>&1 'say.*hello' X-trace && 763 atf_fail "T7: -X tracing included 'say' after funct +X" 764 765 rm -f X-trace X-trace-fn 766 atf_check -s exit:0 \ 767 -o "match:OKg'daybye-bye.*hello.*GOOD" \ 768 -e empty \ 769 ${TEST_SH} -c ' 770 say() { 771 printf "%s" "$*" 772 } 773 fn1() { 774 local - 775 776 set -X 2>>X-trace-fn 777 say "g'\''day" 778 "$@" 779 say bye-bye 780 } 781 fn2() { 782 set +X 783 say hello 784 "$@" 785 say goodbye 786 } 787 788 set -X 2>X-trace 789 790 printf OK 791 fn1 792 fn1 fn2 793 fn1 fn1 fn2 794 fn1 fn2 fn1 fn2 fn1 795 fn1 fn1 fn2 fn2 fn1 796 echo GOOD 797 ' 798 799 # That test generally succeeds if the earlier ones did 800 # and if it did not dump core! 801 802 # But we can check a few things... 803 804 test -s X-trace || atf_fail "T8: Failed to create trace output file" 805 test -s X-trace-fn || atf_fail "T8: Failed to create trace output file" 806 grep >/dev/null 2>&1 'printf.*OK' X-trace || 807 atf_fail "T8: -X tracing missing printf" 808 grep >/dev/null 2>&1 fn1 X-trace || 809 atf_fail "T8: -X tracing missing fn1" 810 grep >/dev/null 2>&1 'set.*-X' X-trace || 811 atf_fail "T8: -X tracing missing set -X from in fn1" 812 grep >/dev/null 2>&1 'echo.*GOOD' X-trace || 813 atf_fail "T8: -X tracing missing echo after fn1 redirect" 814 grep >/dev/null 2>&1 'say.*hello' X-trace && 815 atf_fail "T8: -X tracing included 'say' after fn2 +X" 816 grep >/dev/null 2>&1 'say.*hello' X-trace-fn && 817 atf_fail "T8: -X fn tracing included 'say' after fn2 +X" 818 819 820 rm -f X-trace 821 822 return 0 823} 824 825opt_test_setup() 826{ 827 test -n "$1" || { echo >&2 "Internal error"; exit 1; } 828 829 cat > "$1" << 'END_OF_FUNCTIONS' 830local_opt_check() 831{ 832 local - 833} 834 835instr() 836{ 837 expr "$2" : "\(.*$1\)" >/dev/null 838} 839 840save_opts() 841{ 842 local - 843 844 set -e 845 set -u 846 847 instr e "$-" && instr u "$-" && return 0 848 echo ERR 849} 850 851fiddle_opts() 852{ 853 set -e 854 set -u 855 856 instr e "$-" && instr u "$-" && return 0 857 echo ERR 858} 859 860local_test() 861{ 862 set +eu 863 864 save_opts 865 instr '[eu]' "$-" || printf %s "OK" 866 867 fiddle_opts 868 instr e "$-" && instr u "$-" && printf %s "OK" 869 870 set +eu 871} 872END_OF_FUNCTIONS 873} 874 875atf_test_case restore_local_opts 876restore_local_opts_head() { 877 atf_set "descr" "Tests that 'local -' saves and restores options. " \ 878 "Note that "local" is a local shell addition" 879} 880restore_local_opts_body() { 881 atf_require_prog cat 882 atf_require_prog expr 883 884 FN="test-funcs.$$" 885 opt_test_setup "${FN}" || atf_skip "Cannot setup test environment" 886 887 ${TEST_SH} -ec ". './${FN}'; local_opt_check" 2>/dev/null || 888 atf_skip "sh extension 'local -' not supported by ${TEST_SH}" 889 890 atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \ 891 ${TEST_SH} -ec ". './${FN}'; local_test" 892} 893 894atf_test_case vi_emacs_VE_toggle 895vi_emacs_VE_toggle_head() { 896 atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\ 897 " Note that -V and -E are local shell additions" 898} 899vi_emacs_VE_toggle_body() { 900 901 test_optional_on_off V E || 902 atf_skip "One or both V & E opts unsupported by ${TEST_SH}" 903 904 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c ' 905 q() { 906 eval "case \"$-\" in 907 (*${2}*) return 1;; 908 (*${1}*) return 0;; 909 esac" 910 return 1 911 } 912 x() { 913 echo >&2 "Option set or toggle failure:" \ 914 " on=$1 off=$2 set=$-" 915 exit 1 916 } 917 set -V; q V E || x V E 918 set -E; q E V || x E V 919 set -V; q V E || x V E 920 set +EV; q "" "[VE]" || x "" VE 921 exit 0 922 ' 923} 924 925atf_test_case pipefail 926pipefail_head() { 927 atf_set "descr" "Basic tests of the pipefail option" 928} 929pipefail_body() { 930 ${TEST_SH} -c 'set -o pipefail' 2>/dev/null || 931 atf_skip "pipefail option not supported by ${TEST_SH}" 932 933 atf_check -s exit:0 -o match:'pipefail.*off' -e empty ${TEST_SH} -c \ 934 'set -o | grep pipefail' 935 atf_check -s exit:0 -o match:'pipefail.*on' -e empty ${TEST_SH} -c \ 936 'set -o pipefail; set -o | grep pipefail' 937 938 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 939 '(exit 1) | (exit 2) | (exit 0)' 940 atf_check -s exit:2 -o empty -e empty ${TEST_SH} -c \ 941 'set -o pipefail; (exit 1) | (exit 2) | (exit 0)' 942 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ 943 'set -o pipefail; (exit 1) | (exit 0) | (exit 0)' 944 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 945 'set -o pipefail; (exit 0) | (exit 0) | (exit 0)' 946 947 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ 948 '! (exit 1) | (exit 2) | (exit 0)' 949 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 950 'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0)' 951 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ 952 'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0)' 953 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \ 954 'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0)' 955 956 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 957 '(exit 1) | (exit 2) | (exit 0); echo $?' 958 atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ 959 'set -o pipefail; (exit 1) | (exit 2) | (exit 0); echo $?' 960 atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ 961 'set -o pipefail; (exit 1) | (exit 0) | (exit 0); echo $?' 962 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 963 'set -o pipefail; (exit 0) | (exit 0) | (exit 0); echo $?' 964 965 atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ 966 '! (exit 1) | (exit 2) | (exit 0); echo $?' 967 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 968 'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0); echo $?' 969 atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ 970 'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0); echo $?' 971 atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ 972 'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0); echo $?' 973} 974 975atf_test_case xx_bogus 976xx_bogus_head() { 977 atf_set "descr" "Tests that attempting to set a nonsense option fails." 978} 979xx_bogus_body() { 980 # Biggest problem here is picking a "nonsense option" that is 981 # not implemented by any shell, anywhere. Hopefully this will do. 982 983 # 'set' is a special builtin, so a conforming shell should exit 984 # on an arg error, and the ERR should not be printed. 985 atf_check -s not-exit:0 -o empty -e not-empty \ 986 ${TEST_SH} -c 'set -% ; echo ERR' 987} 988 989atf_test_case Option_switching 990Option_switching_head() { 991 atf_set "descr" "options can be enabled and disabled" 992} 993Option_switching_body() { 994 995 # Cannot test -m, setting it causes test shell to fail... 996 # (test shell gets SIGKILL!) Wonder why ... something related to atf 997 # That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'" 998 999 # Don't bother testing toggling -n, once on, it stays on... 1000 # (and because the test fn refuses to allow us to try) 1001 1002 # Cannot test -o or -c here, or the extension -s 1003 # they can only be used, not switched 1004 1005 # these are the posix options, that all shells should implement 1006 test_option_on_off a b C e f h u v x # m 1007 1008 # and these are extensions that might not exist (non-fatal to test) 1009 # -i and -s (and -c) are posix options, but are not required to 1010 # be accessible via the "set" command, just the command line. 1011 # We allow for -i to work with set, as that makes some sense, 1012 # -c and -s do not. 1013 test_optional_on_off E i I p q V X || true 1014 1015 # Also test (some) option combinations ... 1016 # only testing posix options here, because it is easier... 1017 test_option_on_off aeu vx Ca aCefux 1018} 1019 1020atf_init_test_cases() { 1021 # tests are run in order sort of names produces, so choose names wisely 1022 1023 # this one tests turning on/off all the mandatory. and extra flags 1024 atf_add_test_case Option_switching 1025 # and this tests the NetBSD "local -" functionality in functions. 1026 atf_add_test_case restore_local_opts 1027 1028 # no tests for -m (no idea how to do that one) 1029 # -I (no easy way to generate the EOF it ignores) 1030 # -i (not sure how to test that one at the minute) 1031 # -p (because we aren't going to run tests setuid) 1032 # -V/-E (too much effort, and a real test would be huge) 1033 # -c (because almost all the other tests test it anyway) 1034 # -q (because, for now, I am lazy) 1035 # -s (coming soon, hopefully) 1036 # -o (really +o: again, hopefully soon) 1037 # -o longname (again, just laziness, don't wait...) 1038 # -h/-b (because NetBSD doesn't implement them) 1039 atf_add_test_case set_a 1040 atf_add_test_case set_C 1041 atf_add_test_case set_e 1042 atf_add_test_case set_f 1043 atf_add_test_case set_n 1044 atf_add_test_case set_u 1045 atf_add_test_case set_v 1046 atf_add_test_case set_x 1047 atf_add_test_case set_X 1048 1049 atf_add_test_case vi_emacs_VE_toggle 1050 1051 atf_add_test_case pipefail 1052 1053 atf_add_test_case xx_bogus 1054} 1055