xref: /freebsd/contrib/netbsd-tests/bin/sh/t_option.sh (revision c22165b4f1f5d38b681921797a44b3ba8c13b7e0)
1*640235e2SEnji Cooper# $NetBSD: t_option.sh,v 1.3 2016/03/08 14:19:28 christos Exp $
2*640235e2SEnji Cooper#
3*640235e2SEnji Cooper# Copyright (c) 2016 The NetBSD Foundation, Inc.
4*640235e2SEnji Cooper# All rights reserved.
5*640235e2SEnji Cooper#
6*640235e2SEnji Cooper# Redistribution and use in source and binary forms, with or without
7*640235e2SEnji Cooper# modification, are permitted provided that the following conditions
8*640235e2SEnji Cooper# are met:
9*640235e2SEnji Cooper# 1. Redistributions of source code must retain the above copyright
10*640235e2SEnji Cooper#    notice, this list of conditions and the following disclaimer.
11*640235e2SEnji Cooper# 2. Redistributions in binary form must reproduce the above copyright
12*640235e2SEnji Cooper#    notice, this list of conditions and the following disclaimer in the
13*640235e2SEnji Cooper#    documentation and/or other materials provided with the distribution.
14*640235e2SEnji Cooper#
15*640235e2SEnji Cooper# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16*640235e2SEnji Cooper# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17*640235e2SEnji Cooper# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18*640235e2SEnji Cooper# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19*640235e2SEnji Cooper# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20*640235e2SEnji Cooper# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21*640235e2SEnji Cooper# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22*640235e2SEnji Cooper# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23*640235e2SEnji Cooper# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24*640235e2SEnji Cooper# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25*640235e2SEnji Cooper# POSSIBILITY OF SUCH DAMAGE.
26*640235e2SEnji Cooper#
27*640235e2SEnji Cooper# the implementation of "sh" to test
28*640235e2SEnji Cooper: ${TEST_SH:="/bin/sh"}
29*640235e2SEnji Cooper
30*640235e2SEnji Cooper# The standard
31*640235e2SEnji Cooper# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
32*640235e2SEnji Cooper# says:
33*640235e2SEnji Cooper#	...[lots]
34*640235e2SEnji Cooper
35*640235e2SEnji Coopertest_option_on_off()
36*640235e2SEnji Cooper{
37*640235e2SEnji Cooper	atf_require_prog tr
38*640235e2SEnji Cooper
39*640235e2SEnji Cooper	for opt
40*640235e2SEnji Cooper	do
41*640235e2SEnji Cooper				# t is needed, as inside $()` $- appears to lose
42*640235e2SEnji Cooper				# the 'e' option if it happened to already be
43*640235e2SEnji Cooper				# set.  Must check if that is what should
44*640235e2SEnji Cooper				# happen, but that is a different issue.
45*640235e2SEnji Cooper
46*640235e2SEnji Cooper		test -z "${opt}" && continue
47*640235e2SEnji Cooper
48*640235e2SEnji Cooper		# if we are playing with more that one option at a
49*640235e2SEnji Cooper		# time, the code below requires that we start with no
50*640235e2SEnji Cooper		# options set, or it will mis-diagnose the situation
51*640235e2SEnji Cooper		CLEAR=''
52*640235e2SEnji Cooper		test "${#opt}" -gt 1 &&
53*640235e2SEnji Cooper  CLEAR='xx="$-" && xx=$(echo "$xx" | tr -d cs) && test -n "$xx" && set +"$xx";'
54*640235e2SEnji Cooper
55*640235e2SEnji Cooper		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
56*640235e2SEnji Cooper			"opt=${opt}"'
57*640235e2SEnji Cooper			x() {
58*640235e2SEnji Cooper				echo "ERROR: Unable to $1 option $2" >&2
59*640235e2SEnji Cooper				exit 1
60*640235e2SEnji Cooper			}
61*640235e2SEnji Cooper			s() {
62*640235e2SEnji Cooper				set -"$1"
63*640235e2SEnji Cooper				t="$-"
64*640235e2SEnji Cooper				x=$(echo "$t" | tr -d "$1")
65*640235e2SEnji Cooper				test "$t" = "$x" && x set "$1"
66*640235e2SEnji Cooper				return 0
67*640235e2SEnji Cooper			}
68*640235e2SEnji Cooper			c() {
69*640235e2SEnji Cooper				set +"$1"
70*640235e2SEnji Cooper				t="$-"
71*640235e2SEnji Cooper				x=$(echo "$t" | tr -d "$1")
72*640235e2SEnji Cooper				test "$t" != "$x" && x clear "$1"
73*640235e2SEnji Cooper				return 0
74*640235e2SEnji Cooper			}
75*640235e2SEnji Cooper			'"${CLEAR}"'
76*640235e2SEnji Cooper
77*640235e2SEnji Cooper			# if we do not do this, -x tracing splatters stderr
78*640235e2SEnji Cooper			# for some shells, -v does as well (is that correct?)
79*640235e2SEnji Cooper			case "${opt}" in
80*640235e2SEnji Cooper			(*[xv]*)	exec 2>/dev/null;;
81*640235e2SEnji Cooper			esac
82*640235e2SEnji Cooper
83*640235e2SEnji Cooper			o="$-"
84*640235e2SEnji Cooper			x=$(echo "$o" | tr -d "$opt")
85*640235e2SEnji Cooper
86*640235e2SEnji Cooper			if [ "$o" = "$x" ]; then	# option was off
87*640235e2SEnji Cooper				s "${opt}"
88*640235e2SEnji Cooper				c "${opt}"
89*640235e2SEnji Cooper			else
90*640235e2SEnji Cooper				c "${opt}"
91*640235e2SEnji Cooper				s "${opt}"
92*640235e2SEnji Cooper			fi
93*640235e2SEnji Cooper		'
94*640235e2SEnji Cooper	done
95*640235e2SEnji Cooper}
96*640235e2SEnji Cooper
97*640235e2SEnji Coopertest_optional_on_off()
98*640235e2SEnji Cooper{
99*640235e2SEnji Cooper	RET=0
100*640235e2SEnji Cooper	OPTS=
101*640235e2SEnji Cooper	for opt
102*640235e2SEnji Cooper	do
103*640235e2SEnji Cooper		test "${opt}" = n && continue
104*640235e2SEnji Cooper		${TEST_SH} -c "set -${opt}" 2>/dev/null  &&
105*640235e2SEnji Cooper			OPTS="${OPTS} ${opt}" || RET=1
106*640235e2SEnji Cooper	done
107*640235e2SEnji Cooper
108*640235e2SEnji Cooper	test -n "${OPTS}" && test_option_on_off ${OPTS}
109*640235e2SEnji Cooper
110*640235e2SEnji Cooper	return "${RET}"
111*640235e2SEnji Cooper}
112*640235e2SEnji Cooper
113*640235e2SEnji Cooperatf_test_case set_a
114*640235e2SEnji Cooperset_a_head() {
115*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -a' turns on all var export " \
116*640235e2SEnji Cooper	                "and that it behaves as defined by the standard"
117*640235e2SEnji Cooper}
118*640235e2SEnji Cooperset_a_body() {
119*640235e2SEnji Cooper	atf_require_prog env
120*640235e2SEnji Cooper	atf_require_prog grep
121*640235e2SEnji Cooper
122*640235e2SEnji Cooper	test_option_on_off a
123*640235e2SEnji Cooper
124*640235e2SEnji Cooper	# without -a, new variables should not be exported (so grep "fails")
125*640235e2SEnji Cooper	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -ce \
126*640235e2SEnji Cooper		'unset VAR; set +a; VAR=value; env | grep "^VAR="'
127*640235e2SEnji Cooper
128*640235e2SEnji Cooper	# with -a, they should be
129*640235e2SEnji Cooper	atf_check -s exit:0 -o match:VAR=value -e empty ${TEST_SH} -ce \
130*640235e2SEnji Cooper		'unset VAR; set -a; VAR=value; env | grep "^VAR="'
131*640235e2SEnji Cooper}
132*640235e2SEnji Cooper
133*640235e2SEnji Cooperatf_test_case set_C
134*640235e2SEnji Cooperset_C_head() {
135*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -C' turns on no clobber mode " \
136*640235e2SEnji Cooper	                "and that it behaves as defined by the standard"
137*640235e2SEnji Cooper}
138*640235e2SEnji Cooperset_C_body() {
139*640235e2SEnji Cooper	atf_require_prog ls
140*640235e2SEnji Cooper
141*640235e2SEnji Cooper	test_option_on_off C
142*640235e2SEnji Cooper
143*640235e2SEnji Cooper	# Check that the environment to use for the tests is sane ...
144*640235e2SEnji Cooper	# we assume current dir is a new tempory directory & is empty
145*640235e2SEnji Cooper
146*640235e2SEnji Cooper	test -z "$(ls)" || atf_skip "Test execution directory not clean"
147*640235e2SEnji Cooper	test -c "/dev/null" || atf_skip "Problem with /dev/null"
148*640235e2SEnji Cooper
149*640235e2SEnji Cooper	echo Dummy_Content > Junk_File
150*640235e2SEnji Cooper	echo Precious_Content > Important_File
151*640235e2SEnji Cooper
152*640235e2SEnji Cooper	# Check that we can redirect onto file when -C is not set
153*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
154*640235e2SEnji Cooper		'
155*640235e2SEnji Cooper		D=$(ls -l Junk_File) || exit 1
156*640235e2SEnji Cooper		set +C
157*640235e2SEnji Cooper		echo "Overwrite it now" > Junk_File
158*640235e2SEnji Cooper		A=$(ls -l Junk_File) || exit 1
159*640235e2SEnji Cooper		test "${A}" != "${D}"
160*640235e2SEnji Cooper		'
161*640235e2SEnji Cooper
162*640235e2SEnji Cooper	# Check that we cannot redirect onto file when -C is set
163*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
164*640235e2SEnji Cooper		'
165*640235e2SEnji Cooper		D=$(ls -l Important_File) || exit 1
166*640235e2SEnji Cooper		set -C
167*640235e2SEnji Cooper		echo "Fail to Overwrite it now" > Important_File
168*640235e2SEnji Cooper		A=$(ls -l Important_File) || exit 1
169*640235e2SEnji Cooper		test "${A}" = "${D}"
170*640235e2SEnji Cooper		'
171*640235e2SEnji Cooper
172*640235e2SEnji Cooper	# Check that we can append to file, even when -C is set
173*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
174*640235e2SEnji Cooper		'
175*640235e2SEnji Cooper		D=$(ls -l Junk_File) || exit 1
176*640235e2SEnji Cooper		set -C
177*640235e2SEnji Cooper		echo "Append to it now" >> Junk_File
178*640235e2SEnji Cooper		A=$(ls -l Junk_File) || exit 1
179*640235e2SEnji Cooper		test "${A}" != "${D}"
180*640235e2SEnji Cooper		'
181*640235e2SEnji Cooper
182*640235e2SEnji Cooper	# Check that we abort on attempt to redirect onto file when -Ce is set
183*640235e2SEnji Cooper	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
184*640235e2SEnji Cooper		'
185*640235e2SEnji Cooper		set -Ce
186*640235e2SEnji Cooper		echo "Fail to Overwrite it now" > Important_File
187*640235e2SEnji Cooper		echo "Should not reach this point"
188*640235e2SEnji Cooper		'
189*640235e2SEnji Cooper
190*640235e2SEnji Cooper	# Last check that we can override -C for when we really need to
191*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
192*640235e2SEnji Cooper		'
193*640235e2SEnji Cooper		D=$(ls -l Junk_File) || exit 1
194*640235e2SEnji Cooper		set -C
195*640235e2SEnji Cooper		echo "Change the poor bugger again" >| Junk_File
196*640235e2SEnji Cooper		A=$(ls -l Junk_File) || exit 1
197*640235e2SEnji Cooper		test "${A}" != "${D}"
198*640235e2SEnji Cooper		'
199*640235e2SEnji Cooper}
200*640235e2SEnji Cooper
201*640235e2SEnji Cooperatf_test_case set_e
202*640235e2SEnji Cooperset_e_head() {
203*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -e' turns on error detection " \
204*640235e2SEnji Cooper		"and that a simple case behaves as defined by the standard"
205*640235e2SEnji Cooper}
206*640235e2SEnji Cooperset_e_body() {
207*640235e2SEnji Cooper	test_option_on_off e
208*640235e2SEnji Cooper
209*640235e2SEnji Cooper	# Check that -e does nothing if no commands fail
210*640235e2SEnji Cooper	atf_check -s exit:0 -o match:I_am_OK -e empty \
211*640235e2SEnji Cooper	    ${TEST_SH} -c \
212*640235e2SEnji Cooper		'false; printf "%s" I_am; set -e; true; printf "%s\n" _OK'
213*640235e2SEnji Cooper
214*640235e2SEnji Cooper	# and that it (silently, but with exit status) aborts if cmd fails
215*640235e2SEnji Cooper	atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
216*640235e2SEnji Cooper	    ${TEST_SH} -c \
217*640235e2SEnji Cooper		'false; printf "%s" I_am; set -e; false; printf "%s\n" _Broken'
218*640235e2SEnji Cooper
219*640235e2SEnji Cooper	# same, except -e this time is on from the beginning
220*640235e2SEnji Cooper	atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
221*640235e2SEnji Cooper	    ${TEST_SH} -ec 'printf "%s" I_am; false; printf "%s\n" _Broken'
222*640235e2SEnji Cooper
223*640235e2SEnji Cooper	# More checking of -e in other places, there is lots to deal with.
224*640235e2SEnji Cooper}
225*640235e2SEnji Cooper
226*640235e2SEnji Cooperatf_test_case set_f
227*640235e2SEnji Cooperset_f_head() {
228*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -f' turns off pathname expansion " \
229*640235e2SEnji Cooper	                "and that it behaves as defined by the standard"
230*640235e2SEnji Cooper}
231*640235e2SEnji Cooperset_f_body() {
232*640235e2SEnji Cooper	atf_require_prog ls
233*640235e2SEnji Cooper
234*640235e2SEnji Cooper	test_option_on_off f
235*640235e2SEnji Cooper
236*640235e2SEnji Cooper	# Check that the environment to use for the tests is sane ...
237*640235e2SEnji Cooper	# we assume current dir is a new tempory directory & is empty
238*640235e2SEnji Cooper
239*640235e2SEnji Cooper	test -z "$(ls)" || atf_skip "Test execution directory not clean"
240*640235e2SEnji Cooper
241*640235e2SEnji Cooper	# we will assume that atf will clean up this junk directory
242*640235e2SEnji Cooper	# when we are done.   But for testing pathname expansion
243*640235e2SEnji Cooper	# we need files
244*640235e2SEnji Cooper
245*640235e2SEnji Cooper	for f in a b c d e f aa ab ac ad ae aaa aab aac aad aba abc bbb ccc
246*640235e2SEnji Cooper	do
247*640235e2SEnji Cooper		echo "$f" > "$f"
248*640235e2SEnji Cooper	done
249*640235e2SEnji Cooper
250*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \
251*640235e2SEnji Cooper	    'X=$(echo b*); Y=$(echo b*); test "${X}" != "a*";
252*640235e2SEnji Cooper		test "${X}" = "${Y}"'
253*640235e2SEnji Cooper
254*640235e2SEnji Cooper	# now test expansion is different when -f is set
255*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \
256*640235e2SEnji Cooper	   'X=$(echo b*); Y=$(set -f; echo b*); test "${X}" != "${Y}"'
257*640235e2SEnji Cooper}
258*640235e2SEnji Cooper
259*640235e2SEnji Cooperatf_test_case set_n
260*640235e2SEnji Cooperset_n_head() {
261*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -n' supresses command execution " \
262*640235e2SEnji Cooper	                "and that it behaves as defined by the standard"
263*640235e2SEnji Cooper}
264*640235e2SEnji Cooperset_n_body() {
265*640235e2SEnji Cooper	# pointless to test this, if it turns on, it stays on...
266*640235e2SEnji Cooper	# test_option_on_off n
267*640235e2SEnji Cooper	# so just allow the tests below to verify it can be turned on
268*640235e2SEnji Cooper
269*640235e2SEnji Cooper	# nothing should be executed, hence no output...
270*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty \
271*640235e2SEnji Cooper		${TEST_SH} -enc 'echo ABANDON HOPE; echo ALL YE; echo ...'
272*640235e2SEnji Cooper
273*640235e2SEnji Cooper	# this is true even when the "commands" do not exist
274*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty \
275*640235e2SEnji Cooper		${TEST_SH} -enc 'ERR; FAIL; ABANDON HOPE'
276*640235e2SEnji Cooper
277*640235e2SEnji Cooper	# but if there is a syntax error, it should be detected (w or w/o -e)
278*640235e2SEnji Cooper	atf_check -s not-exit:0 -o empty -e not-empty \
279*640235e2SEnji Cooper		${TEST_SH} -enc 'echo JUMP; for frogs swim; echo in puddles'
280*640235e2SEnji Cooper	atf_check -s not-exit:0 -o empty -e not-empty \
281*640235e2SEnji Cooper		${TEST_SH} -nc 'echo ABANDON HOPE; echo "ALL YE; echo ...'
282*640235e2SEnji Cooper	atf_check -s not-exit:0 -o empty -e not-empty \
283*640235e2SEnji Cooper		${TEST_SH} -enc 'echo ABANDON HOPE;; echo ALL YE; echo ...'
284*640235e2SEnji Cooper	atf_check -s not-exit:0 -o empty -e not-empty \
285*640235e2SEnji Cooper		${TEST_SH} -nc 'do YOU ABANDON HOPE; for all eternity?'
286*640235e2SEnji Cooper
287*640235e2SEnji Cooper	# now test enabling -n in the middle of a script
288*640235e2SEnji Cooper	# note that once turned on, it cannot be turned off again.
289*640235e2SEnji Cooper	#
290*640235e2SEnji Cooper	# omit more complex cases, as those can send some shells
291*640235e2SEnji Cooper	# into infinite loops, and believe it or not, that might be OK!
292*640235e2SEnji Cooper
293*640235e2SEnji Cooper	atf_check -s exit:0 -o match:first -o not-match:second -e empty \
294*640235e2SEnji Cooper		${TEST_SH} -c 'echo first; set -n; echo second'
295*640235e2SEnji Cooper	atf_check -s exit:0 -o match:first -o not-match:third -e empty \
296*640235e2SEnji Cooper	    ${TEST_SH} -c 'echo first; set -n; echo second; set +n; echo third'
297*640235e2SEnji Cooper	atf_check -s exit:0 -o inline:'a\nb\n' -e empty \
298*640235e2SEnji Cooper	    ${TEST_SH} -c 'for x in a b c d
299*640235e2SEnji Cooper			   do
300*640235e2SEnji Cooper				case "$x" in
301*640235e2SEnji Cooper				     a);; b);; c) set -n;; d);;
302*640235e2SEnji Cooper				esac
303*640235e2SEnji Cooper				printf "%s\n" "$x"
304*640235e2SEnji Cooper			   done'
305*640235e2SEnji Cooper
306*640235e2SEnji Cooper	# This last one is a bit more complex to explain, so I will not try
307*640235e2SEnji Cooper
308*640235e2SEnji Cooper	# First, we need to know what signal number is used for SIGUSR1 on
309*640235e2SEnji Cooper	# the local (testing) system (signal number is $(( $XIT - 128 )) )
310*640235e2SEnji Cooper
311*640235e2SEnji Cooper	# this will take slightly over 1 second elapsed time (the sleep 1)
312*640235e2SEnji Cooper	# The "10" for the first sleep just needs to be something big enough
313*640235e2SEnji Cooper	# that the rest of the commands have time to complete, even on
314*640235e2SEnji Cooper	# very slow testing systems.  10 should be enough.  Otherwise irrelevant
315*640235e2SEnji Cooper
316*640235e2SEnji Cooper	# The shell will usually blather to stderr about the sleep 10 being
317*640235e2SEnji Cooper	# killed, but it affects nothing, so just allow it to cry.
318*640235e2SEnji Cooper
319*640235e2SEnji Cooper	(sleep 10 & sleep 1; kill -USR1 $!; wait $!)
320*640235e2SEnji Cooper	XIT="$?"
321*640235e2SEnji Cooper
322*640235e2SEnji Cooper	# The exit value should be an integer > 128 and < 256 (often 158)
323*640235e2SEnji Cooper	# If it is not just skip the test
324*640235e2SEnji Cooper
325*640235e2SEnji Cooper	# If we do run the test, it should take (slightly over) either 1 or 2
326*640235e2SEnji Cooper	# seconds to complete, depending upon the shell being tested.
327*640235e2SEnji Cooper
328*640235e2SEnji Cooper	case "${XIT}" in
329*640235e2SEnji Cooper	( 129 | 1[3-9][0-9] | 2[0-4][0-9] | 25[0-5] )
330*640235e2SEnji Cooper
331*640235e2SEnji Cooper		# The script below should exit with the same code - no output
332*640235e2SEnji Cooper
333*640235e2SEnji Cooper		# Or that is the result that seems best explanable.
334*640235e2SEnji Cooper		# "set -n" in uses like this is not exactly well defined...
335*640235e2SEnji Cooper
336*640235e2SEnji Cooper		# This script comes from a member of the austin group
337*640235e2SEnji Cooper		# (they author changes to the posix shell spec - and more.)
338*640235e2SEnji Cooper		# The author is also an (occasional?) NetBSD user.
339*640235e2SEnji Cooper		atf_check -s exit:${XIT} -o empty -e empty ${TEST_SH} -c '
340*640235e2SEnji Cooper			trap "set -n" USR1
341*640235e2SEnji Cooper			{ sleep 1; kill -USR1 $$; sleep 1; } &
342*640235e2SEnji Cooper			false
343*640235e2SEnji Cooper			wait && echo t || echo f
344*640235e2SEnji Cooper			wait
345*640235e2SEnji Cooper			echo foo
346*640235e2SEnji Cooper		'
347*640235e2SEnji Cooper		;;
348*640235e2SEnji Cooper	esac
349*640235e2SEnji Cooper}
350*640235e2SEnji Cooper
351*640235e2SEnji Cooperatf_test_case set_u
352*640235e2SEnji Cooperset_u_head() {
353*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -u' turns on unset var detection " \
354*640235e2SEnji Cooper	                "and that it behaves as defined by the standard"
355*640235e2SEnji Cooper}
356*640235e2SEnji Cooperset_u_body() {
357*640235e2SEnji Cooper	test_option_on_off u
358*640235e2SEnji Cooper
359*640235e2SEnji Cooper	# first make sure it is OK to unset an unset variable
360*640235e2SEnji Cooper	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
361*640235e2SEnji Cooper		'unset _UNSET_VARIABLE_; echo OK'
362*640235e2SEnji Cooper	# even if -u is set
363*640235e2SEnji Cooper	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -cue \
364*640235e2SEnji Cooper		'unset _UNSET_VARIABLE_; echo OK'
365*640235e2SEnji Cooper
366*640235e2SEnji Cooper	# and that without -u accessing an unset variable is harmless
367*640235e2SEnji Cooper	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
368*640235e2SEnji Cooper		'unset X; echo ${X}; echo OK'
369*640235e2SEnji Cooper	# and that the unset variable test expansion works properly
370*640235e2SEnji Cooper	atf_check -s exit:0 -o match:OKOK -e empty ${TEST_SH} -ce \
371*640235e2SEnji Cooper		'unset X; printf "%s" ${X-OK}; echo OK'
372*640235e2SEnji Cooper
373*640235e2SEnji Cooper	# Next test that with -u set, the shell aborts on access to unset var
374*640235e2SEnji Cooper	# do not use -e, want to make sure it is -u that causes abort
375*640235e2SEnji Cooper	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
376*640235e2SEnji Cooper		'unset X; set -u; echo ${X}; echo ERR'
377*640235e2SEnji Cooper	# quoting should make no difference...
378*640235e2SEnji Cooper	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
379*640235e2SEnji Cooper		'unset X; set -u; echo "${X}"; echo ERR'
380*640235e2SEnji Cooper
381*640235e2SEnji Cooper	# Now a bunch of accesses to unset vars, with -u, in ways that are OK
382*640235e2SEnji Cooper	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
383*640235e2SEnji Cooper		'unset X; set -u; echo ${X-GOOD}; echo OK'
384*640235e2SEnji Cooper	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
385*640235e2SEnji Cooper		'unset X; set -u; echo ${X-OK}'
386*640235e2SEnji Cooper	atf_check -s exit:0 -o not-match:ERR -o match:OK -e empty \
387*640235e2SEnji Cooper		${TEST_SH} -ce 'unset X; set -u; echo ${X+ERR}; echo OK'
388*640235e2SEnji Cooper
389*640235e2SEnji Cooper	# and some more ways that are not OK
390*640235e2SEnji Cooper	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
391*640235e2SEnji Cooper		'unset X; set -u; echo ${X#foo}; echo ERR'
392*640235e2SEnji Cooper	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
393*640235e2SEnji Cooper		'unset X; set -u; echo ${X%%bar}; echo ERR'
394*640235e2SEnji Cooper
395*640235e2SEnji Cooper	# lastly, just while we are checking unset vars, test aborts w/o -u
396*640235e2SEnji Cooper	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
397*640235e2SEnji Cooper		'unset X; echo ${X?}; echo ERR'
398*640235e2SEnji Cooper	atf_check -s not-exit:0 -o not-match:ERR -e match:X_NOT_SET \
399*640235e2SEnji Cooper		${TEST_SH} -c 'unset X; echo ${X?X_NOT_SET}; echo ERR'
400*640235e2SEnji Cooper}
401*640235e2SEnji Cooper
402*640235e2SEnji Cooperatf_test_case set_v
403*640235e2SEnji Cooperset_v_head() {
404*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -v' turns on input read echoing " \
405*640235e2SEnji Cooper	                "and that it behaves as defined by the standard"
406*640235e2SEnji Cooper}
407*640235e2SEnji Cooperset_v_body() {
408*640235e2SEnji Cooper	test_option_on_off v
409*640235e2SEnji Cooper
410*640235e2SEnji Cooper	# check that -v does nothing if no later input line is read
411*640235e2SEnji Cooper	atf_check -s exit:0 \
412*640235e2SEnji Cooper			-o match:OKOK -o not-match:echo -o not-match:printf \
413*640235e2SEnji Cooper			-e empty \
414*640235e2SEnji Cooper		${TEST_SH} -ec 'printf "%s" OK; set -v; echo OK; exit 0'
415*640235e2SEnji Cooper
416*640235e2SEnji Cooper	# but that it does when there are multiple lines
417*640235e2SEnji Cooper	cat <<- 'EOF' |
418*640235e2SEnji Cooper		set -v
419*640235e2SEnji Cooper		printf %s OK
420*640235e2SEnji Cooper		echo OK
421*640235e2SEnji Cooper		exit 0
422*640235e2SEnji Cooper	EOF
423*640235e2SEnji Cooper	atf_check -s exit:0 \
424*640235e2SEnji Cooper			-o match:OKOK -o not-match:echo -o not-match:printf \
425*640235e2SEnji Cooper			-e match:printf -e match:OK -e match:echo \
426*640235e2SEnji Cooper			-e not-match:set ${TEST_SH}
427*640235e2SEnji Cooper
428*640235e2SEnji Cooper	# and that it can be disabled again
429*640235e2SEnji Cooper	cat <<- 'EOF' |
430*640235e2SEnji Cooper		set -v
431*640235e2SEnji Cooper		printf %s OK
432*640235e2SEnji Cooper		set +v
433*640235e2SEnji Cooper		echo OK
434*640235e2SEnji Cooper		exit 0
435*640235e2SEnji Cooper	EOF
436*640235e2SEnji Cooper	atf_check -s exit:0 \
437*640235e2SEnji Cooper			-o match:OKOK -o not-match:echo -o not-match:printf \
438*640235e2SEnji Cooper			-e match:printf -e match:OK -e not-match:echo \
439*640235e2SEnji Cooper				${TEST_SH}
440*640235e2SEnji Cooper
441*640235e2SEnji Cooper	# and lastly, that shell keywords do get output when "read"
442*640235e2SEnji Cooper	cat <<- 'EOF' |
443*640235e2SEnji Cooper		set -v
444*640235e2SEnji Cooper		for i in 111 222 333
445*640235e2SEnji Cooper		do
446*640235e2SEnji Cooper			printf %s $i
447*640235e2SEnji Cooper		done
448*640235e2SEnji Cooper		exit 0
449*640235e2SEnji Cooper	EOF
450*640235e2SEnji Cooper	atf_check -s exit:0 \
451*640235e2SEnji Cooper			-o match:111222333 -o not-match:printf \
452*640235e2SEnji Cooper			-o not-match:for -o not-match:do -o not-match:done \
453*640235e2SEnji Cooper			-e match:printf -e match:111 -e not-match:111222 \
454*640235e2SEnji Cooper			-e match:for -e match:do -e match:done \
455*640235e2SEnji Cooper				${TEST_SH}
456*640235e2SEnji Cooper}
457*640235e2SEnji Cooper
458*640235e2SEnji Cooperatf_test_case set_x
459*640235e2SEnji Cooperset_x_head() {
460*640235e2SEnji Cooper	atf_set "descr" "Tests that 'set -x' turns on command exec logging " \
461*640235e2SEnji Cooper	                "and that it behaves as defined by the standard"
462*640235e2SEnji Cooper}
463*640235e2SEnji Cooperset_x_body() {
464*640235e2SEnji Cooper	test_option_on_off x
465*640235e2SEnji Cooper
466*640235e2SEnji Cooper	# check that cmd output appears after -x is enabled
467*640235e2SEnji Cooper	atf_check -s exit:0 \
468*640235e2SEnji Cooper			-o match:OKOK -o not-match:echo -o not-match:printf \
469*640235e2SEnji Cooper			-e not-match:printf -e match:OK -e match:echo \
470*640235e2SEnji Cooper		${TEST_SH} -ec 'printf "%s" OK; set -x; echo OK; exit 0'
471*640235e2SEnji Cooper
472*640235e2SEnji Cooper	# and that it stops again afer -x is disabled
473*640235e2SEnji Cooper	atf_check -s exit:0 \
474*640235e2SEnji Cooper			-o match:OKOK -o not-match:echo -o not-match:printf \
475*640235e2SEnji Cooper			-e match:printf -e match:OK -e not-match:echo \
476*640235e2SEnji Cooper	    ${TEST_SH} -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0'
477*640235e2SEnji Cooper
478*640235e2SEnji Cooper	# also check that PS4 is output correctly
479*640235e2SEnji Cooper	atf_check -s exit:0 \
480*640235e2SEnji Cooper			-o match:OK -o not-match:echo \
481*640235e2SEnji Cooper			-e match:OK -e match:Run:echo \
482*640235e2SEnji Cooper		${TEST_SH} -ec 'PS4=Run:; set -x; echo OK; exit 0'
483*640235e2SEnji Cooper
484*640235e2SEnji Cooper	return 0
485*640235e2SEnji Cooper
486*640235e2SEnji Cooper	# This one seems controversial... I suspect it is NetBSD's sh
487*640235e2SEnji Cooper	# that is wrong to not output "for" "while" "if" ... etc
488*640235e2SEnji Cooper
489*640235e2SEnji Cooper	# and lastly, that shell keywords do not get output when "executed"
490*640235e2SEnji Cooper	atf_check -s exit:0 \
491*640235e2SEnji Cooper			-o match:111222333 -o not-match:printf \
492*640235e2SEnji Cooper			-o not-match:for \
493*640235e2SEnji Cooper			-e match:printf -e match:111 -e not-match:111222 \
494*640235e2SEnji Cooper			-e not-match:for -e not-match:do -e not-match:done \
495*640235e2SEnji Cooper		${TEST_SH} -ec \
496*640235e2SEnji Cooper	   'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0'
497*640235e2SEnji Cooper}
498*640235e2SEnji Cooper
499*640235e2SEnji Cooperopt_test_setup()
500*640235e2SEnji Cooper{
501*640235e2SEnji Cooper	test -n "$1" || { echo >&2 "Internal error"; exit 1; }
502*640235e2SEnji Cooper
503*640235e2SEnji Cooper	cat > "$1" << 'END_OF_FUNCTIONS'
504*640235e2SEnji Cooperlocal_opt_check()
505*640235e2SEnji Cooper{
506*640235e2SEnji Cooper	local -
507*640235e2SEnji Cooper}
508*640235e2SEnji Cooper
509*640235e2SEnji Cooperinstr()
510*640235e2SEnji Cooper{
511*640235e2SEnji Cooper	expr "$2" : "\(.*$1\)" >/dev/null
512*640235e2SEnji Cooper}
513*640235e2SEnji Cooper
514*640235e2SEnji Coopersave_opts()
515*640235e2SEnji Cooper{
516*640235e2SEnji Cooper	local -
517*640235e2SEnji Cooper
518*640235e2SEnji Cooper	set -e
519*640235e2SEnji Cooper	set -u
520*640235e2SEnji Cooper
521*640235e2SEnji Cooper	instr e "$-" && instr u "$-" && return 0
522*640235e2SEnji Cooper	echo ERR
523*640235e2SEnji Cooper}
524*640235e2SEnji Cooper
525*640235e2SEnji Cooperfiddle_opts()
526*640235e2SEnji Cooper{
527*640235e2SEnji Cooper	set -e
528*640235e2SEnji Cooper	set -u
529*640235e2SEnji Cooper
530*640235e2SEnji Cooper	instr e "$-" && instr u "$-" && return 0
531*640235e2SEnji Cooper	echo ERR
532*640235e2SEnji Cooper}
533*640235e2SEnji Cooper
534*640235e2SEnji Cooperlocal_test()
535*640235e2SEnji Cooper{
536*640235e2SEnji Cooper	set +eu
537*640235e2SEnji Cooper
538*640235e2SEnji Cooper	save_opts
539*640235e2SEnji Cooper	instr '[eu]' "$-" || printf %s "OK"
540*640235e2SEnji Cooper
541*640235e2SEnji Cooper	fiddle_opts
542*640235e2SEnji Cooper	instr e "$-" && instr u "$-" && printf %s "OK"
543*640235e2SEnji Cooper
544*640235e2SEnji Cooper	set +eu
545*640235e2SEnji Cooper}
546*640235e2SEnji CooperEND_OF_FUNCTIONS
547*640235e2SEnji Cooper}
548*640235e2SEnji Cooper
549*640235e2SEnji Cooperatf_test_case restore_local_opts
550*640235e2SEnji Cooperrestore_local_opts_head() {
551*640235e2SEnji Cooper	atf_set "descr" "Tests that 'local -' saves and restores options.  " \
552*640235e2SEnji Cooper			"Note that "local" is a local shell addition"
553*640235e2SEnji Cooper}
554*640235e2SEnji Cooperrestore_local_opts_body() {
555*640235e2SEnji Cooper	atf_require_prog cat
556*640235e2SEnji Cooper	atf_require_prog expr
557*640235e2SEnji Cooper
558*640235e2SEnji Cooper	FN="test-funcs.$$"
559*640235e2SEnji Cooper	opt_test_setup "${FN}" || atf_skip "Cannot setup test environment"
560*640235e2SEnji Cooper
561*640235e2SEnji Cooper	${TEST_SH} -ec ". './${FN}'; local_opt_check" 2>/dev/null ||
562*640235e2SEnji Cooper		atf_skip "sh extension 'local -' not supported by ${TEST_SH}"
563*640235e2SEnji Cooper
564*640235e2SEnji Cooper	atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \
565*640235e2SEnji Cooper		${TEST_SH} -ec ". './${FN}'; local_test"
566*640235e2SEnji Cooper}
567*640235e2SEnji Cooper
568*640235e2SEnji Cooperatf_test_case vi_emacs_VE_toggle
569*640235e2SEnji Coopervi_emacs_VE_toggle_head() {
570*640235e2SEnji Cooper	atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\
571*640235e2SEnji Cooper			"  Note that -V and -E are local shell additions"
572*640235e2SEnji Cooper}
573*640235e2SEnji Coopervi_emacs_VE_toggle_body() {
574*640235e2SEnji Cooper
575*640235e2SEnji Cooper	test_optional_on_off V E ||
576*640235e2SEnji Cooper	  atf_skip "One or both V & E opts unsupported by ${TEST_SH}"
577*640235e2SEnji Cooper
578*640235e2SEnji Cooper	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '
579*640235e2SEnji Cooper		q() {
580*640235e2SEnji Cooper			eval "case \"$-\" in
581*640235e2SEnji Cooper			(*${2}*)	return 1;;
582*640235e2SEnji Cooper			(*${1}*)	return 0;;
583*640235e2SEnji Cooper			esac"
584*640235e2SEnji Cooper			return 1
585*640235e2SEnji Cooper		}
586*640235e2SEnji Cooper		x() {
587*640235e2SEnji Cooper			echo >&2 "Option set or toggle failure:" \
588*640235e2SEnji Cooper					" on=$1 off=$2 set=$-"
589*640235e2SEnji Cooper			exit 1
590*640235e2SEnji Cooper		}
591*640235e2SEnji Cooper		set -V; q V E || x V E
592*640235e2SEnji Cooper		set -E; q E V || x E V
593*640235e2SEnji Cooper		set -V; q V E || x V E
594*640235e2SEnji Cooper		set +EV; q "" "[VE]" || x "" VE
595*640235e2SEnji Cooper		exit 0
596*640235e2SEnji Cooper	'
597*640235e2SEnji Cooper}
598*640235e2SEnji Cooper
599*640235e2SEnji Cooperatf_test_case xx_bogus
600*640235e2SEnji Cooperxx_bogus_head() {
601*640235e2SEnji Cooper	atf_set "descr" "Tests that attempting to set a nonsense option fails."
602*640235e2SEnji Cooper}
603*640235e2SEnji Cooperxx_bogus_body() {
604*640235e2SEnji Cooper	# Biggest problem here is picking a "nonsense option" that is
605*640235e2SEnji Cooper	# not implemented by any shell, anywhere.  Hopefully this will do.
606*640235e2SEnji Cooper
607*640235e2SEnji Cooper	# 'set' is a special builtin, so a conforming shell should exit
608*640235e2SEnji Cooper	# on an arg error, and the ERR should not be printed.
609*640235e2SEnji Cooper	atf_check -s not-exit:0 -o empty -e not-empty \
610*640235e2SEnji Cooper		${TEST_SH} -c 'set -% ; echo ERR'
611*640235e2SEnji Cooper}
612*640235e2SEnji Cooper
613*640235e2SEnji Cooperatf_test_case Option_switching
614*640235e2SEnji CooperOption_switching_head() {
615*640235e2SEnji Cooper	atf_set "descr" "options can be enabled and disabled"
616*640235e2SEnji Cooper}
617*640235e2SEnji CooperOption_switching_body() {
618*640235e2SEnji Cooper
619*640235e2SEnji Cooper	# Cannot test -m, setting it causes test shell to fail...
620*640235e2SEnji Cooper	# (test shell gets SIGKILL!)  Wonder why ... something related to atf
621*640235e2SEnji Cooper	# That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'"
622*640235e2SEnji Cooper
623*640235e2SEnji Cooper	# Don't bother testing toggling -n, once on, it stays on...
624*640235e2SEnji Cooper	# (and because the test fn refuses to allow us to try)
625*640235e2SEnji Cooper
626*640235e2SEnji Cooper	# Cannot test -o or -c here, or the extension -s
627*640235e2SEnji Cooper	# they can only be used, not switched
628*640235e2SEnji Cooper
629*640235e2SEnji Cooper	# these are the posix options, that all shells should implement
630*640235e2SEnji Cooper	test_option_on_off a b C e f h u v x      # m
631*640235e2SEnji Cooper
632*640235e2SEnji Cooper	# and these are extensions that might not exist (non-fatal to test)
633*640235e2SEnji Cooper	# -i and -s (and -c) are posix options, but are not required to
634*640235e2SEnji Cooper	# be accessable via the "set" command, just the command line.
635*640235e2SEnji Cooper	# We allow for -i to work with set, as that makes some sense,
636*640235e2SEnji Cooper	# -c and -s do not.
637*640235e2SEnji Cooper	test_optional_on_off E i I p q V || true
638*640235e2SEnji Cooper
639*640235e2SEnji Cooper	# Also test (some) option combinations ...
640*640235e2SEnji Cooper	# only testing posix options here, because it is easier...
641*640235e2SEnji Cooper	test_option_on_off aeu vx Ca aCefux
642*640235e2SEnji Cooper}
643*640235e2SEnji Cooper
644*640235e2SEnji Cooperatf_init_test_cases() {
645*640235e2SEnji Cooper	# tests are run in order sort of names produces, so choose names wisely
646*640235e2SEnji Cooper
647*640235e2SEnji Cooper	# this one tests turning on/off all the mandatory. and extra flags
648*640235e2SEnji Cooper	atf_add_test_case Option_switching
649*640235e2SEnji Cooper	# and this tests the NetBSD "local -" functionality in functions.
650*640235e2SEnji Cooper	atf_add_test_case restore_local_opts
651*640235e2SEnji Cooper
652*640235e2SEnji Cooper	# no tests for	-m (no idea how to do that one)
653*640235e2SEnji Cooper	#		-I (no easy way to generate the EOF it ignores)
654*640235e2SEnji Cooper	#		-i (not sure how to test that one at the minute)
655*640235e2SEnji Cooper	#		-p (because we aren't going to run tests setuid)
656*640235e2SEnji Cooper	#		-V/-E (too much effort, and a real test would be huge)
657*640235e2SEnji Cooper	#		-c (because almost all the other tests test it anyway)
658*640235e2SEnji Cooper	#		-q (because, for now, I am lazy)
659*640235e2SEnji Cooper	#		-s (coming soon, hopefully)
660*640235e2SEnji Cooper	#		-o (really +o: again, hopefully soon)
661*640235e2SEnji Cooper	#		-o longname (again, just laziness, don't wait...)
662*640235e2SEnji Cooper	# 		-h/-b (because NetBSD doesn't implement them)
663*640235e2SEnji Cooper	atf_add_test_case set_a
664*640235e2SEnji Cooper	atf_add_test_case set_C
665*640235e2SEnji Cooper	atf_add_test_case set_e
666*640235e2SEnji Cooper	atf_add_test_case set_f
667*640235e2SEnji Cooper	atf_add_test_case set_n
668*640235e2SEnji Cooper	atf_add_test_case set_u
669*640235e2SEnji Cooper	atf_add_test_case set_v
670*640235e2SEnji Cooper	atf_add_test_case set_x
671*640235e2SEnji Cooper
672*640235e2SEnji Cooper	atf_add_test_case vi_emacs_VE_toggle
673*640235e2SEnji Cooper	atf_add_test_case xx_bogus
674*640235e2SEnji Cooper}
675