diff options
Diffstat (limited to 'contrib/netbsd-tests/bin/sh/t_option.sh')
-rwxr-xr-x | contrib/netbsd-tests/bin/sh/t_option.sh | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/contrib/netbsd-tests/bin/sh/t_option.sh b/contrib/netbsd-tests/bin/sh/t_option.sh new file mode 100755 index 0000000..eae2110 --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_option.sh @@ -0,0 +1,674 @@ +# $NetBSD: t_option.sh,v 1.3 2016/03/08 14:19:28 christos Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# The standard +# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html +# says: +# ...[lots] + +test_option_on_off() +{ + atf_require_prog tr + + for opt + do + # t is needed, as inside $()` $- appears to lose + # the 'e' option if it happened to already be + # set. Must check if that is what should + # happen, but that is a different issue. + + test -z "${opt}" && continue + + # if we are playing with more that one option at a + # time, the code below requires that we start with no + # options set, or it will mis-diagnose the situation + CLEAR='' + test "${#opt}" -gt 1 && + CLEAR='xx="$-" && xx=$(echo "$xx" | tr -d cs) && test -n "$xx" && set +"$xx";' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "opt=${opt}"' + x() { + echo "ERROR: Unable to $1 option $2" >&2 + exit 1 + } + s() { + set -"$1" + t="$-" + x=$(echo "$t" | tr -d "$1") + test "$t" = "$x" && x set "$1" + return 0 + } + c() { + set +"$1" + t="$-" + x=$(echo "$t" | tr -d "$1") + test "$t" != "$x" && x clear "$1" + return 0 + } + '"${CLEAR}"' + + # if we do not do this, -x tracing splatters stderr + # for some shells, -v does as well (is that correct?) + case "${opt}" in + (*[xv]*) exec 2>/dev/null;; + esac + + o="$-" + x=$(echo "$o" | tr -d "$opt") + + if [ "$o" = "$x" ]; then # option was off + s "${opt}" + c "${opt}" + else + c "${opt}" + s "${opt}" + fi + ' + done +} + +test_optional_on_off() +{ + RET=0 + OPTS= + for opt + do + test "${opt}" = n && continue + ${TEST_SH} -c "set -${opt}" 2>/dev/null && + OPTS="${OPTS} ${opt}" || RET=1 + done + + test -n "${OPTS}" && test_option_on_off ${OPTS} + + return "${RET}" +} + +atf_test_case set_a +set_a_head() { + atf_set "descr" "Tests that 'set -a' turns on all var export " \ + "and that it behaves as defined by the standard" +} +set_a_body() { + atf_require_prog env + atf_require_prog grep + + test_option_on_off a + + # without -a, new variables should not be exported (so grep "fails") + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -ce \ + 'unset VAR; set +a; VAR=value; env | grep "^VAR="' + + # with -a, they should be + atf_check -s exit:0 -o match:VAR=value -e empty ${TEST_SH} -ce \ + 'unset VAR; set -a; VAR=value; env | grep "^VAR="' +} + +atf_test_case set_C +set_C_head() { + atf_set "descr" "Tests that 'set -C' turns on no clobber mode " \ + "and that it behaves as defined by the standard" +} +set_C_body() { + atf_require_prog ls + + test_option_on_off C + + # Check that the environment to use for the tests is sane ... + # we assume current dir is a new tempory directory & is empty + + test -z "$(ls)" || atf_skip "Test execution directory not clean" + test -c "/dev/null" || atf_skip "Problem with /dev/null" + + echo Dummy_Content > Junk_File + echo Precious_Content > Important_File + + # Check that we can redirect onto file when -C is not set + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + ' + D=$(ls -l Junk_File) || exit 1 + set +C + echo "Overwrite it now" > Junk_File + A=$(ls -l Junk_File) || exit 1 + test "${A}" != "${D}" + ' + + # Check that we cannot redirect onto file when -C is set + atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \ + ' + D=$(ls -l Important_File) || exit 1 + set -C + echo "Fail to Overwrite it now" > Important_File + A=$(ls -l Important_File) || exit 1 + test "${A}" = "${D}" + ' + + # Check that we can append to file, even when -C is set + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + ' + D=$(ls -l Junk_File) || exit 1 + set -C + echo "Append to it now" >> Junk_File + A=$(ls -l Junk_File) || exit 1 + test "${A}" != "${D}" + ' + + # Check that we abort on attempt to redirect onto file when -Ce is set + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + ' + set -Ce + echo "Fail to Overwrite it now" > Important_File + echo "Should not reach this point" + ' + + # Last check that we can override -C for when we really need to + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + ' + D=$(ls -l Junk_File) || exit 1 + set -C + echo "Change the poor bugger again" >| Junk_File + A=$(ls -l Junk_File) || exit 1 + test "${A}" != "${D}" + ' +} + +atf_test_case set_e +set_e_head() { + atf_set "descr" "Tests that 'set -e' turns on error detection " \ + "and that a simple case behaves as defined by the standard" +} +set_e_body() { + test_option_on_off e + + # Check that -e does nothing if no commands fail + atf_check -s exit:0 -o match:I_am_OK -e empty \ + ${TEST_SH} -c \ + 'false; printf "%s" I_am; set -e; true; printf "%s\n" _OK' + + # and that it (silently, but with exit status) aborts if cmd fails + atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \ + ${TEST_SH} -c \ + 'false; printf "%s" I_am; set -e; false; printf "%s\n" _Broken' + + # same, except -e this time is on from the beginning + atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \ + ${TEST_SH} -ec 'printf "%s" I_am; false; printf "%s\n" _Broken' + + # More checking of -e in other places, there is lots to deal with. +} + +atf_test_case set_f +set_f_head() { + atf_set "descr" "Tests that 'set -f' turns off pathname expansion " \ + "and that it behaves as defined by the standard" +} +set_f_body() { + atf_require_prog ls + + test_option_on_off f + + # Check that the environment to use for the tests is sane ... + # we assume current dir is a new tempory directory & is empty + + test -z "$(ls)" || atf_skip "Test execution directory not clean" + + # we will assume that atf will clean up this junk directory + # when we are done. But for testing pathname expansion + # we need files + + for f in a b c d e f aa ab ac ad ae aaa aab aac aad aba abc bbb ccc + do + echo "$f" > "$f" + done + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \ + 'X=$(echo b*); Y=$(echo b*); test "${X}" != "a*"; + test "${X}" = "${Y}"' + + # now test expansion is different when -f is set + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \ + 'X=$(echo b*); Y=$(set -f; echo b*); test "${X}" != "${Y}"' +} + +atf_test_case set_n +set_n_head() { + atf_set "descr" "Tests that 'set -n' supresses command execution " \ + "and that it behaves as defined by the standard" +} +set_n_body() { + # pointless to test this, if it turns on, it stays on... + # test_option_on_off n + # so just allow the tests below to verify it can be turned on + + # nothing should be executed, hence no output... + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -enc 'echo ABANDON HOPE; echo ALL YE; echo ...' + + # this is true even when the "commands" do not exist + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -enc 'ERR; FAIL; ABANDON HOPE' + + # but if there is a syntax error, it should be detected (w or w/o -e) + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -enc 'echo JUMP; for frogs swim; echo in puddles' + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -nc 'echo ABANDON HOPE; echo "ALL YE; echo ...' + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -enc 'echo ABANDON HOPE;; echo ALL YE; echo ...' + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -nc 'do YOU ABANDON HOPE; for all eternity?' + + # now test enabling -n in the middle of a script + # note that once turned on, it cannot be turned off again. + # + # omit more complex cases, as those can send some shells + # into infinite loops, and believe it or not, that might be OK! + + atf_check -s exit:0 -o match:first -o not-match:second -e empty \ + ${TEST_SH} -c 'echo first; set -n; echo second' + atf_check -s exit:0 -o match:first -o not-match:third -e empty \ + ${TEST_SH} -c 'echo first; set -n; echo second; set +n; echo third' + atf_check -s exit:0 -o inline:'a\nb\n' -e empty \ + ${TEST_SH} -c 'for x in a b c d + do + case "$x" in + a);; b);; c) set -n;; d);; + esac + printf "%s\n" "$x" + done' + + # This last one is a bit more complex to explain, so I will not try + + # First, we need to know what signal number is used for SIGUSR1 on + # the local (testing) system (signal number is $(( $XIT - 128 )) ) + + # this will take slightly over 1 second elapsed time (the sleep 1) + # The "10" for the first sleep just needs to be something big enough + # that the rest of the commands have time to complete, even on + # very slow testing systems. 10 should be enough. Otherwise irrelevant + + # The shell will usually blather to stderr about the sleep 10 being + # killed, but it affects nothing, so just allow it to cry. + + (sleep 10 & sleep 1; kill -USR1 $!; wait $!) + XIT="$?" + + # The exit value should be an integer > 128 and < 256 (often 158) + # If it is not just skip the test + + # If we do run the test, it should take (slightly over) either 1 or 2 + # seconds to complete, depending upon the shell being tested. + + case "${XIT}" in + ( 129 | 1[3-9][0-9] | 2[0-4][0-9] | 25[0-5] ) + + # The script below should exit with the same code - no output + + # Or that is the result that seems best explanable. + # "set -n" in uses like this is not exactly well defined... + + # This script comes from a member of the austin group + # (they author changes to the posix shell spec - and more.) + # The author is also an (occasional?) NetBSD user. + atf_check -s exit:${XIT} -o empty -e empty ${TEST_SH} -c ' + trap "set -n" USR1 + { sleep 1; kill -USR1 $$; sleep 1; } & + false + wait && echo t || echo f + wait + echo foo + ' + ;; + esac +} + +atf_test_case set_u +set_u_head() { + atf_set "descr" "Tests that 'set -u' turns on unset var detection " \ + "and that it behaves as defined by the standard" +} +set_u_body() { + test_option_on_off u + + # first make sure it is OK to unset an unset variable + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset _UNSET_VARIABLE_; echo OK' + # even if -u is set + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -cue \ + 'unset _UNSET_VARIABLE_; echo OK' + + # and that without -u accessing an unset variable is harmless + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset X; echo ${X}; echo OK' + # and that the unset variable test expansion works properly + atf_check -s exit:0 -o match:OKOK -e empty ${TEST_SH} -ce \ + 'unset X; printf "%s" ${X-OK}; echo OK' + + # Next test that with -u set, the shell aborts on access to unset var + # do not use -e, want to make sure it is -u that causes abort + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo ${X}; echo ERR' + # quoting should make no difference... + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo "${X}"; echo ERR' + + # Now a bunch of accesses to unset vars, with -u, in ways that are OK + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset X; set -u; echo ${X-GOOD}; echo OK' + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset X; set -u; echo ${X-OK}' + atf_check -s exit:0 -o not-match:ERR -o match:OK -e empty \ + ${TEST_SH} -ce 'unset X; set -u; echo ${X+ERR}; echo OK' + + # and some more ways that are not OK + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo ${X#foo}; echo ERR' + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo ${X%%bar}; echo ERR' + + # lastly, just while we are checking unset vars, test aborts w/o -u + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; echo ${X?}; echo ERR' + atf_check -s not-exit:0 -o not-match:ERR -e match:X_NOT_SET \ + ${TEST_SH} -c 'unset X; echo ${X?X_NOT_SET}; echo ERR' +} + +atf_test_case set_v +set_v_head() { + atf_set "descr" "Tests that 'set -v' turns on input read echoing " \ + "and that it behaves as defined by the standard" +} +set_v_body() { + test_option_on_off v + + # check that -v does nothing if no later input line is read + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e empty \ + ${TEST_SH} -ec 'printf "%s" OK; set -v; echo OK; exit 0' + + # but that it does when there are multiple lines + cat <<- 'EOF' | + set -v + printf %s OK + echo OK + exit 0 + EOF + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e match:echo \ + -e not-match:set ${TEST_SH} + + # and that it can be disabled again + cat <<- 'EOF' | + set -v + printf %s OK + set +v + echo OK + exit 0 + EOF + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e not-match:echo \ + ${TEST_SH} + + # and lastly, that shell keywords do get output when "read" + cat <<- 'EOF' | + set -v + for i in 111 222 333 + do + printf %s $i + done + exit 0 + EOF + atf_check -s exit:0 \ + -o match:111222333 -o not-match:printf \ + -o not-match:for -o not-match:do -o not-match:done \ + -e match:printf -e match:111 -e not-match:111222 \ + -e match:for -e match:do -e match:done \ + ${TEST_SH} +} + +atf_test_case set_x +set_x_head() { + atf_set "descr" "Tests that 'set -x' turns on command exec logging " \ + "and that it behaves as defined by the standard" +} +set_x_body() { + test_option_on_off x + + # check that cmd output appears after -x is enabled + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e not-match:printf -e match:OK -e match:echo \ + ${TEST_SH} -ec 'printf "%s" OK; set -x; echo OK; exit 0' + + # and that it stops again afer -x is disabled + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e not-match:echo \ + ${TEST_SH} -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0' + + # also check that PS4 is output correctly + atf_check -s exit:0 \ + -o match:OK -o not-match:echo \ + -e match:OK -e match:Run:echo \ + ${TEST_SH} -ec 'PS4=Run:; set -x; echo OK; exit 0' + + return 0 + + # This one seems controversial... I suspect it is NetBSD's sh + # that is wrong to not output "for" "while" "if" ... etc + + # and lastly, that shell keywords do not get output when "executed" + atf_check -s exit:0 \ + -o match:111222333 -o not-match:printf \ + -o not-match:for \ + -e match:printf -e match:111 -e not-match:111222 \ + -e not-match:for -e not-match:do -e not-match:done \ + ${TEST_SH} -ec \ + 'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0' +} + +opt_test_setup() +{ + test -n "$1" || { echo >&2 "Internal error"; exit 1; } + + cat > "$1" << 'END_OF_FUNCTIONS' +local_opt_check() +{ + local - +} + +instr() +{ + expr "$2" : "\(.*$1\)" >/dev/null +} + +save_opts() +{ + local - + + set -e + set -u + + instr e "$-" && instr u "$-" && return 0 + echo ERR +} + +fiddle_opts() +{ + set -e + set -u + + instr e "$-" && instr u "$-" && return 0 + echo ERR +} + +local_test() +{ + set +eu + + save_opts + instr '[eu]' "$-" || printf %s "OK" + + fiddle_opts + instr e "$-" && instr u "$-" && printf %s "OK" + + set +eu +} +END_OF_FUNCTIONS +} + +atf_test_case restore_local_opts +restore_local_opts_head() { + atf_set "descr" "Tests that 'local -' saves and restores options. " \ + "Note that "local" is a local shell addition" +} +restore_local_opts_body() { + atf_require_prog cat + atf_require_prog expr + + FN="test-funcs.$$" + opt_test_setup "${FN}" || atf_skip "Cannot setup test environment" + + ${TEST_SH} -ec ". './${FN}'; local_opt_check" 2>/dev/null || + atf_skip "sh extension 'local -' not supported by ${TEST_SH}" + + atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \ + ${TEST_SH} -ec ". './${FN}'; local_test" +} + +atf_test_case vi_emacs_VE_toggle +vi_emacs_VE_toggle_head() { + atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\ + " Note that -V and -E are local shell additions" +} +vi_emacs_VE_toggle_body() { + + test_optional_on_off V E || + atf_skip "One or both V & E opts unsupported by ${TEST_SH}" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c ' + q() { + eval "case \"$-\" in + (*${2}*) return 1;; + (*${1}*) return 0;; + esac" + return 1 + } + x() { + echo >&2 "Option set or toggle failure:" \ + " on=$1 off=$2 set=$-" + exit 1 + } + set -V; q V E || x V E + set -E; q E V || x E V + set -V; q V E || x V E + set +EV; q "" "[VE]" || x "" VE + exit 0 + ' +} + +atf_test_case xx_bogus +xx_bogus_head() { + atf_set "descr" "Tests that attempting to set a nonsense option fails." +} +xx_bogus_body() { + # Biggest problem here is picking a "nonsense option" that is + # not implemented by any shell, anywhere. Hopefully this will do. + + # 'set' is a special builtin, so a conforming shell should exit + # on an arg error, and the ERR should not be printed. + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -c 'set -% ; echo ERR' +} + +atf_test_case Option_switching +Option_switching_head() { + atf_set "descr" "options can be enabled and disabled" +} +Option_switching_body() { + + # Cannot test -m, setting it causes test shell to fail... + # (test shell gets SIGKILL!) Wonder why ... something related to atf + # That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'" + + # Don't bother testing toggling -n, once on, it stays on... + # (and because the test fn refuses to allow us to try) + + # Cannot test -o or -c here, or the extension -s + # they can only be used, not switched + + # these are the posix options, that all shells should implement + test_option_on_off a b C e f h u v x # m + + # and these are extensions that might not exist (non-fatal to test) + # -i and -s (and -c) are posix options, but are not required to + # be accessable via the "set" command, just the command line. + # We allow for -i to work with set, as that makes some sense, + # -c and -s do not. + test_optional_on_off E i I p q V || true + + # Also test (some) option combinations ... + # only testing posix options here, because it is easier... + test_option_on_off aeu vx Ca aCefux +} + +atf_init_test_cases() { + # tests are run in order sort of names produces, so choose names wisely + + # this one tests turning on/off all the mandatory. and extra flags + atf_add_test_case Option_switching + # and this tests the NetBSD "local -" functionality in functions. + atf_add_test_case restore_local_opts + + # no tests for -m (no idea how to do that one) + # -I (no easy way to generate the EOF it ignores) + # -i (not sure how to test that one at the minute) + # -p (because we aren't going to run tests setuid) + # -V/-E (too much effort, and a real test would be huge) + # -c (because almost all the other tests test it anyway) + # -q (because, for now, I am lazy) + # -s (coming soon, hopefully) + # -o (really +o: again, hopefully soon) + # -o longname (again, just laziness, don't wait...) + # -h/-b (because NetBSD doesn't implement them) + atf_add_test_case set_a + atf_add_test_case set_C + atf_add_test_case set_e + atf_add_test_case set_f + atf_add_test_case set_n + atf_add_test_case set_u + atf_add_test_case set_v + atf_add_test_case set_x + + atf_add_test_case vi_emacs_VE_toggle + atf_add_test_case xx_bogus +} |