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 35failwith() 36{ 37 case "$SH_FAILS" in 38 "") SH_FAILS=`echo "$1"`;; 39 *) SH_FAILS="$SH_FAILS"`echo; echo "$1"`;; 40 esac 41} 42 43check1() 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. 56dcheck() 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) 64echeck() 65{ 66 check1 'eval '"'( $1 )'" "$2" "eval '($1)'" 67} 68 69atf_test_case all 70all_head() { 71 atf_set "descr" "Tests that 'set -e' works correctly" 72} 73all_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 303atf_init_test_cases() { 304 atf_add_test_case all 305} 306