1313498Sngie# $NetBSD: t_set_e.sh,v 1.4 2016/03/31 16:22:27 christos Exp $ 2272343Sngie# 3272343Sngie# Copyright (c) 2007 The NetBSD Foundation, Inc. 4272343Sngie# All rights reserved. 5272343Sngie# 6272343Sngie# Redistribution and use in source and binary forms, with or without 7272343Sngie# modification, are permitted provided that the following conditions 8272343Sngie# are met: 9272343Sngie# 1. Redistributions of source code must retain the above copyright 10272343Sngie# notice, this list of conditions and the following disclaimer. 11272343Sngie# 2. Redistributions in binary form must reproduce the above copyright 12272343Sngie# notice, this list of conditions and the following disclaimer in the 13272343Sngie# documentation and/or other materials provided with the distribution. 14272343Sngie# 15272343Sngie# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 16272343Sngie# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 17272343Sngie# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18272343Sngie# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19272343Sngie# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20272343Sngie# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21272343Sngie# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22272343Sngie# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23272343Sngie# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24272343Sngie# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25272343Sngie# POSSIBILITY OF SUCH DAMAGE. 26272343Sngie# 27272343Sngie 28272343Sngie# references: 29272343Sngie# http://www.opengroup.org/onlinepubs/009695399/utilities/set.html 30272343Sngie# http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html 31272343Sngie 32272343Sngie# the implementation of "sh" to test 33313498Sngie: ${TEST_SH:="/bin/sh"} 34272343Sngie 35272343Sngiefailwith() 36272343Sngie{ 37272343Sngie case "$SH_FAILS" in 38272343Sngie "") SH_FAILS=`echo "$1"`;; 39272343Sngie *) SH_FAILS="$SH_FAILS"`echo; echo "$1"`;; 40272343Sngie esac 41272343Sngie} 42272343Sngie 43272343Sngiecheck1() 44272343Sngie{ 45272343Sngie #echo "$TEST_SH -c $1" 46272343Sngie result=`$TEST_SH -c "$1" 2>/dev/null | tr '\n' ' ' | sed 's/ *$//'` 47272343Sngie if [ "$result" != "$2" ]; then 48272343Sngie MSG=`printf "%-56s %-8s %s" "$3" "$result" "$2"` 49272343Sngie failwith "$MSG" 50272343Sngie failcount=`expr $failcount + 1` 51272343Sngie fi 52272343Sngie count=`expr $count + 1` 53272343Sngie} 54272343Sngie 55272343Sngie# direct check: try the given expression. 56272343Sngiedcheck() 57272343Sngie{ 58272343Sngie check1 "$1" "$2" "$1" 59272343Sngie} 60272343Sngie 61272343Sngie# eval check: indirect through eval. 62272343Sngie# as of this writing, this changes the behavior pretty drastically and 63272343Sngie# is thus important to test. (PR bin/29861) 64272343Sngieecheck() 65272343Sngie{ 66313498Sngie check1 'eval '"'( $1 )'" "$2" "eval '($1)'" 67272343Sngie} 68272343Sngie 69272343Sngieatf_test_case all 70272343Sngieall_head() { 71272343Sngie atf_set "descr" "Tests that 'set -e' works correctly" 72272343Sngie} 73272343Sngieall_body() { 74272343Sngie count=0 75272343Sngie failcount=0 76272343Sngie 77272343Sngie # make sure exiting from a subshell behaves as expected 78272343Sngie dcheck '(set -e; exit 1; echo ERR$?); echo OK$?' 'OK1' 79272343Sngie echeck '(set -e; exit 1; echo ERR$?); echo OK$?' 'OK1' 80272343Sngie 81272343Sngie # first, check basic functioning. 82272343Sngie # The ERR shouldn't print; the result of the () should be 1. 83272343Sngie # Henceforth we'll assume that we don't need to check $?. 84313498Sngie dcheck '(set -e; false; echo ERR$?); echo OK$?' 'OK1' 85313498Sngie echeck '(set -e; false; echo ERR$?); echo OK$?' 'OK1' 86272343Sngie 87272343Sngie # these cases should be equivalent to the preceding. 88272343Sngie dcheck '(set -e; /nonexistent; echo ERR); echo OK' 'OK' 89272343Sngie echeck '(set -e; /nonexistent; echo ERR); echo OK' 'OK' 90272343Sngie dcheck '(set -e; nonexistent-program-on-path; echo ERR); echo OK' 'OK' 91272343Sngie echeck '(set -e; nonexistent-program-on-path; echo ERR); echo OK' 'OK' 92272343Sngie dcheck 'f() { false; }; (set -e; f; echo ERR); echo OK' 'OK' 93272343Sngie echeck 'f() { false; }; (set -e; f; echo ERR); echo OK' 'OK' 94272343Sngie dcheck 'f() { return 1; }; (set -e; f; echo ERR); echo OK' 'OK' 95272343Sngie echeck 'f() { return 1; }; (set -e; f; echo ERR); echo OK' 'OK' 96272343Sngie 97272343Sngie # but! with set -e, the false should cause an *immediate* exit. 98272343Sngie # The return form should not, as such, but there's no way to 99272343Sngie # distinguish it. 100272343Sngie dcheck 'f() { false; echo ERR; }; (set -e; f); echo OK' 'OK' 101272343Sngie echeck 'f() { false; echo ERR; }; (set -e; f); echo OK' 'OK' 102272343Sngie 103272343Sngie # set is not scoped, so these should not exit at all. 104272343Sngie dcheck 'f() { set +e; false; echo OK; }; (set -e; f); echo OK' 'OK OK' 105272343Sngie echeck 'f() { set +e; false; echo OK; }; (set -e; f); echo OK' 'OK OK' 106272343Sngie 107272343Sngie # according to the standard, only failing *simple* commands 108272343Sngie # cause an exit under -e. () is not a simple command. 109272343Sngie # Correct (per POSIX): 110272343Sngie #dcheck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK OK' 111272343Sngie #echeck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK OK' 112272343Sngie # Wrong current behavior: 113272343Sngie dcheck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK' 114272343Sngie echeck '(set -e; (set +e; false; echo OK; false); echo OK)' 'OK' 115272343Sngie 116272343Sngie # make sure an inner nested shell does exit though. 117272343Sngie dcheck '(set -e; (false; echo ERR)); echo OK' 'OK' 118272343Sngie 119272343Sngie # The left hand side of an || or && is explicitly tested and 120272343Sngie # thus should not cause an exit. Furthermore, because a || or 121272343Sngie # && expression is not a simple command, there should be no 122272343Sngie # exit even if the overall result is false. 123272343Sngie dcheck '(set -e; false || true; echo OK); echo OK' 'OK OK' 124272343Sngie echeck '(set -e; false || true; echo OK); echo OK' 'OK OK' 125272343Sngie dcheck '(set -e; false && true; echo OK); echo OK' 'OK OK' 126272343Sngie echeck '(set -e; false && true; echo OK); echo OK' 'OK OK' 127272343Sngie 128272343Sngie # However, the right hand side is not tested, so a failure 129272343Sngie # there *should* cause an exit, regardless of whether it 130272343Sngie # appears inside a non-simple command. 131272343Sngie # 132272343Sngie # Note that in at least one place the standard does not 133272343Sngie # distinguish between the left and right hand sides of 134272343Sngie # logical operators. It is possible that for strict 135272343Sngie # compliance these need to not exit; however, if so that 136272343Sngie # should probably be limited to when some strict-posix setting 137272343Sngie # is in effect and tested accordingly. 138272343Sngie # 139272343Sngie dcheck '(set -e; false || false; echo ERR); echo OK' 'OK' 140272343Sngie dcheck '(set -e; true && false; echo ERR); echo OK' 'OK' 141272343Sngie echeck '(set -e; false || false; echo ERR); echo OK' 'OK' 142272343Sngie echeck '(set -e; true && false; echo ERR); echo OK' 'OK' 143272343Sngie 144272343Sngie # correct: 145272343Sngie #dcheck '(set -e; false && false; echo ERR); echo OK' 'OK' 146272343Sngie #echeck '(set -e; false && false; echo ERR); echo OK' 'OK' 147272343Sngie 148272343Sngie # wrong current behavior: 149272343Sngie dcheck '(set -e; false && false; echo ERR); echo OK' 'ERR OK' 150272343Sngie echeck '(set -e; false && false; echo ERR); echo OK' 'ERR OK' 151272343Sngie 152272343Sngie # A failure that is not reached because of short-circuit 153272343Sngie # evaluation should not cause an exit, however. 154272343Sngie dcheck '(set -e; true || false; echo OK); echo OK' 'OK OK' 155272343Sngie echeck '(set -e; true || false; echo OK); echo OK' 'OK OK' 156272343Sngie 157272343Sngie # For completeness, test the other two combinations. 158272343Sngie dcheck '(set -e; true || true; echo OK); echo OK' 'OK OK' 159272343Sngie dcheck '(set -e; true && true; echo OK); echo OK' 'OK OK' 160272343Sngie echeck '(set -e; true || true; echo OK); echo OK' 'OK OK' 161272343Sngie echeck '(set -e; true && true; echo OK); echo OK' 'OK OK' 162272343Sngie 163272343Sngie # likewise, none of these should exit. 164272343Sngie dcheck '(set -e; while false; do :; done; echo OK); echo OK' 'OK OK' 165272343Sngie dcheck '(set -e; if false; then :; fi; echo OK); echo OK' 'OK OK' 166272343Sngie # problematic :-) 167272343Sngie #dcheck '(set -e; until false; do :; done; echo OK); echo OK' 'OK OK' 168272343Sngie dcheck '(set -e; until [ "$t" = 1 ]; do t=1; done; echo OK); echo OK' \ 169272343Sngie 'OK OK' 170272343Sngie echeck '(set -e; while false; do :; done; echo OK); echo OK' 'OK OK' 171272343Sngie echeck '(set -e; if false; then :; fi; echo OK); echo OK' 'OK OK' 172272343Sngie echeck '(set -e; until [ "$t" = 1 ]; do t=1; done; echo OK); echo OK' \ 173272343Sngie 'OK OK' 174272343Sngie 175272343Sngie # the bang operator tests its argument and thus the argument 176272343Sngie # should not cause an exit. it is also not a simple command (I 177272343Sngie # believe) so it also shouldn't exit even if it yields a false 178272343Sngie # result. 179272343Sngie dcheck '(set -e; ! false; echo OK); echo OK' 'OK OK' 180272343Sngie dcheck '(set -e; ! true; echo OK); echo OK' 'OK OK' 181272343Sngie echeck '(set -e; ! false; echo OK); echo OK' 'OK OK' 182272343Sngie echeck '(set -e; ! true; echo OK); echo OK' 'OK OK' 183272343Sngie 184272343Sngie # combined case with () and &&; the inner expression is false 185272343Sngie # but does not itself exit, and the () should not cause an 186272343Sngie # exit even when failing. 187272343Sngie # correct: 188272343Sngie #dcheck '(set -e; (false && true); echo OK); echo OK' 'OK OK' 189272343Sngie #echeck '(set -e; (false && true); echo OK); echo OK' 'OK OK' 190272343Sngie # wrong current behavior: 191272343Sngie dcheck '(set -e; (false && true); echo OK); echo OK' 'OK' 192272343Sngie echeck '(set -e; (false && true); echo OK); echo OK' 'OK' 193272343Sngie 194272343Sngie # pipelines. only the right-hand end is significant. 195272343Sngie dcheck '(set -e; false | true; echo OK); echo OK' 'OK OK' 196272343Sngie echeck '(set -e; false | true; echo OK); echo OK' 'OK OK' 197272343Sngie dcheck '(set -e; true | false; echo ERR); echo OK' 'OK' 198272343Sngie echeck '(set -e; true | false; echo ERR); echo OK' 'OK' 199272343Sngie 200272343Sngie dcheck '(set -e; while true | false; do :; done; echo OK); echo OK' \ 201272343Sngie 'OK OK' 202272343Sngie dcheck '(set -e; if true | false; then :; fi; echo OK); echo OK' \ 203272343Sngie 'OK OK' 204272343Sngie 205272343Sngie 206272343Sngie # According to dsl@ in PR bin/32282, () is not defined as a 207272343Sngie # subshell, only as a grouping operator [and a scope, I guess] 208313498Sngie 209313498Sngie # (This is incorrect. () is definitely a sub-shell) 210313498Sngie 211272343Sngie # so the nested false ought to cause the whole shell to exit, 212272343Sngie # not just the subshell. dholland@ would like to see C&V, 213272343Sngie # because that seems like a bad idea. (Among other things, it 214272343Sngie # would break all the above test logic, which relies on being 215272343Sngie # able to isolate set -e behavior inside ().) However, I'm 216272343Sngie # going to put these tests here to make sure the issue gets 217272343Sngie # dealt with sometime. 218272343Sngie # 219272343Sngie # XXX: the second set has been disabled in the name of making 220272343Sngie # all tests "pass". 221313498Sngie # 222313498Sngie # As they should be, they are utter nonsense. 223272343Sngie 224313498Sngie # 1. error if the whole shell exits (current correct behavior) 225272343Sngie dcheck 'echo OK; (set -e; false); echo OK' 'OK OK' 226272343Sngie echeck 'echo OK; (set -e; false); echo OK' 'OK OK' 227272343Sngie # 2. error if the whole shell does not exit (dsl's suggested behavior) 228272343Sngie #dcheck 'echo OK; (set -e; false); echo ERR' 'OK' 229272343Sngie #echeck 'echo OK; (set -e; false); echo ERR' 'OK' 230272343Sngie 231272343Sngie # The current behavior of the shell is that it exits out as 232272343Sngie # far as -e is set and then stops. This is probably a 233272343Sngie # consequence of it handling () wrong, but it's a somewhat 234272343Sngie # curious compromise position between 1. and 2. above. 235272343Sngie dcheck '(set -e; (false; echo ERR); echo ERR); echo OK' 'OK' 236272343Sngie echeck '(set -e; (false; echo ERR); echo ERR); echo OK' 'OK' 237272343Sngie 238272343Sngie # backquote expansion (PR bin/17514) 239272343Sngie 240313498Sngie # (in-)correct 241272343Sngie #dcheck '(set -e; echo ERR `false`; echo ERR); echo OK' 'OK' 242272343Sngie #dcheck '(set -e; echo ERR $(false); echo ERR); echo OK' 'OK' 243272343Sngie #dcheck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'OK' 244272343Sngie #dcheck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'OK' 245313498Sngie # Not-wrong current behavior 246313498Sngie # the exit status of ommand substitution is ignored in most cases 247313498Sngie # None of these should be causing the shell to exit. 248272343Sngie dcheck '(set -e; echo ERR `false`; echo ERR); echo OK' 'ERR ERR OK' 249272343Sngie dcheck '(set -e; echo ERR $(false); echo ERR); echo OK' 'ERR ERR OK' 250272343Sngie dcheck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'ERR ERR OK' 251272343Sngie dcheck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'ERR ERR OK' 252272343Sngie 253313498Sngie # This is testing one case (the case?) where the exit status is used 254272343Sngie dcheck '(set -e; x=`false`; echo ERR); echo OK' 'OK' 255272343Sngie dcheck '(set -e; x=$(false); echo ERR); echo OK' 'OK' 256272343Sngie dcheck '(set -e; x=`exit 3`; echo ERR); echo OK' 'OK' 257272343Sngie dcheck '(set -e; x=$(exit 3); echo ERR); echo OK' 'OK' 258272343Sngie 259313498Sngie # correct (really just commented out incorrect nonsense) 260272343Sngie #echeck '(set -e; echo ERR `false`; echo ERR); echo OK' 'OK' 261272343Sngie #echeck '(set -e; echo ERR $(false); echo ERR); echo OK' 'OK' 262272343Sngie #echeck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'OK' 263272343Sngie #echeck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'OK' 264272343Sngie 265313498Sngie # not-wrong current behavior (as above) 266272343Sngie echeck '(set -e; echo ERR `false`; echo ERR); echo OK' 'ERR ERR OK' 267272343Sngie echeck '(set -e; echo ERR $(false); echo ERR); echo OK' 'ERR ERR OK' 268272343Sngie echeck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'ERR ERR OK' 269272343Sngie echeck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'ERR ERR OK' 270272343Sngie 271272343Sngie echeck '(set -e; x=`false`; echo ERR); echo OK' 'OK' 272272343Sngie echeck '(set -e; x=$(false); echo ERR); echo OK' 'OK' 273272343Sngie echeck '(set -e; x=`exit 3`; echo ERR); echo OK' 'OK' 274272343Sngie echeck '(set -e; x=$(exit 3); echo ERR); echo OK' 'OK' 275272343Sngie 276272343Sngie # shift (PR bin/37493) 277272343Sngie # correct 278313498Sngie # Actually, both ways are correct, both are permitted 279272343Sngie #dcheck '(set -e; shift || true; echo OK); echo OK' 'OK OK' 280272343Sngie #echeck '(set -e; shift || true; echo OK); echo OK' 'OK OK' 281313498Sngie # (not-) wrong current behavior 282313498Sngie #dcheck '(set -e; shift || true; echo OK); echo OK' 'OK' 283313498Sngie #echeck '(set -e; shift || true; echo OK); echo OK' 'OK' 284272343Sngie 285313498Sngie # what is wrong is this test assuming one behaviour or the other 286313498Sngie # (and incidentally this has nothing whatever to do with "-e", 287313498Sngie # the test should really be moved elsewhere...) 288313498Sngie # But for now, leave it here, and correct it: 289313498Sngie dcheck '(set -e; shift && echo OK); echo OK' 'OK' 290313498Sngie echeck '(set -e; shift && echo OK); echo OK' 'OK' 291313498Sngie 292272343Sngie # Done. 293272343Sngie 294272343Sngie if [ "x$SH_FAILS" != x ]; then 295272343Sngie printf '%-56s %-8s %s\n' "Expression" "Result" "Should be" 296272343Sngie echo "$SH_FAILS" 297272343Sngie atf_fail "$failcount of $count failed cases" 298272343Sngie else 299272343Sngie atf_pass 300272343Sngie fi 301272343Sngie} 302272343Sngie 303272343Sngieatf_init_test_cases() { 304272343Sngie atf_add_test_case all 305272343Sngie} 306