1303980Sngie# $NetBSD: t_option.sh,v 1.3 2016/03/08 14:19:28 christos Exp $
2303980Sngie#
3303980Sngie# Copyright (c) 2016 The NetBSD Foundation, Inc.
4303980Sngie# All rights reserved.
5303980Sngie#
6303980Sngie# Redistribution and use in source and binary forms, with or without
7303980Sngie# modification, are permitted provided that the following conditions
8303980Sngie# are met:
9303980Sngie# 1. Redistributions of source code must retain the above copyright
10303980Sngie#    notice, this list of conditions and the following disclaimer.
11303980Sngie# 2. Redistributions in binary form must reproduce the above copyright
12303980Sngie#    notice, this list of conditions and the following disclaimer in the
13303980Sngie#    documentation and/or other materials provided with the distribution.
14303980Sngie#
15303980Sngie# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16303980Sngie# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17303980Sngie# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18303980Sngie# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19303980Sngie# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20303980Sngie# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21303980Sngie# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22303980Sngie# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23303980Sngie# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24303980Sngie# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25303980Sngie# POSSIBILITY OF SUCH DAMAGE.
26303980Sngie#
27303980Sngie# the implementation of "sh" to test
28303980Sngie: ${TEST_SH:="/bin/sh"}
29303980Sngie
30303980Sngie# The standard
31303980Sngie# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
32303980Sngie# says:
33303980Sngie#	...[lots]
34303980Sngie
35303980Sngietest_option_on_off()
36303980Sngie{
37303980Sngie	atf_require_prog tr
38303980Sngie
39303980Sngie	for opt
40303980Sngie	do
41303980Sngie				# t is needed, as inside $()` $- appears to lose
42303980Sngie				# the 'e' option if it happened to already be
43303980Sngie				# set.  Must check if that is what should
44303980Sngie				# happen, but that is a different issue.
45303980Sngie
46303980Sngie		test -z "${opt}" && continue
47303980Sngie
48303980Sngie		# if we are playing with more that one option at a
49303980Sngie		# time, the code below requires that we start with no
50303980Sngie		# options set, or it will mis-diagnose the situation
51303980Sngie		CLEAR=''
52303980Sngie		test "${#opt}" -gt 1 &&
53303980Sngie  CLEAR='xx="$-" && xx=$(echo "$xx" | tr -d cs) && test -n "$xx" && set +"$xx";'
54303980Sngie
55303980Sngie		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
56303980Sngie			"opt=${opt}"'
57303980Sngie			x() {
58303980Sngie				echo "ERROR: Unable to $1 option $2" >&2
59303980Sngie				exit 1
60303980Sngie			}
61303980Sngie			s() {
62303980Sngie				set -"$1"
63303980Sngie				t="$-"
64303980Sngie				x=$(echo "$t" | tr -d "$1")
65303980Sngie				test "$t" = "$x" && x set "$1"
66303980Sngie				return 0
67303980Sngie			}
68303980Sngie			c() {
69303980Sngie				set +"$1"
70303980Sngie				t="$-"
71303980Sngie				x=$(echo "$t" | tr -d "$1")
72303980Sngie				test "$t" != "$x" && x clear "$1"
73303980Sngie				return 0
74303980Sngie			}
75303980Sngie			'"${CLEAR}"'
76303980Sngie
77303980Sngie			# if we do not do this, -x tracing splatters stderr
78303980Sngie			# for some shells, -v does as well (is that correct?)
79303980Sngie			case "${opt}" in
80303980Sngie			(*[xv]*)	exec 2>/dev/null;;
81303980Sngie			esac
82303980Sngie
83303980Sngie			o="$-"
84303980Sngie			x=$(echo "$o" | tr -d "$opt")
85303980Sngie
86303980Sngie			if [ "$o" = "$x" ]; then	# option was off
87303980Sngie				s "${opt}"
88303980Sngie				c "${opt}"
89303980Sngie			else
90303980Sngie				c "${opt}"
91303980Sngie				s "${opt}"
92303980Sngie			fi
93303980Sngie		'
94303980Sngie	done
95303980Sngie}
96303980Sngie
97303980Sngietest_optional_on_off()
98303980Sngie{
99303980Sngie	RET=0
100303980Sngie	OPTS=
101303980Sngie	for opt
102303980Sngie	do
103303980Sngie		test "${opt}" = n && continue
104303980Sngie		${TEST_SH} -c "set -${opt}" 2>/dev/null  &&
105303980Sngie			OPTS="${OPTS} ${opt}" || RET=1
106303980Sngie	done
107303980Sngie
108303980Sngie	test -n "${OPTS}" && test_option_on_off ${OPTS}
109303980Sngie
110303980Sngie	return "${RET}"
111303980Sngie}
112303980Sngie
113303980Sngieatf_test_case set_a
114303980Sngieset_a_head() {
115303980Sngie	atf_set "descr" "Tests that 'set -a' turns on all var export " \
116303980Sngie	                "and that it behaves as defined by the standard"
117303980Sngie}
118303980Sngieset_a_body() {
119303980Sngie	atf_require_prog env
120303980Sngie	atf_require_prog grep
121303980Sngie
122303980Sngie	test_option_on_off a
123303980Sngie
124303980Sngie	# without -a, new variables should not be exported (so grep "fails")
125303980Sngie	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -ce \
126303980Sngie		'unset VAR; set +a; VAR=value; env | grep "^VAR="'
127303980Sngie
128303980Sngie	# with -a, they should be
129303980Sngie	atf_check -s exit:0 -o match:VAR=value -e empty ${TEST_SH} -ce \
130303980Sngie		'unset VAR; set -a; VAR=value; env | grep "^VAR="'
131303980Sngie}
132303980Sngie
133303980Sngieatf_test_case set_C
134303980Sngieset_C_head() {
135303980Sngie	atf_set "descr" "Tests that 'set -C' turns on no clobber mode " \
136303980Sngie	                "and that it behaves as defined by the standard"
137303980Sngie}
138303980Sngieset_C_body() {
139303980Sngie	atf_require_prog ls
140303980Sngie
141303980Sngie	test_option_on_off C
142303980Sngie
143303980Sngie	# Check that the environment to use for the tests is sane ...
144303980Sngie	# we assume current dir is a new tempory directory & is empty
145303980Sngie
146303980Sngie	test -z "$(ls)" || atf_skip "Test execution directory not clean"
147303980Sngie	test -c "/dev/null" || atf_skip "Problem with /dev/null"
148303980Sngie
149303980Sngie	echo Dummy_Content > Junk_File
150303980Sngie	echo Precious_Content > Important_File
151303980Sngie
152303980Sngie	# Check that we can redirect onto file when -C is not set
153303980Sngie	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
154303980Sngie		'
155303980Sngie		D=$(ls -l Junk_File) || exit 1
156303980Sngie		set +C
157303980Sngie		echo "Overwrite it now" > Junk_File
158303980Sngie		A=$(ls -l Junk_File) || exit 1
159303980Sngie		test "${A}" != "${D}"
160303980Sngie		'
161303980Sngie
162303980Sngie	# Check that we cannot redirect onto file when -C is set
163303980Sngie	atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
164303980Sngie		'
165303980Sngie		D=$(ls -l Important_File) || exit 1
166303980Sngie		set -C
167303980Sngie		echo "Fail to Overwrite it now" > Important_File
168303980Sngie		A=$(ls -l Important_File) || exit 1
169303980Sngie		test "${A}" = "${D}"
170303980Sngie		'
171303980Sngie
172303980Sngie	# Check that we can append to file, even when -C is set
173303980Sngie	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
174303980Sngie		'
175303980Sngie		D=$(ls -l Junk_File) || exit 1
176303980Sngie		set -C
177303980Sngie		echo "Append to it now" >> Junk_File
178303980Sngie		A=$(ls -l Junk_File) || exit 1
179303980Sngie		test "${A}" != "${D}"
180303980Sngie		'
181303980Sngie
182303980Sngie	# Check that we abort on attempt to redirect onto file when -Ce is set
183303980Sngie	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
184303980Sngie		'
185303980Sngie		set -Ce
186303980Sngie		echo "Fail to Overwrite it now" > Important_File
187303980Sngie		echo "Should not reach this point"
188303980Sngie		'
189303980Sngie
190303980Sngie	# Last check that we can override -C for when we really need to
191303980Sngie	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
192303980Sngie		'
193303980Sngie		D=$(ls -l Junk_File) || exit 1
194303980Sngie		set -C
195303980Sngie		echo "Change the poor bugger again" >| Junk_File
196303980Sngie		A=$(ls -l Junk_File) || exit 1
197303980Sngie		test "${A}" != "${D}"
198303980Sngie		'
199303980Sngie}
200303980Sngie
201303980Sngieatf_test_case set_e
202303980Sngieset_e_head() {
203303980Sngie	atf_set "descr" "Tests that 'set -e' turns on error detection " \
204303980Sngie		"and that a simple case behaves as defined by the standard"
205303980Sngie}
206303980Sngieset_e_body() {
207303980Sngie	test_option_on_off e
208303980Sngie
209303980Sngie	# Check that -e does nothing if no commands fail
210303980Sngie	atf_check -s exit:0 -o match:I_am_OK -e empty \
211303980Sngie	    ${TEST_SH} -c \
212303980Sngie		'false; printf "%s" I_am; set -e; true; printf "%s\n" _OK'
213303980Sngie
214303980Sngie	# and that it (silently, but with exit status) aborts if cmd fails
215303980Sngie	atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
216303980Sngie	    ${TEST_SH} -c \
217303980Sngie		'false; printf "%s" I_am; set -e; false; printf "%s\n" _Broken'
218303980Sngie
219303980Sngie	# same, except -e this time is on from the beginning
220303980Sngie	atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
221303980Sngie	    ${TEST_SH} -ec 'printf "%s" I_am; false; printf "%s\n" _Broken'
222303980Sngie
223303980Sngie	# More checking of -e in other places, there is lots to deal with.
224303980Sngie}
225303980Sngie
226303980Sngieatf_test_case set_f
227303980Sngieset_f_head() {
228303980Sngie	atf_set "descr" "Tests that 'set -f' turns off pathname expansion " \
229303980Sngie	                "and that it behaves as defined by the standard"
230303980Sngie}
231303980Sngieset_f_body() {
232303980Sngie	atf_require_prog ls
233303980Sngie
234303980Sngie	test_option_on_off f
235303980Sngie
236303980Sngie	# Check that the environment to use for the tests is sane ...
237303980Sngie	# we assume current dir is a new tempory directory & is empty
238303980Sngie
239303980Sngie	test -z "$(ls)" || atf_skip "Test execution directory not clean"
240303980Sngie
241303980Sngie	# we will assume that atf will clean up this junk directory
242303980Sngie	# when we are done.   But for testing pathname expansion
243303980Sngie	# we need files
244303980Sngie
245303980Sngie	for f in a b c d e f aa ab ac ad ae aaa aab aac aad aba abc bbb ccc
246303980Sngie	do
247303980Sngie		echo "$f" > "$f"
248303980Sngie	done
249303980Sngie
250303980Sngie	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \
251303980Sngie	    'X=$(echo b*); Y=$(echo b*); test "${X}" != "a*";
252303980Sngie		test "${X}" = "${Y}"'
253303980Sngie
254303980Sngie	# now test expansion is different when -f is set
255303980Sngie	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \
256303980Sngie	   'X=$(echo b*); Y=$(set -f; echo b*); test "${X}" != "${Y}"'
257303980Sngie}
258303980Sngie
259303980Sngieatf_test_case set_n
260303980Sngieset_n_head() {
261303980Sngie	atf_set "descr" "Tests that 'set -n' supresses command execution " \
262303980Sngie	                "and that it behaves as defined by the standard"
263303980Sngie}
264303980Sngieset_n_body() {
265303980Sngie	# pointless to test this, if it turns on, it stays on...
266303980Sngie	# test_option_on_off n
267303980Sngie	# so just allow the tests below to verify it can be turned on
268303980Sngie
269303980Sngie	# nothing should be executed, hence no output...
270303980Sngie	atf_check -s exit:0 -o empty -e empty \
271303980Sngie		${TEST_SH} -enc 'echo ABANDON HOPE; echo ALL YE; echo ...'
272303980Sngie
273303980Sngie	# this is true even when the "commands" do not exist
274303980Sngie	atf_check -s exit:0 -o empty -e empty \
275303980Sngie		${TEST_SH} -enc 'ERR; FAIL; ABANDON HOPE'
276303980Sngie
277303980Sngie	# but if there is a syntax error, it should be detected (w or w/o -e)
278303980Sngie	atf_check -s not-exit:0 -o empty -e not-empty \
279303980Sngie		${TEST_SH} -enc 'echo JUMP; for frogs swim; echo in puddles'
280303980Sngie	atf_check -s not-exit:0 -o empty -e not-empty \
281303980Sngie		${TEST_SH} -nc 'echo ABANDON HOPE; echo "ALL YE; echo ...'
282303980Sngie	atf_check -s not-exit:0 -o empty -e not-empty \
283303980Sngie		${TEST_SH} -enc 'echo ABANDON HOPE;; echo ALL YE; echo ...'
284303980Sngie	atf_check -s not-exit:0 -o empty -e not-empty \
285303980Sngie		${TEST_SH} -nc 'do YOU ABANDON HOPE; for all eternity?'
286303980Sngie
287303980Sngie	# now test enabling -n in the middle of a script
288303980Sngie	# note that once turned on, it cannot be turned off again.
289303980Sngie	#
290303980Sngie	# omit more complex cases, as those can send some shells
291303980Sngie	# into infinite loops, and believe it or not, that might be OK!
292303980Sngie
293303980Sngie	atf_check -s exit:0 -o match:first -o not-match:second -e empty \
294303980Sngie		${TEST_SH} -c 'echo first; set -n; echo second'
295303980Sngie	atf_check -s exit:0 -o match:first -o not-match:third -e empty \
296303980Sngie	    ${TEST_SH} -c 'echo first; set -n; echo second; set +n; echo third'
297303980Sngie	atf_check -s exit:0 -o inline:'a\nb\n' -e empty \
298303980Sngie	    ${TEST_SH} -c 'for x in a b c d
299303980Sngie			   do
300303980Sngie				case "$x" in
301303980Sngie				     a);; b);; c) set -n;; d);;
302303980Sngie				esac
303303980Sngie				printf "%s\n" "$x"
304303980Sngie			   done'
305303980Sngie
306303980Sngie	# This last one is a bit more complex to explain, so I will not try
307303980Sngie
308303980Sngie	# First, we need to know what signal number is used for SIGUSR1 on
309303980Sngie	# the local (testing) system (signal number is $(( $XIT - 128 )) )
310303980Sngie
311303980Sngie	# this will take slightly over 1 second elapsed time (the sleep 1)
312303980Sngie	# The "10" for the first sleep just needs to be something big enough
313303980Sngie	# that the rest of the commands have time to complete, even on
314303980Sngie	# very slow testing systems.  10 should be enough.  Otherwise irrelevant
315303980Sngie
316303980Sngie	# The shell will usually blather to stderr about the sleep 10 being
317303980Sngie	# killed, but it affects nothing, so just allow it to cry.
318303980Sngie
319303980Sngie	(sleep 10 & sleep 1; kill -USR1 $!; wait $!)
320303980Sngie	XIT="$?"
321303980Sngie
322303980Sngie	# The exit value should be an integer > 128 and < 256 (often 158)
323303980Sngie	# If it is not just skip the test
324303980Sngie
325303980Sngie	# If we do run the test, it should take (slightly over) either 1 or 2
326303980Sngie	# seconds to complete, depending upon the shell being tested.
327303980Sngie
328303980Sngie	case "${XIT}" in
329303980Sngie	( 129 | 1[3-9][0-9] | 2[0-4][0-9] | 25[0-5] )
330303980Sngie
331303980Sngie		# The script below should exit with the same code - no output
332303980Sngie
333303980Sngie		# Or that is the result that seems best explanable.
334303980Sngie		# "set -n" in uses like this is not exactly well defined...
335303980Sngie
336303980Sngie		# This script comes from a member of the austin group
337303980Sngie		# (they author changes to the posix shell spec - and more.)
338303980Sngie		# The author is also an (occasional?) NetBSD user.
339303980Sngie		atf_check -s exit:${XIT} -o empty -e empty ${TEST_SH} -c '
340303980Sngie			trap "set -n" USR1
341303980Sngie			{ sleep 1; kill -USR1 $$; sleep 1; } &
342303980Sngie			false
343303980Sngie			wait && echo t || echo f
344303980Sngie			wait
345303980Sngie			echo foo
346303980Sngie		'
347303980Sngie		;;
348303980Sngie	esac
349303980Sngie}
350303980Sngie
351303980Sngieatf_test_case set_u
352303980Sngieset_u_head() {
353303980Sngie	atf_set "descr" "Tests that 'set -u' turns on unset var detection " \
354303980Sngie	                "and that it behaves as defined by the standard"
355303980Sngie}
356303980Sngieset_u_body() {
357303980Sngie	test_option_on_off u
358303980Sngie
359303980Sngie	# first make sure it is OK to unset an unset variable
360303980Sngie	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
361303980Sngie		'unset _UNSET_VARIABLE_; echo OK'
362303980Sngie	# even if -u is set
363303980Sngie	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -cue \
364303980Sngie		'unset _UNSET_VARIABLE_; echo OK'
365303980Sngie
366303980Sngie	# and that without -u accessing an unset variable is harmless
367303980Sngie	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
368303980Sngie		'unset X; echo ${X}; echo OK'
369303980Sngie	# and that the unset variable test expansion works properly
370303980Sngie	atf_check -s exit:0 -o match:OKOK -e empty ${TEST_SH} -ce \
371303980Sngie		'unset X; printf "%s" ${X-OK}; echo OK'
372303980Sngie
373303980Sngie	# Next test that with -u set, the shell aborts on access to unset var
374303980Sngie	# do not use -e, want to make sure it is -u that causes abort
375303980Sngie	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
376303980Sngie		'unset X; set -u; echo ${X}; echo ERR'
377303980Sngie	# quoting should make no difference...
378303980Sngie	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
379303980Sngie		'unset X; set -u; echo "${X}"; echo ERR'
380303980Sngie
381303980Sngie	# Now a bunch of accesses to unset vars, with -u, in ways that are OK
382303980Sngie	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
383303980Sngie		'unset X; set -u; echo ${X-GOOD}; echo OK'
384303980Sngie	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
385303980Sngie		'unset X; set -u; echo ${X-OK}'
386303980Sngie	atf_check -s exit:0 -o not-match:ERR -o match:OK -e empty \
387303980Sngie		${TEST_SH} -ce 'unset X; set -u; echo ${X+ERR}; echo OK'
388303980Sngie
389303980Sngie	# and some more ways that are not OK
390303980Sngie	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
391303980Sngie		'unset X; set -u; echo ${X#foo}; echo ERR'
392303980Sngie	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
393303980Sngie		'unset X; set -u; echo ${X%%bar}; echo ERR'
394303980Sngie
395303980Sngie	# lastly, just while we are checking unset vars, test aborts w/o -u
396303980Sngie	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
397303980Sngie		'unset X; echo ${X?}; echo ERR'
398303980Sngie	atf_check -s not-exit:0 -o not-match:ERR -e match:X_NOT_SET \
399303980Sngie		${TEST_SH} -c 'unset X; echo ${X?X_NOT_SET}; echo ERR'
400303980Sngie}
401303980Sngie
402303980Sngieatf_test_case set_v
403303980Sngieset_v_head() {
404303980Sngie	atf_set "descr" "Tests that 'set -v' turns on input read echoing " \
405303980Sngie	                "and that it behaves as defined by the standard"
406303980Sngie}
407303980Sngieset_v_body() {
408303980Sngie	test_option_on_off v
409303980Sngie
410303980Sngie	# check that -v does nothing if no later input line is read
411303980Sngie	atf_check -s exit:0 \
412303980Sngie			-o match:OKOK -o not-match:echo -o not-match:printf \
413303980Sngie			-e empty \
414303980Sngie		${TEST_SH} -ec 'printf "%s" OK; set -v; echo OK; exit 0'
415303980Sngie
416303980Sngie	# but that it does when there are multiple lines
417303980Sngie	cat <<- 'EOF' |
418303980Sngie		set -v
419303980Sngie		printf %s OK
420303980Sngie		echo OK
421303980Sngie		exit 0
422303980Sngie	EOF
423303980Sngie	atf_check -s exit:0 \
424303980Sngie			-o match:OKOK -o not-match:echo -o not-match:printf \
425303980Sngie			-e match:printf -e match:OK -e match:echo \
426303980Sngie			-e not-match:set ${TEST_SH}
427303980Sngie
428303980Sngie	# and that it can be disabled again
429303980Sngie	cat <<- 'EOF' |
430303980Sngie		set -v
431303980Sngie		printf %s OK
432303980Sngie		set +v
433303980Sngie		echo OK
434303980Sngie		exit 0
435303980Sngie	EOF
436303980Sngie	atf_check -s exit:0 \
437303980Sngie			-o match:OKOK -o not-match:echo -o not-match:printf \
438303980Sngie			-e match:printf -e match:OK -e not-match:echo \
439303980Sngie				${TEST_SH}
440303980Sngie
441303980Sngie	# and lastly, that shell keywords do get output when "read"
442303980Sngie	cat <<- 'EOF' |
443303980Sngie		set -v
444303980Sngie		for i in 111 222 333
445303980Sngie		do
446303980Sngie			printf %s $i
447303980Sngie		done
448303980Sngie		exit 0
449303980Sngie	EOF
450303980Sngie	atf_check -s exit:0 \
451303980Sngie			-o match:111222333 -o not-match:printf \
452303980Sngie			-o not-match:for -o not-match:do -o not-match:done \
453303980Sngie			-e match:printf -e match:111 -e not-match:111222 \
454303980Sngie			-e match:for -e match:do -e match:done \
455303980Sngie				${TEST_SH}
456303980Sngie}
457303980Sngie
458303980Sngieatf_test_case set_x
459303980Sngieset_x_head() {
460303980Sngie	atf_set "descr" "Tests that 'set -x' turns on command exec logging " \
461303980Sngie	                "and that it behaves as defined by the standard"
462303980Sngie}
463303980Sngieset_x_body() {
464303980Sngie	test_option_on_off x
465303980Sngie
466303980Sngie	# check that cmd output appears after -x is enabled
467303980Sngie	atf_check -s exit:0 \
468303980Sngie			-o match:OKOK -o not-match:echo -o not-match:printf \
469303980Sngie			-e not-match:printf -e match:OK -e match:echo \
470303980Sngie		${TEST_SH} -ec 'printf "%s" OK; set -x; echo OK; exit 0'
471303980Sngie
472303980Sngie	# and that it stops again afer -x is disabled
473303980Sngie	atf_check -s exit:0 \
474303980Sngie			-o match:OKOK -o not-match:echo -o not-match:printf \
475303980Sngie			-e match:printf -e match:OK -e not-match:echo \
476303980Sngie	    ${TEST_SH} -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0'
477303980Sngie
478303980Sngie	# also check that PS4 is output correctly
479303980Sngie	atf_check -s exit:0 \
480303980Sngie			-o match:OK -o not-match:echo \
481303980Sngie			-e match:OK -e match:Run:echo \
482303980Sngie		${TEST_SH} -ec 'PS4=Run:; set -x; echo OK; exit 0'
483303980Sngie
484303980Sngie	return 0
485303980Sngie
486303980Sngie	# This one seems controversial... I suspect it is NetBSD's sh
487303980Sngie	# that is wrong to not output "for" "while" "if" ... etc
488303980Sngie
489303980Sngie	# and lastly, that shell keywords do not get output when "executed"
490303980Sngie	atf_check -s exit:0 \
491303980Sngie			-o match:111222333 -o not-match:printf \
492303980Sngie			-o not-match:for \
493303980Sngie			-e match:printf -e match:111 -e not-match:111222 \
494303980Sngie			-e not-match:for -e not-match:do -e not-match:done \
495303980Sngie		${TEST_SH} -ec \
496303980Sngie	   'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0'
497303980Sngie}
498303980Sngie
499303980Sngieopt_test_setup()
500303980Sngie{
501303980Sngie	test -n "$1" || { echo >&2 "Internal error"; exit 1; }
502303980Sngie
503303980Sngie	cat > "$1" << 'END_OF_FUNCTIONS'
504303980Sngielocal_opt_check()
505303980Sngie{
506303980Sngie	local -
507303980Sngie}
508303980Sngie
509303980Sngieinstr()
510303980Sngie{
511303980Sngie	expr "$2" : "\(.*$1\)" >/dev/null
512303980Sngie}
513303980Sngie
514303980Sngiesave_opts()
515303980Sngie{
516303980Sngie	local -
517303980Sngie
518303980Sngie	set -e
519303980Sngie	set -u
520303980Sngie
521303980Sngie	instr e "$-" && instr u "$-" && return 0
522303980Sngie	echo ERR
523303980Sngie}
524303980Sngie
525303980Sngiefiddle_opts()
526303980Sngie{
527303980Sngie	set -e
528303980Sngie	set -u
529303980Sngie
530303980Sngie	instr e "$-" && instr u "$-" && return 0
531303980Sngie	echo ERR
532303980Sngie}
533303980Sngie
534303980Sngielocal_test()
535303980Sngie{
536303980Sngie	set +eu
537303980Sngie
538303980Sngie	save_opts
539303980Sngie	instr '[eu]' "$-" || printf %s "OK"
540303980Sngie
541303980Sngie	fiddle_opts
542303980Sngie	instr e "$-" && instr u "$-" && printf %s "OK"
543303980Sngie
544303980Sngie	set +eu
545303980Sngie}
546303980SngieEND_OF_FUNCTIONS
547303980Sngie}
548303980Sngie
549303980Sngieatf_test_case restore_local_opts
550303980Sngierestore_local_opts_head() {
551303980Sngie	atf_set "descr" "Tests that 'local -' saves and restores options.  " \
552303980Sngie			"Note that "local" is a local shell addition"
553303980Sngie}
554303980Sngierestore_local_opts_body() {
555303980Sngie	atf_require_prog cat
556303980Sngie	atf_require_prog expr
557303980Sngie
558303980Sngie	FN="test-funcs.$$"
559303980Sngie	opt_test_setup "${FN}" || atf_skip "Cannot setup test environment"
560303980Sngie
561303980Sngie	${TEST_SH} -ec ". './${FN}'; local_opt_check" 2>/dev/null ||
562303980Sngie		atf_skip "sh extension 'local -' not supported by ${TEST_SH}"
563303980Sngie
564303980Sngie	atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \
565303980Sngie		${TEST_SH} -ec ". './${FN}'; local_test"
566303980Sngie}
567303980Sngie
568303980Sngieatf_test_case vi_emacs_VE_toggle
569303980Sngievi_emacs_VE_toggle_head() {
570303980Sngie	atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\
571303980Sngie			"  Note that -V and -E are local shell additions"
572303980Sngie}
573303980Sngievi_emacs_VE_toggle_body() {
574303980Sngie
575303980Sngie	test_optional_on_off V E ||
576303980Sngie	  atf_skip "One or both V & E opts unsupported by ${TEST_SH}"
577303980Sngie
578303980Sngie	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '
579303980Sngie		q() {
580303980Sngie			eval "case \"$-\" in
581303980Sngie			(*${2}*)	return 1;;
582303980Sngie			(*${1}*)	return 0;;
583303980Sngie			esac"
584303980Sngie			return 1
585303980Sngie		}
586303980Sngie		x() {
587303980Sngie			echo >&2 "Option set or toggle failure:" \
588303980Sngie					" on=$1 off=$2 set=$-"
589303980Sngie			exit 1
590303980Sngie		}
591303980Sngie		set -V; q V E || x V E
592303980Sngie		set -E; q E V || x E V
593303980Sngie		set -V; q V E || x V E
594303980Sngie		set +EV; q "" "[VE]" || x "" VE
595303980Sngie		exit 0
596303980Sngie	'
597303980Sngie}
598303980Sngie
599303980Sngieatf_test_case xx_bogus
600303980Sngiexx_bogus_head() {
601303980Sngie	atf_set "descr" "Tests that attempting to set a nonsense option fails."
602303980Sngie}
603303980Sngiexx_bogus_body() {
604303980Sngie	# Biggest problem here is picking a "nonsense option" that is
605303980Sngie	# not implemented by any shell, anywhere.  Hopefully this will do.
606303980Sngie
607303980Sngie	# 'set' is a special builtin, so a conforming shell should exit
608303980Sngie	# on an arg error, and the ERR should not be printed.
609303980Sngie	atf_check -s not-exit:0 -o empty -e not-empty \
610303980Sngie		${TEST_SH} -c 'set -% ; echo ERR'
611303980Sngie}
612303980Sngie
613303980Sngieatf_test_case Option_switching
614303980SngieOption_switching_head() {
615303980Sngie	atf_set "descr" "options can be enabled and disabled"
616303980Sngie}
617303980SngieOption_switching_body() {
618303980Sngie
619303980Sngie	# Cannot test -m, setting it causes test shell to fail...
620303980Sngie	# (test shell gets SIGKILL!)  Wonder why ... something related to atf
621303980Sngie	# That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'"
622303980Sngie
623303980Sngie	# Don't bother testing toggling -n, once on, it stays on...
624303980Sngie	# (and because the test fn refuses to allow us to try)
625303980Sngie
626303980Sngie	# Cannot test -o or -c here, or the extension -s
627303980Sngie	# they can only be used, not switched
628303980Sngie
629303980Sngie	# these are the posix options, that all shells should implement
630303980Sngie	test_option_on_off a b C e f h u v x      # m
631303980Sngie
632303980Sngie	# and these are extensions that might not exist (non-fatal to test)
633303980Sngie	# -i and -s (and -c) are posix options, but are not required to
634303980Sngie	# be accessable via the "set" command, just the command line.
635303980Sngie	# We allow for -i to work with set, as that makes some sense,
636303980Sngie	# -c and -s do not.
637303980Sngie	test_optional_on_off E i I p q V || true
638303980Sngie
639303980Sngie	# Also test (some) option combinations ...
640303980Sngie	# only testing posix options here, because it is easier...
641303980Sngie	test_option_on_off aeu vx Ca aCefux
642303980Sngie}
643303980Sngie
644303980Sngieatf_init_test_cases() {
645303980Sngie	# tests are run in order sort of names produces, so choose names wisely
646303980Sngie
647303980Sngie	# this one tests turning on/off all the mandatory. and extra flags
648303980Sngie	atf_add_test_case Option_switching
649303980Sngie	# and this tests the NetBSD "local -" functionality in functions.
650303980Sngie	atf_add_test_case restore_local_opts
651303980Sngie
652303980Sngie	# no tests for	-m (no idea how to do that one)
653303980Sngie	#		-I (no easy way to generate the EOF it ignores)
654303980Sngie	#		-i (not sure how to test that one at the minute)
655303980Sngie	#		-p (because we aren't going to run tests setuid)
656303980Sngie	#		-V/-E (too much effort, and a real test would be huge)
657303980Sngie	#		-c (because almost all the other tests test it anyway)
658303980Sngie	#		-q (because, for now, I am lazy)
659303980Sngie	#		-s (coming soon, hopefully)
660303980Sngie	#		-o (really +o: again, hopefully soon)
661303980Sngie	#		-o longname (again, just laziness, don't wait...)
662303980Sngie	# 		-h/-b (because NetBSD doesn't implement them)
663303980Sngie	atf_add_test_case set_a
664303980Sngie	atf_add_test_case set_C
665303980Sngie	atf_add_test_case set_e
666303980Sngie	atf_add_test_case set_f
667303980Sngie	atf_add_test_case set_n
668303980Sngie	atf_add_test_case set_u
669303980Sngie	atf_add_test_case set_v
670303980Sngie	atf_add_test_case set_x
671303980Sngie
672303980Sngie	atf_add_test_case vi_emacs_VE_toggle
673303980Sngie	atf_add_test_case xx_bogus
674303980Sngie}
675