t_set_e.sh revision 313498
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