xref: /netbsd-src/tests/bin/sh/t_option.sh (revision f42f89fd6fc4d08451450f9b7ffa731678160f1c)
1*f42f89fdSandvar# $NetBSD: t_option.sh,v 1.9 2022/05/22 11:27:36 andvar Exp $
2c1ce7cf3Schristos#
3c1ce7cf3Schristos# Copyright (c) 2016 The NetBSD Foundation, Inc.
4c1ce7cf3Schristos# All rights reserved.
5c1ce7cf3Schristos#
6c1ce7cf3Schristos# Redistribution and use in source and binary forms, with or without
7c1ce7cf3Schristos# modification, are permitted provided that the following conditions
8c1ce7cf3Schristos# are met:
9c1ce7cf3Schristos# 1. Redistributions of source code must retain the above copyright
10c1ce7cf3Schristos#    notice, this list of conditions and the following disclaimer.
11c1ce7cf3Schristos# 2. Redistributions in binary form must reproduce the above copyright
12c1ce7cf3Schristos#    notice, this list of conditions and the following disclaimer in the
13c1ce7cf3Schristos#    documentation and/or other materials provided with the distribution.
14c1ce7cf3Schristos#
15c1ce7cf3Schristos# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16c1ce7cf3Schristos# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17c1ce7cf3Schristos# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18c1ce7cf3Schristos# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19c1ce7cf3Schristos# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20c1ce7cf3Schristos# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21c1ce7cf3Schristos# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22c1ce7cf3Schristos# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23c1ce7cf3Schristos# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24c1ce7cf3Schristos# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25c1ce7cf3Schristos# POSSIBILITY OF SUCH DAMAGE.
26c1ce7cf3Schristos#
27c1ce7cf3Schristos# the implementation of "sh" to test
28c1ce7cf3Schristos: ${TEST_SH:="/bin/sh"}
29c1ce7cf3Schristos
30c1ce7cf3Schristos# The standard
31c1ce7cf3Schristos# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
32c1ce7cf3Schristos# says:
33c1ce7cf3Schristos#	...[lots]
34c1ce7cf3Schristos
35c1ce7cf3Schristostest_option_on_off()
36c1ce7cf3Schristos{
37c1ce7cf3Schristos	atf_require_prog tr
38c1ce7cf3Schristos
39c1ce7cf3Schristos	for opt
40c1ce7cf3Schristos	do
41c1ce7cf3Schristos				# t is needed, as inside $()` $- appears to lose
42c1ce7cf3Schristos				# the 'e' option if it happened to already be
43c1ce7cf3Schristos				# set.  Must check if that is what should
44c1ce7cf3Schristos				# happen, but that is a different issue.
45c1ce7cf3Schristos
46c1ce7cf3Schristos		test -z "${opt}" && continue
47c1ce7cf3Schristos
48c1ce7cf3Schristos		# if we are playing with more that one option at a
49c1ce7cf3Schristos		# time, the code below requires that we start with no
50c1ce7cf3Schristos		# options set, or it will mis-diagnose the situation
51c1ce7cf3Schristos		CLEAR=''
52c1ce7cf3Schristos		test "${#opt}" -gt 1 &&
53c1ce7cf3Schristos  CLEAR='xx="$-" && xx=$(echo "$xx" | tr -d cs) && test -n "$xx" && set +"$xx";'
54c1ce7cf3Schristos
5506f9bef6Schristos		atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
56c1ce7cf3Schristos			"opt=${opt}"'
57c1ce7cf3Schristos			x() {
58c1ce7cf3Schristos				echo "ERROR: Unable to $1 option $2" >&2
59c1ce7cf3Schristos				exit 1
60c1ce7cf3Schristos			}
61c1ce7cf3Schristos			s() {
62c1ce7cf3Schristos				set -"$1"
63c1ce7cf3Schristos				t="$-"
64c1ce7cf3Schristos				x=$(echo "$t" | tr -d "$1")
65c1ce7cf3Schristos				test "$t" = "$x" && x set "$1"
66c1ce7cf3Schristos				return 0
67c1ce7cf3Schristos			}
68c1ce7cf3Schristos			c() {
69c1ce7cf3Schristos				set +"$1"
70c1ce7cf3Schristos				t="$-"
71c1ce7cf3Schristos				x=$(echo "$t" | tr -d "$1")
72c1ce7cf3Schristos				test "$t" != "$x" && x clear "$1"
73c1ce7cf3Schristos				return 0
74c1ce7cf3Schristos			}
75c1ce7cf3Schristos			'"${CLEAR}"'
76c1ce7cf3Schristos
77c1ce7cf3Schristos			# if we do not do this, -x tracing splatters stderr
78c1ce7cf3Schristos			# for some shells, -v does as well (is that correct?)
79c1ce7cf3Schristos			case "${opt}" in
80a60600e0Skre			(*[xXv]*)	exec 2>/dev/null;;
81c1ce7cf3Schristos			esac
82c1ce7cf3Schristos
83c1ce7cf3Schristos			o="$-"
84c1ce7cf3Schristos			x=$(echo "$o" | tr -d "$opt")
85c1ce7cf3Schristos
86c1ce7cf3Schristos			if [ "$o" = "$x" ]; then	# option was off
87c1ce7cf3Schristos				s "${opt}"
88c1ce7cf3Schristos				c "${opt}"
89c1ce7cf3Schristos			else
90c1ce7cf3Schristos				c "${opt}"
91c1ce7cf3Schristos				s "${opt}"
92c1ce7cf3Schristos			fi
93c1ce7cf3Schristos		'
94c1ce7cf3Schristos	done
95c1ce7cf3Schristos}
96c1ce7cf3Schristos
97c1ce7cf3Schristostest_optional_on_off()
98c1ce7cf3Schristos{
99c1ce7cf3Schristos	RET=0
100c1ce7cf3Schristos	OPTS=
101c1ce7cf3Schristos	for opt
102c1ce7cf3Schristos	do
103c1ce7cf3Schristos		test "${opt}" = n && continue
10406f9bef6Schristos		${TEST_SH} -c "set -${opt}" 2>/dev/null  &&
105c1ce7cf3Schristos			OPTS="${OPTS} ${opt}" || RET=1
106c1ce7cf3Schristos	done
107c1ce7cf3Schristos
108c1ce7cf3Schristos	test -n "${OPTS}" && test_option_on_off ${OPTS}
109c1ce7cf3Schristos
110c1ce7cf3Schristos	return "${RET}"
111c1ce7cf3Schristos}
112c1ce7cf3Schristos
113c1ce7cf3Schristosatf_test_case set_a
114c1ce7cf3Schristosset_a_head() {
115c1ce7cf3Schristos	atf_set "descr" "Tests that 'set -a' turns on all var export " \
116c1ce7cf3Schristos	                "and that it behaves as defined by the standard"
117c1ce7cf3Schristos}
118c1ce7cf3Schristosset_a_body() {
119c1ce7cf3Schristos	atf_require_prog env
120c1ce7cf3Schristos	atf_require_prog grep
121c1ce7cf3Schristos
122c1ce7cf3Schristos	test_option_on_off a
123c1ce7cf3Schristos
124c1ce7cf3Schristos	# without -a, new variables should not be exported (so grep "fails")
12506f9bef6Schristos	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -ce \
126c1ce7cf3Schristos		'unset VAR; set +a; VAR=value; env | grep "^VAR="'
127c1ce7cf3Schristos
128c1ce7cf3Schristos	# with -a, they should be
12906f9bef6Schristos	atf_check -s exit:0 -o match:VAR=value -e empty ${TEST_SH} -ce \
130c1ce7cf3Schristos		'unset VAR; set -a; VAR=value; env | grep "^VAR="'
131c1ce7cf3Schristos}
132c1ce7cf3Schristos
133c1ce7cf3Schristosatf_test_case set_C
134c1ce7cf3Schristosset_C_head() {
135c1ce7cf3Schristos	atf_set "descr" "Tests that 'set -C' turns on no clobber mode " \
136c1ce7cf3Schristos	                "and that it behaves as defined by the standard"
137c1ce7cf3Schristos}
138c1ce7cf3Schristosset_C_body() {
139c1ce7cf3Schristos	atf_require_prog ls
140c1ce7cf3Schristos
141c1ce7cf3Schristos	test_option_on_off C
142c1ce7cf3Schristos
143c1ce7cf3Schristos	# Check that the environment to use for the tests is sane ...
144c1ce7cf3Schristos	# we assume current dir is a new tempory directory & is empty
145c1ce7cf3Schristos
146c1ce7cf3Schristos	test -z "$(ls)" || atf_skip "Test execution directory not clean"
147c1ce7cf3Schristos	test -c "/dev/null" || atf_skip "Problem with /dev/null"
148c1ce7cf3Schristos
149c1ce7cf3Schristos	echo Dummy_Content > Junk_File
150c1ce7cf3Schristos	echo Precious_Content > Important_File
151c1ce7cf3Schristos
152c1ce7cf3Schristos	# Check that we can redirect onto file when -C is not set
15306f9bef6Schristos	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
154c1ce7cf3Schristos		'
155c1ce7cf3Schristos		D=$(ls -l Junk_File) || exit 1
156c1ce7cf3Schristos		set +C
157c1ce7cf3Schristos		echo "Overwrite it now" > Junk_File
158c1ce7cf3Schristos		A=$(ls -l Junk_File) || exit 1
159c1ce7cf3Schristos		test "${A}" != "${D}"
160c1ce7cf3Schristos		'
161c1ce7cf3Schristos
162c1ce7cf3Schristos	# Check that we cannot redirect onto file when -C is set
16306f9bef6Schristos	atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
164c1ce7cf3Schristos		'
165c1ce7cf3Schristos		D=$(ls -l Important_File) || exit 1
166c1ce7cf3Schristos		set -C
167c1ce7cf3Schristos		echo "Fail to Overwrite it now" > Important_File
168c1ce7cf3Schristos		A=$(ls -l Important_File) || exit 1
169c1ce7cf3Schristos		test "${A}" = "${D}"
170c1ce7cf3Schristos		'
171c1ce7cf3Schristos
172c1ce7cf3Schristos	# Check that we can append to file, even when -C is set
17306f9bef6Schristos	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
174c1ce7cf3Schristos		'
175c1ce7cf3Schristos		D=$(ls -l Junk_File) || exit 1
176c1ce7cf3Schristos		set -C
177c1ce7cf3Schristos		echo "Append to it now" >> Junk_File
178c1ce7cf3Schristos		A=$(ls -l Junk_File) || exit 1
179c1ce7cf3Schristos		test "${A}" != "${D}"
180c1ce7cf3Schristos		'
181c1ce7cf3Schristos
182c1ce7cf3Schristos	# Check that we abort on attempt to redirect onto file when -Ce is set
18306f9bef6Schristos	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
184c1ce7cf3Schristos		'
185c1ce7cf3Schristos		set -Ce
186c1ce7cf3Schristos		echo "Fail to Overwrite it now" > Important_File
187c1ce7cf3Schristos		echo "Should not reach this point"
188c1ce7cf3Schristos		'
189c1ce7cf3Schristos
190c1ce7cf3Schristos	# Last check that we can override -C for when we really need to
19106f9bef6Schristos	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
192c1ce7cf3Schristos		'
193c1ce7cf3Schristos		D=$(ls -l Junk_File) || exit 1
194c1ce7cf3Schristos		set -C
195c1ce7cf3Schristos		echo "Change the poor bugger again" >| Junk_File
196c1ce7cf3Schristos		A=$(ls -l Junk_File) || exit 1
197c1ce7cf3Schristos		test "${A}" != "${D}"
198c1ce7cf3Schristos		'
199c1ce7cf3Schristos}
200c1ce7cf3Schristos
201c1ce7cf3Schristosatf_test_case set_e
202c1ce7cf3Schristosset_e_head() {
203c1ce7cf3Schristos	atf_set "descr" "Tests that 'set -e' turns on error detection " \
204c1ce7cf3Schristos		"and that a simple case behaves as defined by the standard"
205c1ce7cf3Schristos}
206c1ce7cf3Schristosset_e_body() {
207c1ce7cf3Schristos	test_option_on_off e
208c1ce7cf3Schristos
209c1ce7cf3Schristos	# Check that -e does nothing if no commands fail
210c1ce7cf3Schristos	atf_check -s exit:0 -o match:I_am_OK -e empty \
21106f9bef6Schristos	    ${TEST_SH} -c \
212c1ce7cf3Schristos		'false; printf "%s" I_am; set -e; true; printf "%s\n" _OK'
213c1ce7cf3Schristos
214c1ce7cf3Schristos	# and that it (silently, but with exit status) aborts if cmd fails
215c1ce7cf3Schristos	atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
21606f9bef6Schristos	    ${TEST_SH} -c \
217c1ce7cf3Schristos		'false; printf "%s" I_am; set -e; false; printf "%s\n" _Broken'
218c1ce7cf3Schristos
219c1ce7cf3Schristos	# same, except -e this time is on from the beginning
220c1ce7cf3Schristos	atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \
22106f9bef6Schristos	    ${TEST_SH} -ec 'printf "%s" I_am; false; printf "%s\n" _Broken'
222c1ce7cf3Schristos
223c1ce7cf3Schristos	# More checking of -e in other places, there is lots to deal with.
224c1ce7cf3Schristos}
225c1ce7cf3Schristos
226c1ce7cf3Schristosatf_test_case set_f
227c1ce7cf3Schristosset_f_head() {
228c1ce7cf3Schristos	atf_set "descr" "Tests that 'set -f' turns off pathname expansion " \
229c1ce7cf3Schristos	                "and that it behaves as defined by the standard"
230c1ce7cf3Schristos}
231c1ce7cf3Schristosset_f_body() {
232c1ce7cf3Schristos	atf_require_prog ls
233c1ce7cf3Schristos
234c1ce7cf3Schristos	test_option_on_off f
235c1ce7cf3Schristos
236c1ce7cf3Schristos	# Check that the environment to use for the tests is sane ...
237c1ce7cf3Schristos	# we assume current dir is a new tempory directory & is empty
238c1ce7cf3Schristos
239c1ce7cf3Schristos	test -z "$(ls)" || atf_skip "Test execution directory not clean"
240c1ce7cf3Schristos
241c1ce7cf3Schristos	# we will assume that atf will clean up this junk directory
242c1ce7cf3Schristos	# when we are done.   But for testing pathname expansion
243c1ce7cf3Schristos	# we need files
244c1ce7cf3Schristos
245c1ce7cf3Schristos	for f in a b c d e f aa ab ac ad ae aaa aab aac aad aba abc bbb ccc
246c1ce7cf3Schristos	do
247c1ce7cf3Schristos		echo "$f" > "$f"
248c1ce7cf3Schristos	done
249c1ce7cf3Schristos
25006f9bef6Schristos	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \
251c1ce7cf3Schristos	    'X=$(echo b*); Y=$(echo b*); test "${X}" != "a*";
252c1ce7cf3Schristos		test "${X}" = "${Y}"'
253c1ce7cf3Schristos
254c1ce7cf3Schristos	# now test expansion is different when -f is set
25506f9bef6Schristos	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \
256c1ce7cf3Schristos	   'X=$(echo b*); Y=$(set -f; echo b*); test "${X}" != "${Y}"'
257c1ce7cf3Schristos}
258c1ce7cf3Schristos
259c1ce7cf3Schristosatf_test_case set_n
260c1ce7cf3Schristosset_n_head() {
26103e9d50aSmsaitoh	atf_set "descr" "Tests that 'set -n' suppresses command execution " \
262c1ce7cf3Schristos	                "and that it behaves as defined by the standard"
263c1ce7cf3Schristos}
264c1ce7cf3Schristosset_n_body() {
265c1ce7cf3Schristos	# pointless to test this, if it turns on, it stays on...
266c1ce7cf3Schristos	# test_option_on_off n
267c1ce7cf3Schristos	# so just allow the tests below to verify it can be turned on
268c1ce7cf3Schristos
269c1ce7cf3Schristos	# nothing should be executed, hence no output...
270c1ce7cf3Schristos	atf_check -s exit:0 -o empty -e empty \
27106f9bef6Schristos		${TEST_SH} -enc 'echo ABANDON HOPE; echo ALL YE; echo ...'
272c1ce7cf3Schristos
273c1ce7cf3Schristos	# this is true even when the "commands" do not exist
274c1ce7cf3Schristos	atf_check -s exit:0 -o empty -e empty \
27506f9bef6Schristos		${TEST_SH} -enc 'ERR; FAIL; ABANDON HOPE'
276c1ce7cf3Schristos
27706f9bef6Schristos	# but if there is a syntax error, it should be detected (w or w/o -e)
278c1ce7cf3Schristos	atf_check -s not-exit:0 -o empty -e not-empty \
27906f9bef6Schristos		${TEST_SH} -enc 'echo JUMP; for frogs swim; echo in puddles'
28006f9bef6Schristos	atf_check -s not-exit:0 -o empty -e not-empty \
28106f9bef6Schristos		${TEST_SH} -nc 'echo ABANDON HOPE; echo "ALL YE; echo ...'
28206f9bef6Schristos	atf_check -s not-exit:0 -o empty -e not-empty \
28306f9bef6Schristos		${TEST_SH} -enc 'echo ABANDON HOPE;; echo ALL YE; echo ...'
28406f9bef6Schristos	atf_check -s not-exit:0 -o empty -e not-empty \
28506f9bef6Schristos		${TEST_SH} -nc 'do YOU ABANDON HOPE; for all eternity?'
286c1ce7cf3Schristos
28706f9bef6Schristos	# now test enabling -n in the middle of a script
28806f9bef6Schristos	# note that once turned on, it cannot be turned off again.
28906f9bef6Schristos	#
29006f9bef6Schristos	# omit more complex cases, as those can send some shells
29106f9bef6Schristos	# into infinite loops, and believe it or not, that might be OK!
29206f9bef6Schristos
29306f9bef6Schristos	atf_check -s exit:0 -o match:first -o not-match:second -e empty \
29406f9bef6Schristos		${TEST_SH} -c 'echo first; set -n; echo second'
29506f9bef6Schristos	atf_check -s exit:0 -o match:first -o not-match:third -e empty \
29606f9bef6Schristos	    ${TEST_SH} -c 'echo first; set -n; echo second; set +n; echo third'
29706f9bef6Schristos	atf_check -s exit:0 -o inline:'a\nb\n' -e empty \
29806f9bef6Schristos	    ${TEST_SH} -c 'for x in a b c d
29906f9bef6Schristos			   do
30006f9bef6Schristos				case "$x" in
30106f9bef6Schristos				     a);; b);; c) set -n;; d);;
30206f9bef6Schristos				esac
30306f9bef6Schristos				printf "%s\n" "$x"
30406f9bef6Schristos			   done'
30506f9bef6Schristos
30606f9bef6Schristos	# This last one is a bit more complex to explain, so I will not try
30706f9bef6Schristos
30806f9bef6Schristos	# First, we need to know what signal number is used for SIGUSR1 on
30906f9bef6Schristos	# the local (testing) system (signal number is $(( $XIT - 128 )) )
31006f9bef6Schristos
31106f9bef6Schristos	# this will take slightly over 1 second elapsed time (the sleep 1)
31206f9bef6Schristos	# The "10" for the first sleep just needs to be something big enough
31306f9bef6Schristos	# that the rest of the commands have time to complete, even on
31406f9bef6Schristos	# very slow testing systems.  10 should be enough.  Otherwise irrelevant
31506f9bef6Schristos
31606f9bef6Schristos	# The shell will usually blather to stderr about the sleep 10 being
31706f9bef6Schristos	# killed, but it affects nothing, so just allow it to cry.
31806f9bef6Schristos
31906f9bef6Schristos	(sleep 10 & sleep 1; kill -USR1 $!; wait $!)
32006f9bef6Schristos	XIT="$?"
32106f9bef6Schristos
32206f9bef6Schristos	# The exit value should be an integer > 128 and < 256 (often 158)
32306f9bef6Schristos	# If it is not just skip the test
32406f9bef6Schristos
32506f9bef6Schristos	# If we do run the test, it should take (slightly over) either 1 or 2
32606f9bef6Schristos	# seconds to complete, depending upon the shell being tested.
32706f9bef6Schristos
32806f9bef6Schristos	case "${XIT}" in
32906f9bef6Schristos	( 129 | 1[3-9][0-9] | 2[0-4][0-9] | 25[0-5] )
33006f9bef6Schristos
33106f9bef6Schristos		# The script below should exit with the same code - no output
33206f9bef6Schristos
33306f9bef6Schristos		# Or that is the result that seems best explanable.
33406f9bef6Schristos		# "set -n" in uses like this is not exactly well defined...
33506f9bef6Schristos
33606f9bef6Schristos		# This script comes from a member of the austin group
33706f9bef6Schristos		# (they author changes to the posix shell spec - and more.)
33806f9bef6Schristos		# The author is also an (occasional?) NetBSD user.
33906f9bef6Schristos		atf_check -s exit:${XIT} -o empty -e empty ${TEST_SH} -c '
34006f9bef6Schristos			trap "set -n" USR1
34106f9bef6Schristos			{ sleep 1; kill -USR1 $$; sleep 1; } &
34206f9bef6Schristos			false
34306f9bef6Schristos			wait && echo t || echo f
34406f9bef6Schristos			wait
34506f9bef6Schristos			echo foo
34606f9bef6Schristos		'
34706f9bef6Schristos		;;
34806f9bef6Schristos	esac
349c1ce7cf3Schristos}
350c1ce7cf3Schristos
351c1ce7cf3Schristosatf_test_case set_u
352c1ce7cf3Schristosset_u_head() {
353c1ce7cf3Schristos	atf_set "descr" "Tests that 'set -u' turns on unset var detection " \
354c1ce7cf3Schristos	                "and that it behaves as defined by the standard"
355c1ce7cf3Schristos}
356c1ce7cf3Schristosset_u_body() {
357c1ce7cf3Schristos	test_option_on_off u
358c1ce7cf3Schristos
359bdbf3d66Skre	unset ENV	# make sure there is nothing there to cause problems
360bdbf3d66Skre
361c1ce7cf3Schristos	# first make sure it is OK to unset an unset variable
36206f9bef6Schristos	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
363c1ce7cf3Schristos		'unset _UNSET_VARIABLE_; echo OK'
364c1ce7cf3Schristos	# even if -u is set
36506f9bef6Schristos	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -cue \
366c1ce7cf3Schristos		'unset _UNSET_VARIABLE_; echo OK'
367c1ce7cf3Schristos
368c1ce7cf3Schristos	# and that without -u accessing an unset variable is harmless
36906f9bef6Schristos	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
370c1ce7cf3Schristos		'unset X; echo ${X}; echo OK'
371c1ce7cf3Schristos	# and that the unset variable test expansion works properly
37206f9bef6Schristos	atf_check -s exit:0 -o match:OKOK -e empty ${TEST_SH} -ce \
373c1ce7cf3Schristos		'unset X; printf "%s" ${X-OK}; echo OK'
374c1ce7cf3Schristos
375c1ce7cf3Schristos	# Next test that with -u set, the shell aborts on access to unset var
376c1ce7cf3Schristos	# do not use -e, want to make sure it is -u that causes abort
37706f9bef6Schristos	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
378c1ce7cf3Schristos		'unset X; set -u; echo ${X}; echo ERR'
379c1ce7cf3Schristos	# quoting should make no difference...
38006f9bef6Schristos	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
381c1ce7cf3Schristos		'unset X; set -u; echo "${X}"; echo ERR'
382c1ce7cf3Schristos
383c1ce7cf3Schristos	# Now a bunch of accesses to unset vars, with -u, in ways that are OK
38406f9bef6Schristos	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
385c1ce7cf3Schristos		'unset X; set -u; echo ${X-GOOD}; echo OK'
38606f9bef6Schristos	atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \
387c1ce7cf3Schristos		'unset X; set -u; echo ${X-OK}'
388c1ce7cf3Schristos	atf_check -s exit:0 -o not-match:ERR -o match:OK -e empty \
38906f9bef6Schristos		${TEST_SH} -ce 'unset X; set -u; echo ${X+ERR}; echo OK'
390c1ce7cf3Schristos
391c1ce7cf3Schristos	# and some more ways that are not OK
39206f9bef6Schristos	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
393c1ce7cf3Schristos		'unset X; set -u; echo ${X#foo}; echo ERR'
39406f9bef6Schristos	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
395c1ce7cf3Schristos		'unset X; set -u; echo ${X%%bar}; echo ERR'
396c1ce7cf3Schristos
397c1ce7cf3Schristos	# lastly, just while we are checking unset vars, test aborts w/o -u
39806f9bef6Schristos	atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \
399c1ce7cf3Schristos		'unset X; echo ${X?}; echo ERR'
400c1ce7cf3Schristos	atf_check -s not-exit:0 -o not-match:ERR -e match:X_NOT_SET \
40106f9bef6Schristos		${TEST_SH} -c 'unset X; echo ${X?X_NOT_SET}; echo ERR'
402c1ce7cf3Schristos}
403c1ce7cf3Schristos
404c1ce7cf3Schristosatf_test_case set_v
405c1ce7cf3Schristosset_v_head() {
406c1ce7cf3Schristos	atf_set "descr" "Tests that 'set -v' turns on input read echoing " \
407c1ce7cf3Schristos	                "and that it behaves as defined by the standard"
408c1ce7cf3Schristos}
409c1ce7cf3Schristosset_v_body() {
410c1ce7cf3Schristos	test_option_on_off v
411c1ce7cf3Schristos
412c1ce7cf3Schristos	# check that -v does nothing if no later input line is read
413c1ce7cf3Schristos	atf_check -s exit:0 \
414c1ce7cf3Schristos			-o match:OKOK -o not-match:echo -o not-match:printf \
415c1ce7cf3Schristos			-e empty \
41606f9bef6Schristos		${TEST_SH} -ec 'printf "%s" OK; set -v; echo OK; exit 0'
417c1ce7cf3Schristos
418c1ce7cf3Schristos	# but that it does when there are multiple lines
419dd894f6fSchristos	cat <<- 'EOF' |
420dd894f6fSchristos		set -v
421dd894f6fSchristos		printf %s OK
422dd894f6fSchristos		echo OK
423dd894f6fSchristos		exit 0
424dd894f6fSchristos	EOF
425c1ce7cf3Schristos	atf_check -s exit:0 \
426c1ce7cf3Schristos			-o match:OKOK -o not-match:echo -o not-match:printf \
427c1ce7cf3Schristos			-e match:printf -e match:OK -e match:echo \
428dd894f6fSchristos			-e not-match:set ${TEST_SH}
429c1ce7cf3Schristos
430c1ce7cf3Schristos	# and that it can be disabled again
431dd894f6fSchristos	cat <<- 'EOF' |
432dd894f6fSchristos		set -v
433dd894f6fSchristos		printf %s OK
434dd894f6fSchristos		set +v
435dd894f6fSchristos		echo OK
436dd894f6fSchristos		exit 0
437dd894f6fSchristos	EOF
438c1ce7cf3Schristos	atf_check -s exit:0 \
439c1ce7cf3Schristos			-o match:OKOK -o not-match:echo -o not-match:printf \
440c1ce7cf3Schristos			-e match:printf -e match:OK -e not-match:echo \
441dd894f6fSchristos				${TEST_SH}
442c1ce7cf3Schristos
443c1ce7cf3Schristos	# and lastly, that shell keywords do get output when "read"
444dd894f6fSchristos	cat <<- 'EOF' |
445dd894f6fSchristos		set -v
446dd894f6fSchristos		for i in 111 222 333
447dd894f6fSchristos		do
448dd894f6fSchristos			printf %s $i
449dd894f6fSchristos		done
450dd894f6fSchristos		exit 0
451dd894f6fSchristos	EOF
452c1ce7cf3Schristos	atf_check -s exit:0 \
453c1ce7cf3Schristos			-o match:111222333 -o not-match:printf \
454c1ce7cf3Schristos			-o not-match:for -o not-match:do -o not-match:done \
455c1ce7cf3Schristos			-e match:printf -e match:111 -e not-match:111222 \
456c1ce7cf3Schristos			-e match:for -e match:do -e match:done \
457d4ae1f8bSkre				${TEST_SH} ||
458d4ae1f8bSkre		atf_fail '111 222 333 test failure'
459c1ce7cf3Schristos}
460c1ce7cf3Schristos
461c1ce7cf3Schristosatf_test_case set_x
462c1ce7cf3Schristosset_x_head() {
463c1ce7cf3Schristos	atf_set "descr" "Tests that 'set -x' turns on command exec logging " \
464c1ce7cf3Schristos	                "and that it behaves as defined by the standard"
465c1ce7cf3Schristos}
466c1ce7cf3Schristosset_x_body() {
467c1ce7cf3Schristos	test_option_on_off x
468c1ce7cf3Schristos
469c1ce7cf3Schristos	# check that cmd output appears after -x is enabled
470c1ce7cf3Schristos	atf_check -s exit:0 \
471c1ce7cf3Schristos			-o match:OKOK -o not-match:echo -o not-match:printf \
472c1ce7cf3Schristos			-e not-match:printf -e match:OK -e match:echo \
47306f9bef6Schristos		${TEST_SH} -ec 'printf "%s" OK; set -x; echo OK; exit 0'
474c1ce7cf3Schristos
475c1ce7cf3Schristos	# and that it stops again afer -x is disabled
476c1ce7cf3Schristos	atf_check -s exit:0 \
477c1ce7cf3Schristos			-o match:OKOK -o not-match:echo -o not-match:printf \
478c1ce7cf3Schristos			-e match:printf -e match:OK -e not-match:echo \
47906f9bef6Schristos	    ${TEST_SH} -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0'
480c1ce7cf3Schristos
481c1ce7cf3Schristos	# also check that PS4 is output correctly
482c1ce7cf3Schristos	atf_check -s exit:0 \
483c1ce7cf3Schristos			-o match:OK -o not-match:echo \
484c1ce7cf3Schristos			-e match:OK -e match:Run:echo \
48506f9bef6Schristos		${TEST_SH} -ec 'PS4=Run:; set -x; echo OK; exit 0'
486c1ce7cf3Schristos
487c1ce7cf3Schristos	return 0
488c1ce7cf3Schristos
489c1ce7cf3Schristos	# This one seems controversial... I suspect it is NetBSD's sh
490c1ce7cf3Schristos	# that is wrong to not output "for" "while" "if" ... etc
491c1ce7cf3Schristos
492c1ce7cf3Schristos	# and lastly, that shell keywords do not get output when "executed"
493c1ce7cf3Schristos	atf_check -s exit:0 \
494c1ce7cf3Schristos			-o match:111222333 -o not-match:printf \
495c1ce7cf3Schristos			-o not-match:for \
496c1ce7cf3Schristos			-e match:printf -e match:111 -e not-match:111222 \
497c1ce7cf3Schristos			-e not-match:for -e not-match:do -e not-match:done \
49806f9bef6Schristos		${TEST_SH} -ec \
499c1ce7cf3Schristos	   'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0'
500c1ce7cf3Schristos}
501c1ce7cf3Schristos
502a60600e0Skreatf_test_case set_X
503a60600e0Skreset_X_head() {
504a60600e0Skre	atf_set "descr" "Tests that 'set -X' turns on command exec logging " \
505a60600e0Skre	                "and that it enables set -x and retains a single fd"
506a60600e0Skre}
507a60600e0Skreset_X_body() {
508a60600e0Skre
509a60600e0Skre	# First we need to verify that $TEST_SH supports -X
510a60600e0Skre	test_optional_on_off X					||
511a60600e0Skre		atf_skip "$TEST_SH does not support -X"
512a60600e0Skre
513a60600e0Skre	# and that the -X it implements is the -X we expect
514a60600e0Skre	$TEST_SH -c 'exec 2>/dev/null;
515a60600e0Skre		set +x; set -X;
516a60600e0Skre		case "$-" in (*x*) exit 0;; esac;
517a60600e0Skre		exit 1'						||
518a60600e0Skre			atf_skip "$TEST_SH supports -X but not 'the' -X"
519a60600e0Skre
520a60600e0Skre	# Above has already tested that set -X => set -x
521a60600e0Skre	# Now test that set +X => set +x
522a60600e0Skre	# and that set -x and set +x do not affect -X
523a60600e0Skre
524a60600e0Skre	atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \
525a60600e0Skre		'set -x; set +X; case "$-" in (*x*) echo FAIL; exit 1;; esac'
526a60600e0Skre
527a60600e0Skre	atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \
528a60600e0Skre		'set -X; set +x;
529a60600e0Skre		 case "$-" in (*x*) echo FAIL; exit 1;; esac
530a60600e0Skre		 case "$-" in (*X*) exit 0;; esac; echo ERROR; exit 2'
531a60600e0Skre
532a60600e0Skre	atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \
533a60600e0Skre		'set -X; set +x; set -x;
534a60600e0Skre		 case "$-" in (*x*X*|*X*x*) exit 0;; esac; echo ERROR; exit 2'
535a60600e0Skre
536a60600e0Skre	atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \
537a60600e0Skre		'set +X; set -x;
538a60600e0Skre		 case "$-" in (*X*) echo FAIL; exit 1;; esac
539a60600e0Skre		 case "$-" in (*x*) exit 0;; esac; echo ERROR; exit 2'
540a60600e0Skre
541a60600e0Skre	atf_check -s exit:0 -o empty -e ignore ${TEST_SH} -c \
542a60600e0Skre		'set +X; set -x; set +x;
543a60600e0Skre		 case "$-" in (*[xX]*) echo FAULT; exit 3;; esac'
544a60600e0Skre
545a60600e0Skre	# The following just verify regular tracing using -X instead of -x
546a60600e0Skre	# These are the same tests as the -x test (set_x) performs.
547a60600e0Skre
548a60600e0Skre	# check that cmd output appears after -X is enabled
549a60600e0Skre	atf_check -s exit:0 \
550a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
551a60600e0Skre			-e not-match:printf -e match:OK -e match:echo \
552a60600e0Skre		${TEST_SH} -ec 'printf "%s" OK; set -X; echo OK; exit 0'
553a60600e0Skre
554a60600e0Skre	# and that it stops again afer -X is disabled
555a60600e0Skre	atf_check -s exit:0 \
556a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
557a60600e0Skre			-e match:printf -e match:OK -e not-match:echo \
558a60600e0Skre	    ${TEST_SH} -ec 'set -X; printf "%s" OK; set +X; echo OK; exit 0'
559a60600e0Skre
560a60600e0Skre	# also check that PS4 is output correctly
561a60600e0Skre	atf_check -s exit:0 \
562a60600e0Skre			-o match:OK -o not-match:echo \
563a60600e0Skre			-e match:OK -e match:Run:echo \
564a60600e0Skre		${TEST_SH} -ec 'PS4=Run:; set -X; echo OK; exit 0'
565a60600e0Skre
566a60600e0Skre	# end copies of -x tests ...
567a60600e0Skre
568a60600e0Skre	# now check that we can move stderr around without affecting -X output
569a60600e0Skre
570a60600e0Skre	atf_check -s exit:0 \
571a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
572a60600e0Skre			-e match:printf -e match:OK -e match:echo \
573a60600e0Skre		${TEST_SH} -ecX 'printf "%s" OK; exec 2>/dev/null; echo OK'
574a60600e0Skre	atf_check -s exit:0 \
575a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
576a60600e0Skre			-e match:printf -e match:OK -e match:echo \
577a60600e0Skre		${TEST_SH} -ecX 'printf "%s" OK; exec 2>&1; echo OK'
578a60600e0Skre	atf_check -s exit:0 \
579a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
580a60600e0Skre			-e match:printf -e match:OK -e match:echo \
581a60600e0Skre		${TEST_SH} -ecX 'printf "%s" OK; exec 2>&-; echo OK'
582a60600e0Skre
583a60600e0Skre	# and that we can put tracing on an external file, leaving stderr alone
584a60600e0Skre
585a60600e0Skre	atf_require_prog grep
586a60600e0Skre
587a60600e0Skre	rm -f X-trace
588a60600e0Skre	atf_check -s exit:0 \
589a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
590a60600e0Skre			-e empty \
591a60600e0Skre		${TEST_SH} -ec 'PS4=; set -X 2>X-trace; printf "%s" OK; echo OK'
592a60600e0Skre	test -s X-trace || atf_fail "T1: Failed to create trace output file"
593a60600e0Skre	grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace ||
594a60600e0Skre		atf_fail "T1: -X tracing missing printf"
595a60600e0Skre	grep >/dev/null 2>&1 'echo.*OK' X-trace ||
596a60600e0Skre		atf_fail "T1: -X tracing missing echo"
597a60600e0Skre
598a60600e0Skre	rm -f X-trace
599a60600e0Skre	atf_check -s exit:0 \
600a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
601a60600e0Skre			-e empty \
602a60600e0Skre		${TEST_SH} -ec \
603a60600e0Skre			'PS4=; set -X 2>X-trace;
604a60600e0Skre			printf "%s" OK;
605a60600e0Skre			exec 2>/dev/null;
606a60600e0Skre			echo OK'
607a60600e0Skre	test -s X-trace || atf_fail "T2: Failed to create trace output file"
608a60600e0Skre	grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace ||
609a60600e0Skre		atf_fail "T2: -X tracing missing printf"
610a60600e0Skre	grep >/dev/null 2>&1 'exec' X-trace ||
611a60600e0Skre		atf_fail "T2: -X tracing missing exec"
612a60600e0Skre	grep >/dev/null 2>&1 'echo.*OK' X-trace ||
613a60600e0Skre		atf_fail "T2: -X tracing missing echo after stderr redirect"
614a60600e0Skre
615a60600e0Skre	rm -f X-trace
616a60600e0Skre	atf_check -s exit:0 \
617a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
618a60600e0Skre			-e empty \
619a60600e0Skre		${TEST_SH} -ec \
620a60600e0Skre			'PS4=; set -X 2>X-trace;
621a60600e0Skre			printf "%s" OK;
622a60600e0Skre			set -X 2>/dev/null;
623a60600e0Skre			echo OK'
624a60600e0Skre	test -s X-trace || atf_fail "T3: Failed to create trace output file"
625a60600e0Skre	grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace ||
626a60600e0Skre		atf_fail "T3: -X tracing missing printf"
627a60600e0Skre	grep >/dev/null 2>&1 'set.*-X' X-trace ||
628a60600e0Skre		atf_fail "T3: -X tracing missing set -X"
629a60600e0Skre	grep >/dev/null 2>&1 'echo.*OK' X-trace &&
630a60600e0Skre		atf_fail "T3: -X tracing included echo after set -X redirect"
631a60600e0Skre
632a60600e0Skre	rm -f X-trace
633a60600e0Skre	atf_check -s exit:0 \
634a60600e0Skre			-o match:OKOK -o not-match:echo -o not-match:printf \
635a60600e0Skre			-e match:echo -e match:OK -e not-match:printf \
636a60600e0Skre		${TEST_SH} -ec \
637a60600e0Skre			'PS4=; set -X 2>X-trace;
638a60600e0Skre			printf "%s" OK;
639a60600e0Skre			set -X;
640a60600e0Skre			echo OK'
641a60600e0Skre	test -s X-trace || atf_fail "T4: Failed to create trace output file"
642a60600e0Skre	grep >/dev/null 2>&1 'printf.*%s.*OK' X-trace ||
643a60600e0Skre		atf_fail "T4: -X tracing missing printf"
644a60600e0Skre	grep >/dev/null 2>&1 'set.*-X' X-trace ||
645a60600e0Skre		atf_fail "T4: -X tracing missing set -X"
646a60600e0Skre	grep >/dev/null 2>&1 'echo.*OK' X-trace &&
647a60600e0Skre		atf_fail "T4: -X tracing included echo after set -X redirect"
648a60600e0Skre
649a60600e0Skre	# Now check that -X and the tracing files work properly wrt functions
650a60600e0Skre
651a60600e0Skre	# a shell that supports -X should support "local -" ... but verify
652a60600e0Skre
653a60600e0Skre	( ${TEST_SH} -c 'fn() { local - || exit 2; set -f; }; set +f; fn;
654a60600e0Skre		case "$-" in ("*f*") exit 1;; esac; exit 0' ) 2>/dev/null ||
655a60600e0Skre			atf_skip "-X function test: 'local -' unsupported"
656a60600e0Skre
657a60600e0Skre	rm -f X-trace X-trace-fn
658a60600e0Skre	atf_check -s exit:0 \
659a60600e0Skre			-o match:OKhelloGOOD		\
660a60600e0Skre			-e empty			\
661a60600e0Skre		${TEST_SH} -c '
662a60600e0Skre			say() {
663a60600e0Skre				printf "%s" "$*"
664a60600e0Skre			}
665a60600e0Skre			funct() {
666a60600e0Skre				local -
667a60600e0Skre
668a60600e0Skre				set -X 2>X-trace-fn
669a60600e0Skre				say hello
670a60600e0Skre			}
671a60600e0Skre
672a60600e0Skre			set -X 2>X-trace
673a60600e0Skre
674a60600e0Skre			printf OK
675a60600e0Skre			funct
676a60600e0Skre			echo GOOD
677a60600e0Skre		'
678a60600e0Skre	test -s X-trace || atf_fail "T5: Failed to create trace output file"
679a60600e0Skre	test -s X-trace-fn || atf_fail "T5: Failed to create fn trace output"
680a60600e0Skre	grep >/dev/null 2>&1 'printf.*OK' X-trace ||
681a60600e0Skre		atf_fail "T5: -X tracing missing printf"
682a60600e0Skre	grep >/dev/null 2>&1 funct X-trace ||
683a60600e0Skre		atf_fail "T5: -X tracing missing funct"
684a60600e0Skre	grep >/dev/null 2>&1 'set.*-X' X-trace ||
685a60600e0Skre		atf_fail "T5: -X tracing missing set -X from in funct"
686a60600e0Skre	grep >/dev/null 2>&1 'echo.*GOOD' X-trace ||
687a60600e0Skre		atf_fail "T5: -X tracing missing echo after funct redirect"
688a60600e0Skre	grep >/dev/null 2>&1 'say.*hello' X-trace &&
689a60600e0Skre		atf_fail "T5: -X tracing included 'say' after funct redirect"
690a60600e0Skre	grep >/dev/null 2>&1 'say.*hello' X-trace-fn ||
691a60600e0Skre		atf_fail "T5: -X funct tracing missed 'say'"
692a60600e0Skre
693a60600e0Skre	rm -f X-trace X-trace-fn
694a60600e0Skre
695a60600e0Skre	atf_check -s exit:0 \
696a60600e0Skre			-o match:OKhelloGOOD		\
697a60600e0Skre			-e empty			\
698a60600e0Skre		${TEST_SH} -c '
699a60600e0Skre			say() {
700a60600e0Skre				printf "%s" "$*"
701a60600e0Skre			}
702a60600e0Skre			funct() {
703a60600e0Skre				local -
704a60600e0Skre
705a60600e0Skre				set +X
706a60600e0Skre				say hello
707a60600e0Skre			}
708a60600e0Skre
709a60600e0Skre			set -X 2>X-trace
710a60600e0Skre
711a60600e0Skre			printf OK
712a60600e0Skre			funct
713a60600e0Skre			echo GOOD
714a60600e0Skre		'
715a60600e0Skre	test -s X-trace || atf_fail "T6: Failed to create trace output file"
716a60600e0Skre	grep >/dev/null 2>&1 'printf.*OK' X-trace ||
717a60600e0Skre		atf_fail "T6: -X tracing missing printf"
718a60600e0Skre	grep >/dev/null 2>&1 funct X-trace ||
719a60600e0Skre		atf_fail "T6: -X tracing missing funct"
720a60600e0Skre	grep >/dev/null 2>&1 'set.*+X' X-trace ||
721a60600e0Skre		atf_fail "T6: -X tracing missing set +X from in funct"
722a60600e0Skre	grep >/dev/null 2>&1 'echo.*GOOD' X-trace ||
723a60600e0Skre		atf_fail "T6: -X tracing missing echo after funct redirect"
724a60600e0Skre	grep >/dev/null 2>&1 'say.*hello' X-trace &&
725a60600e0Skre		atf_fail "T6: -X tracing included 'say' after funct redirect"
726a60600e0Skre
727a60600e0Skre	rm -f X-trace
728a60600e0Skre
729a60600e0Skre	atf_check -s exit:0 \
730a60600e0Skre			-o match:OKtracednotraceGOOD \
731a60600e0Skre			-e match:say -e match:traced -e not-match:notrace \
732a60600e0Skre		${TEST_SH} -c '
733a60600e0Skre			say() {
734a60600e0Skre				printf "%s" "$*"
735a60600e0Skre			}
736a60600e0Skre			funct() {
737a60600e0Skre				local -
738a60600e0Skre
739a60600e0Skre				set +X -x
740a60600e0Skre
741a60600e0Skre				say traced
742a60600e0Skre				exec 2>/dev/null
743a60600e0Skre				say notrace
744a60600e0Skre
745a60600e0Skre			}
746a60600e0Skre
747a60600e0Skre			set -X 2>X-trace
748a60600e0Skre
749a60600e0Skre			printf OK
750a60600e0Skre			funct
751a60600e0Skre			echo GOOD
752a60600e0Skre		'
753a60600e0Skre	test -s X-trace || atf_fail "T7: Failed to create trace output file"
754a60600e0Skre	grep >/dev/null 2>&1 'printf.*OK' X-trace ||
755a60600e0Skre		atf_fail "T7: -X tracing missing printf"
756a60600e0Skre	grep >/dev/null 2>&1 funct X-trace ||
757a60600e0Skre		atf_fail "T7: -X tracing missing funct"
758a60600e0Skre	grep >/dev/null 2>&1 'set.*+X.*-x' X-trace ||
759a60600e0Skre		atf_fail "T7: -X tracing missing set +X -x from in funct"
760a60600e0Skre	grep >/dev/null 2>&1 'echo.*GOOD' X-trace ||
761a60600e0Skre		atf_fail "T7: -X tracing missing echo after funct +X"
762a60600e0Skre	grep >/dev/null 2>&1 'say.*hello' X-trace &&
763a60600e0Skre		atf_fail "T7: -X tracing included 'say' after funct +X"
764a60600e0Skre
765a60600e0Skre	rm -f X-trace X-trace-fn
766a60600e0Skre	atf_check -s exit:0 \
767a60600e0Skre			-o "match:OKg'daybye-bye.*hello.*GOOD"		\
768a60600e0Skre			-e empty 					\
769a60600e0Skre		${TEST_SH} -c '
770a60600e0Skre			say() {
771a60600e0Skre				printf "%s" "$*"
772a60600e0Skre			}
773a60600e0Skre			fn1() {
774a60600e0Skre				local -
775a60600e0Skre
776a60600e0Skre				set -X 2>>X-trace-fn
777a60600e0Skre				say "g'\''day"
778a60600e0Skre				"$@"
779a60600e0Skre				say bye-bye
780a60600e0Skre			}
781a60600e0Skre			fn2() {
782a60600e0Skre				set +X
783a60600e0Skre				say hello
784a60600e0Skre				"$@"
785a60600e0Skre				say goodbye
786a60600e0Skre			}
787a60600e0Skre
788a60600e0Skre			set -X 2>X-trace
789a60600e0Skre
790a60600e0Skre			printf OK
791a60600e0Skre			fn1
792a60600e0Skre			fn1 fn2
793a60600e0Skre			fn1 fn1 fn2
794a60600e0Skre			fn1 fn2 fn1 fn2 fn1
795a60600e0Skre			fn1 fn1 fn2 fn2 fn1
796a60600e0Skre			echo GOOD
797a60600e0Skre		'
798a60600e0Skre
799a60600e0Skre	# That test generally succeeds if the earlier ones did
800a60600e0Skre	# and if it did not dump core!
801a60600e0Skre
802a60600e0Skre	# But we can check a few things...
803a60600e0Skre
804a60600e0Skre	test -s X-trace || atf_fail "T8: Failed to create trace output file"
805a60600e0Skre	test -s X-trace-fn || atf_fail "T8: Failed to create trace output file"
806a60600e0Skre	grep >/dev/null 2>&1 'printf.*OK' X-trace ||
807a60600e0Skre		atf_fail "T8: -X tracing missing printf"
808a60600e0Skre	grep >/dev/null 2>&1 fn1 X-trace ||
809a60600e0Skre		atf_fail "T8: -X tracing missing fn1"
810a60600e0Skre	grep >/dev/null 2>&1 'set.*-X' X-trace ||
811a60600e0Skre		atf_fail "T8: -X tracing missing set -X from in fn1"
812a60600e0Skre	grep >/dev/null 2>&1 'echo.*GOOD' X-trace ||
813a60600e0Skre		atf_fail "T8: -X tracing missing echo after fn1 redirect"
814a60600e0Skre	grep >/dev/null 2>&1 'say.*hello' X-trace &&
815a60600e0Skre		atf_fail "T8: -X tracing included 'say' after fn2 +X"
816a60600e0Skre	grep >/dev/null 2>&1 'say.*hello' X-trace-fn &&
817a60600e0Skre		atf_fail "T8: -X fn tracing included 'say' after fn2 +X"
818a60600e0Skre
819a60600e0Skre
820a60600e0Skre	rm -f X-trace
821a60600e0Skre
822a60600e0Skre	return 0
823a60600e0Skre}
824a60600e0Skre
825c1ce7cf3Schristosopt_test_setup()
826c1ce7cf3Schristos{
827c1ce7cf3Schristos	test -n "$1" || { echo >&2 "Internal error"; exit 1; }
828c1ce7cf3Schristos
829c1ce7cf3Schristos	cat > "$1" << 'END_OF_FUNCTIONS'
830c1ce7cf3Schristoslocal_opt_check()
831c1ce7cf3Schristos{
832c1ce7cf3Schristos	local -
833c1ce7cf3Schristos}
834c1ce7cf3Schristos
835c1ce7cf3Schristosinstr()
836c1ce7cf3Schristos{
837c1ce7cf3Schristos	expr "$2" : "\(.*$1\)" >/dev/null
838c1ce7cf3Schristos}
839c1ce7cf3Schristos
840c1ce7cf3Schristossave_opts()
841c1ce7cf3Schristos{
842c1ce7cf3Schristos	local -
843c1ce7cf3Schristos
844c1ce7cf3Schristos	set -e
845c1ce7cf3Schristos	set -u
846c1ce7cf3Schristos
847c1ce7cf3Schristos	instr e "$-" && instr u "$-" && return 0
848c1ce7cf3Schristos	echo ERR
849c1ce7cf3Schristos}
850c1ce7cf3Schristos
851c1ce7cf3Schristosfiddle_opts()
852c1ce7cf3Schristos{
853c1ce7cf3Schristos	set -e
854c1ce7cf3Schristos	set -u
855c1ce7cf3Schristos
856c1ce7cf3Schristos	instr e "$-" && instr u "$-" && return 0
857c1ce7cf3Schristos	echo ERR
858c1ce7cf3Schristos}
859c1ce7cf3Schristos
860c1ce7cf3Schristoslocal_test()
861c1ce7cf3Schristos{
862c1ce7cf3Schristos	set +eu
863c1ce7cf3Schristos
864c1ce7cf3Schristos	save_opts
865c1ce7cf3Schristos	instr '[eu]' "$-" || printf %s "OK"
866c1ce7cf3Schristos
867c1ce7cf3Schristos	fiddle_opts
868c1ce7cf3Schristos	instr e "$-" && instr u "$-" && printf %s "OK"
869c1ce7cf3Schristos
870c1ce7cf3Schristos	set +eu
871c1ce7cf3Schristos}
872c1ce7cf3SchristosEND_OF_FUNCTIONS
873c1ce7cf3Schristos}
874c1ce7cf3Schristos
875c1ce7cf3Schristosatf_test_case restore_local_opts
876c1ce7cf3Schristosrestore_local_opts_head() {
877c1ce7cf3Schristos	atf_set "descr" "Tests that 'local -' saves and restores options.  " \
878c1ce7cf3Schristos			"Note that "local" is a local shell addition"
879c1ce7cf3Schristos}
880c1ce7cf3Schristosrestore_local_opts_body() {
881c1ce7cf3Schristos	atf_require_prog cat
882c1ce7cf3Schristos	atf_require_prog expr
883c1ce7cf3Schristos
884c1ce7cf3Schristos	FN="test-funcs.$$"
885c1ce7cf3Schristos	opt_test_setup "${FN}" || atf_skip "Cannot setup test environment"
886c1ce7cf3Schristos
88706f9bef6Schristos	${TEST_SH} -ec ". './${FN}'; local_opt_check" 2>/dev/null ||
888c1ce7cf3Schristos		atf_skip "sh extension 'local -' not supported by ${TEST_SH}"
889c1ce7cf3Schristos
890c1ce7cf3Schristos	atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \
89106f9bef6Schristos		${TEST_SH} -ec ". './${FN}'; local_test"
892c1ce7cf3Schristos}
893c1ce7cf3Schristos
894c1ce7cf3Schristosatf_test_case vi_emacs_VE_toggle
895c1ce7cf3Schristosvi_emacs_VE_toggle_head() {
896c1ce7cf3Schristos	atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\
897c1ce7cf3Schristos			"  Note that -V and -E are local shell additions"
898c1ce7cf3Schristos}
899c1ce7cf3Schristosvi_emacs_VE_toggle_body() {
900c1ce7cf3Schristos
901c1ce7cf3Schristos	test_optional_on_off V E ||
902c1ce7cf3Schristos	  atf_skip "One or both V & E opts unsupported by ${TEST_SH}"
903c1ce7cf3Schristos
90406f9bef6Schristos	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '
905c1ce7cf3Schristos		q() {
906c1ce7cf3Schristos			eval "case \"$-\" in
907c1ce7cf3Schristos			(*${2}*)	return 1;;
908c1ce7cf3Schristos			(*${1}*)	return 0;;
909c1ce7cf3Schristos			esac"
910c1ce7cf3Schristos			return 1
911c1ce7cf3Schristos		}
912c1ce7cf3Schristos		x() {
913c1ce7cf3Schristos			echo >&2 "Option set or toggle failure:" \
914c1ce7cf3Schristos					" on=$1 off=$2 set=$-"
915c1ce7cf3Schristos			exit 1
916c1ce7cf3Schristos		}
917c1ce7cf3Schristos		set -V; q V E || x V E
918c1ce7cf3Schristos		set -E; q E V || x E V
919c1ce7cf3Schristos		set -V; q V E || x V E
920c1ce7cf3Schristos		set +EV; q "" "[VE]" || x "" VE
921c1ce7cf3Schristos		exit 0
922c1ce7cf3Schristos	'
923c1ce7cf3Schristos}
924c1ce7cf3Schristos
92556aa5062Skreatf_test_case pipefail
92656aa5062Skrepipefail_head() {
92756aa5062Skre	atf_set "descr" "Basic tests of the pipefail option"
92856aa5062Skre}
92956aa5062Skrepipefail_body() {
93056aa5062Skre	${TEST_SH} -c 'set -o pipefail' 2>/dev/null ||
93156aa5062Skre		atf_skip "pipefail option not supported by ${TEST_SH}"
93256aa5062Skre
93356aa5062Skre	atf_check -s exit:0 -o match:'pipefail.*off' -e empty ${TEST_SH} -c \
93456aa5062Skre		'set -o | grep pipefail'
93556aa5062Skre	atf_check -s exit:0 -o match:'pipefail.*on' -e empty ${TEST_SH} -c \
93656aa5062Skre		'set -o pipefail; set -o | grep pipefail'
93756aa5062Skre
93856aa5062Skre	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
93956aa5062Skre		'(exit 1) | (exit 2) | (exit 0)'
94056aa5062Skre	atf_check -s exit:2 -o empty -e empty ${TEST_SH} -c \
94156aa5062Skre		'set -o pipefail; (exit 1) | (exit 2) | (exit 0)'
94256aa5062Skre	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
94356aa5062Skre		'set -o pipefail; (exit 1) | (exit 0) | (exit 0)'
94456aa5062Skre	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
94556aa5062Skre		'set -o pipefail; (exit 0) | (exit 0) | (exit 0)'
94656aa5062Skre
94756aa5062Skre	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
94856aa5062Skre		'! (exit 1) | (exit 2) | (exit 0)'
94956aa5062Skre	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
95056aa5062Skre		'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0)'
95156aa5062Skre	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
95256aa5062Skre		'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0)'
95356aa5062Skre	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
95456aa5062Skre		'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0)'
95556aa5062Skre
95656aa5062Skre	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
95756aa5062Skre		'(exit 1) | (exit 2) | (exit 0); echo $?'
95856aa5062Skre	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
95956aa5062Skre		'set -o pipefail; (exit 1) | (exit 2) | (exit 0); echo $?'
96056aa5062Skre	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
96156aa5062Skre		'set -o pipefail; (exit 1) | (exit 0) | (exit 0); echo $?'
96256aa5062Skre	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
96356aa5062Skre		'set -o pipefail; (exit 0) | (exit 0) | (exit 0); echo $?'
96456aa5062Skre
96556aa5062Skre	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
96656aa5062Skre		'! (exit 1) | (exit 2) | (exit 0); echo $?'
96756aa5062Skre	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
96856aa5062Skre		'set -o pipefail; ! (exit 1) | (exit 2) | (exit 0); echo $?'
96956aa5062Skre	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
97056aa5062Skre		'set -o pipefail; ! (exit 1) | (exit 0) | (exit 0); echo $?'
97156aa5062Skre	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
97256aa5062Skre		'set -o pipefail; ! (exit 0) | (exit 0) | (exit 0); echo $?'
97356aa5062Skre}
97456aa5062Skre
975c1ce7cf3Schristosatf_test_case xx_bogus
976c1ce7cf3Schristosxx_bogus_head() {
977c1ce7cf3Schristos	atf_set "descr" "Tests that attempting to set a nonsense option fails."
978c1ce7cf3Schristos}
979c1ce7cf3Schristosxx_bogus_body() {
980c1ce7cf3Schristos	# Biggest problem here is picking a "nonsense option" that is
981c1ce7cf3Schristos	# not implemented by any shell, anywhere.  Hopefully this will do.
982dd894f6fSchristos
983dd894f6fSchristos	# 'set' is a special builtin, so a conforming shell should exit
984dd894f6fSchristos	# on an arg error, and the ERR should not be printed.
985c1ce7cf3Schristos	atf_check -s not-exit:0 -o empty -e not-empty \
98606f9bef6Schristos		${TEST_SH} -c 'set -% ; echo ERR'
987c1ce7cf3Schristos}
988c1ce7cf3Schristos
989c1ce7cf3Schristosatf_test_case Option_switching
990c1ce7cf3SchristosOption_switching_head() {
991c1ce7cf3Schristos	atf_set "descr" "options can be enabled and disabled"
992c1ce7cf3Schristos}
993c1ce7cf3SchristosOption_switching_body() {
994c1ce7cf3Schristos
995c1ce7cf3Schristos	# Cannot test -m, setting it causes test shell to fail...
996c1ce7cf3Schristos	# (test shell gets SIGKILL!)  Wonder why ... something related to atf
997c1ce7cf3Schristos	# That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'"
998c1ce7cf3Schristos
999c1ce7cf3Schristos	# Don't bother testing toggling -n, once on, it stays on...
1000c1ce7cf3Schristos	# (and because the test fn refuses to allow us to try)
1001c1ce7cf3Schristos
1002c1ce7cf3Schristos	# Cannot test -o or -c here, or the extension -s
1003c1ce7cf3Schristos	# they can only be used, not switched
1004c1ce7cf3Schristos
1005c1ce7cf3Schristos	# these are the posix options, that all shells should implement
1006c1ce7cf3Schristos	test_option_on_off a b C e f h u v x      # m
1007c1ce7cf3Schristos
1008c1ce7cf3Schristos	# and these are extensions that might not exist (non-fatal to test)
1009c1ce7cf3Schristos	# -i and -s (and -c) are posix options, but are not required to
1010*f42f89fdSandvar	# be accessible via the "set" command, just the command line.
1011c1ce7cf3Schristos	# We allow for -i to work with set, as that makes some sense,
1012c1ce7cf3Schristos	# -c and -s do not.
1013a60600e0Skre	test_optional_on_off E i I p q V X || true
1014c1ce7cf3Schristos
1015c1ce7cf3Schristos	# Also test (some) option combinations ...
1016c1ce7cf3Schristos	# only testing posix options here, because it is easier...
1017c1ce7cf3Schristos	test_option_on_off aeu vx Ca aCefux
1018c1ce7cf3Schristos}
1019c1ce7cf3Schristos
1020c1ce7cf3Schristosatf_init_test_cases() {
1021c1ce7cf3Schristos	# tests are run in order sort of names produces, so choose names wisely
1022c1ce7cf3Schristos
1023c1ce7cf3Schristos	# this one tests turning on/off all the mandatory. and extra flags
1024c1ce7cf3Schristos	atf_add_test_case Option_switching
1025c1ce7cf3Schristos	# and this tests the NetBSD "local -" functionality in functions.
1026c1ce7cf3Schristos	atf_add_test_case restore_local_opts
1027c1ce7cf3Schristos
1028c1ce7cf3Schristos	# no tests for	-m (no idea how to do that one)
1029c1ce7cf3Schristos	#		-I (no easy way to generate the EOF it ignores)
1030c1ce7cf3Schristos	#		-i (not sure how to test that one at the minute)
103106f9bef6Schristos	#		-p (because we aren't going to run tests setuid)
1032c1ce7cf3Schristos	#		-V/-E (too much effort, and a real test would be huge)
1033c1ce7cf3Schristos	#		-c (because almost all the other tests test it anyway)
1034c1ce7cf3Schristos	#		-q (because, for now, I am lazy)
1035c1ce7cf3Schristos	#		-s (coming soon, hopefully)
1036c1ce7cf3Schristos	#		-o (really +o: again, hopefully soon)
1037c1ce7cf3Schristos	#		-o longname (again, just laziness, don't wait...)
1038c1ce7cf3Schristos	# 		-h/-b (because NetBSD doesn't implement them)
1039c1ce7cf3Schristos	atf_add_test_case set_a
1040c1ce7cf3Schristos	atf_add_test_case set_C
1041c1ce7cf3Schristos	atf_add_test_case set_e
1042c1ce7cf3Schristos	atf_add_test_case set_f
1043c1ce7cf3Schristos	atf_add_test_case set_n
1044c1ce7cf3Schristos	atf_add_test_case set_u
1045c1ce7cf3Schristos	atf_add_test_case set_v
1046c1ce7cf3Schristos	atf_add_test_case set_x
1047a60600e0Skre	atf_add_test_case set_X
1048c1ce7cf3Schristos
1049c1ce7cf3Schristos	atf_add_test_case vi_emacs_VE_toggle
105056aa5062Skre
105156aa5062Skre	atf_add_test_case pipefail
105256aa5062Skre
1053c1ce7cf3Schristos	atf_add_test_case xx_bogus
1054c1ce7cf3Schristos}
1055