Home | History | Annotate | Line # | Download | only in sh
      1 # $NetBSD: t_set_e.sh,v 1.4 2016/03/31 16:22:27 christos Exp $
      2 #
      3 # Copyright (c) 2007 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 
     28 # references:
     29 #   http://www.opengroup.org/onlinepubs/009695399/utilities/set.html
     30 #   http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
     31 
     32 # the implementation of "sh" to test
     33 : ${TEST_SH:="/bin/sh"}
     34 
     35 failwith()
     36 {
     37 	case "$SH_FAILS" in
     38 		"") SH_FAILS=`echo "$1"`;;
     39 		*) SH_FAILS="$SH_FAILS"`echo; echo "$1"`;;
     40 	esac
     41 }
     42 
     43 check1()
     44 {
     45 	#echo "$TEST_SH -c $1"
     46 	result=`$TEST_SH -c "$1" 2>/dev/null | tr '\n' ' ' | sed 's/ *$//'`
     47 	if [ "$result" != "$2" ]; then
     48 		MSG=`printf "%-56s %-8s  %s" "$3" "$result" "$2"`
     49 		failwith "$MSG"
     50 		failcount=`expr $failcount + 1`
     51 	fi
     52 	count=`expr $count + 1`
     53 }
     54 
     55 # direct check: try the given expression.
     56 dcheck()
     57 {
     58 	check1 "$1" "$2" "$1"
     59 }
     60 
     61 # eval check: indirect through eval.
     62 # as of this writing, this changes the behavior pretty drastically and
     63 # is thus important to test. (PR bin/29861)
     64 echeck()
     65 {
     66 	check1 'eval '"'( $1 )'" "$2" "eval '($1)'"
     67 }
     68 
     69 atf_test_case all
     70 all_head() {
     71 	atf_set "descr" "Tests that 'set -e' works correctly"
     72 }
     73 all_body() {
     74 	count=0
     75 	failcount=0
     76 
     77 	# make sure exiting from a subshell behaves as expected
     78 	dcheck '(set -e; exit 1; echo ERR$?); echo OK$?' 'OK1'
     79 	echeck '(set -e; exit 1; echo ERR$?); echo OK$?' 'OK1'
     80 
     81 	# first, check basic functioning.
     82 	# The ERR shouldn't print; the result of the () should be 1.
     83 	# Henceforth we'll assume that we don't need to check $?.
     84 	dcheck '(set -e; false; echo ERR$?); echo OK$?' 'OK1'
     85 	echeck '(set -e; false; echo ERR$?); echo OK$?' 'OK1'
     86 
     87 	# these cases should be equivalent to the preceding.
     88 	dcheck '(set -e; /nonexistent; echo ERR); echo OK' 'OK'
     89 	echeck '(set -e; /nonexistent; echo ERR); echo OK' 'OK'
     90 	dcheck '(set -e; nonexistent-program-on-path; echo ERR); echo OK' 'OK'
     91 	echeck '(set -e; nonexistent-program-on-path; echo ERR); echo OK' 'OK'
     92 	dcheck 'f() { false; }; (set -e; f; echo ERR); echo OK' 'OK'
     93 	echeck 'f() { false; }; (set -e; f; echo ERR); echo OK' 'OK'
     94 	dcheck 'f() { return 1; }; (set -e; f; echo ERR); echo OK' 'OK'
     95 	echeck 'f() { return 1; }; (set -e; f; echo ERR); echo OK' 'OK'
     96 
     97 	# but! with set -e, the false should cause an *immediate* exit.
     98 	# The return form should not, as such, but there's no way to
     99 	# distinguish it.
    100 	dcheck 'f() { false; echo ERR; }; (set -e; f); echo OK' 'OK'
    101 	echeck 'f() { false; echo ERR; }; (set -e; f); echo OK' 'OK'
    102 
    103 	# set is not scoped, so these should not exit at all.
    104 	dcheck 'f() { set +e; false; echo OK; }; (set -e; f); echo OK' 'OK OK'
    105 	echeck 'f() { set +e; false; echo OK; }; (set -e; f); echo OK' 'OK OK'
    106 
    107 	# according to the standard, only failing *simple* commands
    108 	# cause an exit under -e. () is not a simple command.
    109 	#   Correct (per POSIX):
    110 	#dcheck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK OK'
    111 	#echeck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK OK'
    112 	#   Wrong current behavior:
    113 	dcheck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK'
    114 	echeck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK'
    115 
    116 	# make sure an inner nested shell does exit though.
    117 	dcheck '(set -e; (false; echo ERR)); echo OK' 'OK'
    118 
    119 	# The left hand side of an || or && is explicitly tested and
    120 	# thus should not cause an exit. Furthermore, because a || or
    121 	# && expression is not a simple command, there should be no
    122 	# exit even if the overall result is false.
    123 	dcheck '(set -e; false || true; echo OK); echo OK' 'OK OK'
    124 	echeck '(set -e; false || true; echo OK); echo OK' 'OK OK'
    125 	dcheck '(set -e; false && true; echo OK); echo OK' 'OK OK'
    126 	echeck '(set -e; false && true; echo OK); echo OK' 'OK OK'
    127 
    128 	# However, the right hand side is not tested, so a failure
    129 	# there *should* cause an exit, regardless of whether it
    130 	# appears inside a non-simple command.
    131 	#
    132 	# Note that in at least one place the standard does not
    133 	# distinguish between the left and right hand sides of
    134 	# logical operators. It is possible that for strict
    135 	# compliance these need to not exit; however, if so that
    136 	# should probably be limited to when some strict-posix setting
    137 	# is in effect and tested accordingly.
    138 	#
    139 	dcheck '(set -e; false || false; echo ERR); echo OK' 'OK'
    140 	dcheck '(set -e; true && false; echo ERR); echo OK' 'OK'
    141 	echeck '(set -e; false || false; echo ERR); echo OK' 'OK'
    142 	echeck '(set -e; true && false; echo ERR); echo OK' 'OK'
    143 
    144 	# correct:
    145 	#dcheck '(set -e; false && false; echo ERR); echo OK' 'OK'
    146 	#echeck '(set -e; false && false; echo ERR); echo OK' 'OK'
    147 
    148 	# wrong current behavior:
    149 	dcheck '(set -e; false && false; echo ERR); echo OK' 'ERR OK'
    150 	echeck '(set -e; false && false; echo ERR); echo OK' 'ERR OK'
    151 
    152 	# A failure that is not reached because of short-circuit
    153 	# evaluation should not cause an exit, however.
    154 	dcheck '(set -e; true || false; echo OK); echo OK' 'OK OK'
    155 	echeck '(set -e; true || false; echo OK); echo OK' 'OK OK'
    156 
    157 	# For completeness, test the other two combinations.
    158 	dcheck '(set -e; true || true; echo OK); echo OK' 'OK OK'
    159 	dcheck '(set -e; true && true; echo OK); echo OK' 'OK OK'
    160 	echeck '(set -e; true || true; echo OK); echo OK' 'OK OK'
    161 	echeck '(set -e; true && true; echo OK); echo OK' 'OK OK'
    162 
    163 	# likewise, none of these should exit.
    164 	dcheck '(set -e; while false; do :; done; echo OK); echo OK' 'OK OK'
    165 	dcheck '(set -e; if false; then :; fi; echo OK); echo OK' 'OK OK'
    166 	# problematic :-)
    167 	#dcheck '(set -e; until false; do :; done; echo OK); echo OK' 'OK OK'
    168 	dcheck '(set -e; until [ "$t" = 1 ]; do t=1; done; echo OK); echo OK' \
    169 	  'OK OK'
    170 	echeck '(set -e; while false; do :; done; echo OK); echo OK' 'OK OK'
    171 	echeck '(set -e; if false; then :; fi; echo OK); echo OK' 'OK OK'
    172 	echeck '(set -e; until [ "$t" = 1 ]; do t=1; done; echo OK); echo OK' \
    173 	  'OK OK'
    174 
    175 	# the bang operator tests its argument and thus the argument
    176 	# should not cause an exit. it is also not a simple command (I
    177 	# believe) so it also shouldn't exit even if it yields a false
    178 	# result.
    179 	dcheck '(set -e; ! false; echo OK); echo OK' 'OK OK'
    180 	dcheck '(set -e; ! true; echo OK); echo OK' 'OK OK'
    181 	echeck '(set -e; ! false; echo OK); echo OK' 'OK OK'
    182 	echeck '(set -e; ! true; echo OK); echo OK' 'OK OK'
    183 
    184 	# combined case with () and &&; the inner expression is false
    185 	# but does not itself exit, and the () should not cause an 
    186 	# exit even when failing.
    187 	# correct:
    188 	#dcheck '(set -e; (false && true); echo OK); echo OK' 'OK OK'
    189 	#echeck '(set -e; (false && true); echo OK); echo OK' 'OK OK'
    190 	# wrong current behavior:
    191 	dcheck '(set -e; (false && true); echo OK); echo OK' 'OK'
    192 	echeck '(set -e; (false && true); echo OK); echo OK' 'OK'
    193 
    194 	# pipelines. only the right-hand end is significant.
    195 	dcheck '(set -e; false | true; echo OK); echo OK' 'OK OK'
    196 	echeck '(set -e; false | true; echo OK); echo OK' 'OK OK'
    197 	dcheck '(set -e; true | false; echo ERR); echo OK' 'OK'
    198 	echeck '(set -e; true | false; echo ERR); echo OK' 'OK'
    199 
    200 	dcheck '(set -e; while true | false; do :; done; echo OK); echo OK' \
    201 	    'OK OK'
    202 	dcheck '(set -e; if true | false; then :; fi; echo OK); echo OK' \
    203 	    'OK OK'
    204 
    205 
    206 	# According to dsl@ in PR bin/32282, () is not defined as a
    207 	# subshell, only as a grouping operator [and a scope, I guess]
    208 
    209 	#		(This is incorrect.   () is definitely a sub-shell)
    210 
    211 	# so the nested false ought to cause the whole shell to exit,
    212 	# not just the subshell. dholland@ would like to see C&V,
    213 	# because that seems like a bad idea. (Among other things, it
    214 	# would break all the above test logic, which relies on being
    215 	# able to isolate set -e behavior inside ().) However, I'm
    216 	# going to put these tests here to make sure the issue gets
    217 	# dealt with sometime.
    218 	#
    219 	# XXX: the second set has been disabled in the name of making
    220 	# all tests "pass".
    221 	#
    222 	# As they should be, they are utter nonsense.
    223 
    224 	# 1. error if the whole shell exits (current correct behavior)
    225 	dcheck 'echo OK; (set -e; false); echo OK' 'OK OK'
    226 	echeck 'echo OK; (set -e; false); echo OK' 'OK OK'
    227 	# 2. error if the whole shell does not exit (dsl's suggested behavior)
    228 	#dcheck 'echo OK; (set -e; false); echo ERR' 'OK'
    229 	#echeck 'echo OK; (set -e; false); echo ERR' 'OK'
    230 
    231 	# The current behavior of the shell is that it exits out as
    232 	# far as -e is set and then stops. This is probably a
    233 	# consequence of it handling () wrong, but it's a somewhat
    234 	# curious compromise position between 1. and 2. above.
    235 	dcheck '(set -e; (false; echo ERR); echo ERR); echo OK' 'OK'
    236 	echeck '(set -e; (false; echo ERR); echo ERR); echo OK' 'OK'
    237 
    238 	# backquote expansion (PR bin/17514)
    239 
    240 	# (in-)correct
    241 	#dcheck '(set -e; echo ERR `false`; echo ERR); echo OK' 'OK'
    242 	#dcheck '(set -e; echo ERR $(false); echo ERR); echo OK' 'OK'
    243 	#dcheck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'OK'
    244 	#dcheck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'OK'
    245 	# Not-wrong current behavior
    246 	# the exit status of ommand substitution is ignored in most cases
    247 	# None of these should be causing the shell to exit.
    248 	dcheck '(set -e; echo ERR `false`; echo ERR); echo OK' 'ERR ERR OK'
    249 	dcheck '(set -e; echo ERR $(false); echo ERR); echo OK' 'ERR ERR OK'
    250 	dcheck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'ERR ERR OK'
    251 	dcheck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'ERR ERR OK'
    252 
    253 	# This is testing one case (the case?) where the exit status is used
    254 	dcheck '(set -e; x=`false`; echo ERR); echo OK' 'OK'
    255 	dcheck '(set -e; x=$(false); echo ERR); echo OK' 'OK'
    256 	dcheck '(set -e; x=`exit 3`; echo ERR); echo OK' 'OK'
    257 	dcheck '(set -e; x=$(exit 3); echo ERR); echo OK' 'OK'
    258 
    259 	# correct (really just commented out incorrect nonsense)
    260 	#echeck '(set -e; echo ERR `false`; echo ERR); echo OK' 'OK'
    261 	#echeck '(set -e; echo ERR $(false); echo ERR); echo OK' 'OK'
    262 	#echeck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'OK'
    263 	#echeck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'OK'
    264 
    265 	# not-wrong current behavior (as above)
    266 	echeck '(set -e; echo ERR `false`; echo ERR); echo OK' 'ERR ERR OK'
    267 	echeck '(set -e; echo ERR $(false); echo ERR); echo OK' 'ERR ERR OK'
    268 	echeck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'ERR ERR OK'
    269 	echeck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'ERR ERR OK'
    270 
    271 	echeck '(set -e; x=`false`; echo ERR); echo OK' 'OK'
    272 	echeck '(set -e; x=$(false); echo ERR); echo OK' 'OK'
    273 	echeck '(set -e; x=`exit 3`; echo ERR); echo OK' 'OK'
    274 	echeck '(set -e; x=$(exit 3); echo ERR); echo OK' 'OK'
    275 
    276 	# shift (PR bin/37493)
    277 	# correct
    278 	# Actually, both ways are correct, both are permitted
    279 	#dcheck '(set -e; shift || true; echo OK); echo OK' 'OK OK'
    280 	#echeck '(set -e; shift || true; echo OK); echo OK' 'OK OK'
    281 	# (not-) wrong current behavior
    282 	#dcheck '(set -e; shift || true; echo OK); echo OK' 'OK'
    283 	#echeck '(set -e; shift || true; echo OK); echo OK' 'OK'
    284 
    285 	# what is wrong is this test assuming one behaviour or the other
    286 	# (and incidentally this has nothing whatever to do with "-e",
    287 	# the test should really be moved elsewhere...)
    288 	# But for now, leave it here, and correct it:
    289 	dcheck '(set -e; shift && echo OK); echo OK' 'OK'
    290 	echeck '(set -e; shift && echo OK); echo OK' 'OK'
    291 
    292 	# Done.
    293 
    294 	if [ "x$SH_FAILS" != x ]; then
    295 	    printf '%-56s %-8s  %s\n' "Expression" "Result" "Should be"
    296 	    echo "$SH_FAILS"
    297 	    atf_fail "$failcount of $count failed cases"
    298 	else
    299 	    atf_pass
    300 	fi
    301 }
    302 
    303 atf_init_test_cases() {
    304 	atf_add_test_case all
    305 }
    306