1313498Sngie# $NetBSD: t_expand.sh,v 1.8 2016/04/29 18:29:17 christos Exp $
2272343Sngie#
3272343Sngie# Copyright (c) 2007, 2009 The NetBSD Foundation, Inc.
4272343Sngie# All rights reserved.
5272343Sngie#
6272343Sngie# Redistribution and use in source and binary forms, with or without
7272343Sngie# modification, are permitted provided that the following conditions
8272343Sngie# are met:
9272343Sngie# 1. Redistributions of source code must retain the above copyright
10272343Sngie#    notice, this list of conditions and the following disclaimer.
11272343Sngie# 2. Redistributions in binary form must reproduce the above copyright
12272343Sngie#    notice, this list of conditions and the following disclaimer in the
13272343Sngie#    documentation and/or other materials provided with the distribution.
14272343Sngie#
15272343Sngie# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16272343Sngie# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17272343Sngie# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18272343Sngie# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19272343Sngie# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20272343Sngie# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21272343Sngie# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22272343Sngie# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23272343Sngie# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24272343Sngie# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25272343Sngie# POSSIBILITY OF SUCH DAMAGE.
26272343Sngie#
27313498Sngie# the implementation of "sh" to test
28313498Sngie: ${TEST_SH:="/bin/sh"}
29272343Sngie
30272343Sngie#
31272343Sngie# This file tests the functions in expand.c.
32272343Sngie#
33272343Sngie
34272343Sngiedelim_argv() {
35272343Sngie	str=
36272343Sngie	while [ $# -gt 0 ]; do
37272343Sngie		if [ -z "${str}" ]; then
38272343Sngie			str=">$1<"
39272343Sngie		else
40272343Sngie			str="${str} >$1<"
41272343Sngie		fi
42272343Sngie		shift
43272343Sngie	done
44272343Sngie	echo ${str}
45272343Sngie}
46272343Sngie
47272343Sngieatf_test_case dollar_at
48272343Sngiedollar_at_head() {
49272343Sngie	atf_set "descr" "Somewhere between 2.0.2 and 3.0 the expansion" \
50272343Sngie	                "of the \$@ variable had been broken.  Check for" \
51272343Sngie			"this behavior."
52272343Sngie}
53272343Sngiedollar_at_body() {
54272343Sngie	# This one should work everywhere.
55313498Sngie	atf_check -s exit:0 -o inline:' EOL\n' -e empty \
56313498Sngie		${TEST_SH} -c 'echo "" "" | '" sed 's,\$,EOL,'"
57272343Sngie
58272343Sngie	# This code triggered the bug.
59313498Sngie	atf_check -s exit:0 -o inline:' EOL\n' -e empty \
60313498Sngie		${TEST_SH} -c 'set -- "" ""; echo "$@" | '" sed 's,\$,EOL,'"
61272343Sngie
62313498Sngie	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
63313498Sngie		'set -- -; shift; n_arg() { echo $#; }; n_arg "$@"'
64272343Sngie}
65272343Sngie
66272343Sngieatf_test_case dollar_at_with_text
67272343Sngiedollar_at_with_text_head() {
68272343Sngie	atf_set "descr" "Test \$@ expansion when it is surrounded by text" \
69272343Sngie	                "within the quotes.  PR bin/33956."
70272343Sngie}
71272343Sngiedollar_at_with_text_body() {
72272343Sngie
73313498Sngie	cat <<'EOF' > h-f1
74313498Sngie
75313498Sngiedelim_argv() {
76313498Sngie	str=
77313498Sngie	while [ $# -gt 0 ]; do
78313498Sngie		if [ -z "${str}" ]; then
79313498Sngie			str=">$1<"
80313498Sngie		else
81313498Sngie			str="${str} >$1<"
82313498Sngie		fi
83313498Sngie		shift
84313498Sngie	done
85313498Sngie	echo "${str}"
86272343Sngie}
87272343Sngie
88313498SngieEOF
89313498Sngie	cat <<'EOF' > h-f2
90313498Sngie
91313498Sngiedelim_argv() {
92313498Sngie	str=
93313498Sngie	while [ $# -gt 0 ]; do
94313498Sngie
95313498Sngie		str="${str}${str:+ }>$1<"
96313498Sngie		shift
97313498Sngie
98313498Sngie	done
99313498Sngie	echo "${str}"
100313498Sngie}
101313498Sngie
102313498SngieEOF
103313498Sngie
104313498Sngie	chmod +x h-f1 h-f2
105313498Sngie
106313498Sngie	for f in 1 2
107313498Sngie	do
108313498Sngie		atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \
109313498Sngie			". ./h-f${f}; "'set -- ; delim_argv "$@"'
110313498Sngie		atf_check -s exit:0 -o inline:'>foobar<\n' -e empty \
111313498Sngie			${TEST_SH} -c \
112313498Sngie			". ./h-f${f}; "'set -- ; delim_argv "foo$@bar"'
113313498Sngie		atf_check -s exit:0 -o inline:'>foo  bar<\n' -e empty \
114313498Sngie			${TEST_SH} -c \
115313498Sngie			". ./h-f${f}; "'set -- ; delim_argv "foo $@ bar"'
116313498Sngie
117313498Sngie		atf_check -s exit:0 -o inline:'>a< >b< >c<\n' -e empty \
118313498Sngie			${TEST_SH} -c \
119313498Sngie			". ./h-f${f}; "'set -- a b c; delim_argv "$@"'
120313498Sngie		atf_check -s exit:0 -o inline:'>fooa< >b< >cbar<\n' -e empty \
121313498Sngie			${TEST_SH} -c \
122313498Sngie			". ./h-f${f}; "'set -- a b c; delim_argv "foo$@bar"'
123313498Sngie		atf_check -s exit:0 -o inline:'>foo a< >b< >c bar<\n' -e empty \
124313498Sngie			${TEST_SH} -c \
125313498Sngie			". ./h-f${f}; "'set -- a b c; delim_argv "foo $@ bar"'
126313498Sngie	done
127313498Sngie}
128313498Sngie
129272343Sngieatf_test_case strip
130272343Sngiestrip_head() {
131272343Sngie	atf_set "descr" "Checks that the %% operator works and strips" \
132272343Sngie	                "the contents of a variable from the given point" \
133272343Sngie			"to the end"
134272343Sngie}
135272343Sngiestrip_body() {
136272343Sngie	line='#define bindir "/usr/bin" /* comment */'
137272343Sngie	stripped='#define bindir "/usr/bin" '
138313498Sngie
139313498Sngie	# atf_expect_fail "PR bin/43469" -- now fixed
140313498Sngie	for exp in 				\
141313498Sngie		'${line%%/\**}'			\
142313498Sngie		'${line%%"/*"*}'		\
143313498Sngie		'${line%%'"'"'/*'"'"'*}'	\
144313498Sngie		'"${line%%/\**}"'		\
145313498Sngie		'"${line%%"/*"*}"'		\
146313498Sngie		'"${line%%'"'"'/*'"'"'*}"'	\
147313498Sngie		'${line%/\**}'			\
148313498Sngie		'${line%"/*"*}'			\
149313498Sngie		'${line%'"'"'/*'"'"'*}'		\
150313498Sngie		'"${line%/\**}"'		\
151313498Sngie		'"${line%"/*"*}"'		\
152313498Sngie		'"${line%'"'"'/*'"'"'*}"'
153313498Sngie	do
154313498Sngie		atf_check -o inline:":$stripped:\n" -e empty ${TEST_SH} -c \
155313498Sngie			"line='${line}'; echo :${exp}:"
156313498Sngie	done
157272343Sngie}
158272343Sngie
159272343Sngieatf_test_case varpattern_backslashes
160272343Sngievarpattern_backslashes_head() {
161272343Sngie	atf_set "descr" "Tests that protecting wildcards with backslashes" \
162272343Sngie	                "works in variable patterns."
163272343Sngie}
164272343Sngievarpattern_backslashes_body() {
165272343Sngie	line='/foo/bar/*/baz'
166272343Sngie	stripped='/foo/bar/'
167313498Sngie	atf_check -o inline:'/foo/bar/\n' -e empty ${TEST_SH} -c \
168313498Sngie		'line="/foo/bar/*/baz"; echo ${line%%\**}'
169272343Sngie}
170272343Sngie
171272343Sngieatf_test_case arithmetic
172272343Sngiearithmetic_head() {
173272343Sngie	atf_set "descr" "POSIX requires shell arithmetic to use signed" \
174272343Sngie	                "long or a wider type.  We use intmax_t, so at" \
175272343Sngie			"least 64 bits should be available.  Make sure" \
176272343Sngie			"this is true."
177272343Sngie}
178272343Sngiearithmetic_body() {
179313498Sngie
180313498Sngie	atf_check -o inline:'3' -e empty ${TEST_SH} -c \
181313498Sngie		'printf %s $((1 + 2))'
182313498Sngie	atf_check -o inline:'2147483647' -e empty ${TEST_SH} -c \
183313498Sngie		'printf %s $((0x7fffffff))'
184313498Sngie	atf_check -o inline:'9223372036854775807' -e empty ${TEST_SH} -c \
185313498Sngie		'printf %s $(((1 << 63) - 1))'
186272343Sngie}
187272343Sngie
188272343Sngieatf_test_case iteration_on_null_parameter
189272343Sngieiteration_on_null_parameter_head() {
190272343Sngie	atf_set "descr" "Check iteration of \$@ in for loop when set to null;" \
191272343Sngie	                "the error \"sh: @: parameter not set\" is incorrect." \
192272343Sngie	                "PR bin/48202."
193272343Sngie}
194272343Sngieiteration_on_null_parameter_body() {
195313498Sngie	atf_check -o empty -e empty ${TEST_SH} -c \
196313498Sngie		'N=; set -- ${N};   for X; do echo "[$X]"; done'
197272343Sngie}
198272343Sngie
199313498Sngieatf_test_case iteration_on_quoted_null_parameter
200313498Sngieiteration_on_quoted_null_parameter_head() {
201313498Sngie	atf_set "descr" \
202313498Sngie		'Check iteration of "$@" in for loop when set to null;'
203313498Sngie}
204313498Sngieiteration_on_quoted_null_parameter_body() {
205313498Sngie	atf_check -o inline:'[]\n' -e empty ${TEST_SH} -c \
206313498Sngie		'N=; set -- "${N}"; for X; do echo "[$X]"; done'
207313498Sngie}
208313498Sngie
209313498Sngieatf_test_case iteration_on_null_or_null_parameter
210313498Sngieiteration_on_null_or_null_parameter_head() {
211313498Sngie	atf_set "descr" \
212313498Sngie		'Check expansion of null parameter as default for another null'
213313498Sngie}
214313498Sngieiteration_on_null_or_null_parameter_body() {
215313498Sngie	atf_check -o empty -e empty ${TEST_SH} -c \
216313498Sngie		'N=; E=; set -- ${N:-${E}}; for X; do echo "[$X]"; done'
217313498Sngie}
218313498Sngie
219313498Sngieatf_test_case iteration_on_null_or_missing_parameter
220313498Sngieiteration_on_null_or_missing_parameter_head() {
221313498Sngie	atf_set "descr" \
222313498Sngie	    'Check expansion of missing parameter as default for another null'
223313498Sngie}
224313498Sngieiteration_on_null_or_missing_parameter_body() {
225313498Sngie	# atf_expect_fail 'PR bin/50834'
226313498Sngie	atf_check -o empty -e empty ${TEST_SH} -c \
227313498Sngie		'N=; set -- ${N:-}; for X; do echo "[$X]"; done'
228313498Sngie}
229313498Sngie
230313498Sngienl='
231313498Sngie'
232313498Sngiereset()
233313498Sngie{
234313498Sngie	TEST_NUM=0
235313498Sngie	TEST_FAILURES=''
236313498Sngie	TEST_FAIL_COUNT=0
237313498Sngie	TEST_ID="$1"
238313498Sngie}
239313498Sngie
240313498Sngiecheck()
241313498Sngie{
242313498Sngie	fail=false
243313498Sngie	TEMP_FILE=$( mktemp OUT.XXXXXX )
244313498Sngie	TEST_NUM=$(( $TEST_NUM + 1 ))
245313498Sngie	MSG=
246313498Sngie
247313498Sngie	# our local shell (ATF_SHELL) better do quoting correctly...
248313498Sngie	# some of the tests expect us to expand $nl internally...
249313498Sngie	CMD="$1"
250313498Sngie
251313498Sngie	result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" )"
252313498Sngie	STATUS=$?
253313498Sngie
254313498Sngie	if [ "${STATUS}" -ne "$3" ]; then
255313498Sngie		MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
256313498Sngie		MSG="${MSG} expected exit code $3, got ${STATUS}"
257313498Sngie
258313498Sngie		# don't actually fail just because of wrong exit code
259313498Sngie		# unless we either expected, or received "good"
260313498Sngie		case "$3/${STATUS}" in
261313498Sngie		(*/0|0/*) fail=true;;
262313498Sngie		esac
263313498Sngie	fi
264313498Sngie
265313498Sngie	if [ "$3" -eq 0 ]; then
266313498Sngie		if [ -s "${TEMP_FILE}" ]; then
267313498Sngie			MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
268313498Sngie			MSG="${MSG} Messages produced on stderr unexpected..."
269313498Sngie			MSG="${MSG}${nl}$( cat "${TEMP_FILE}" )"
270313498Sngie			fail=true
271313498Sngie		fi
272313498Sngie	else
273313498Sngie		if ! [ -s "${TEMP_FILE}" ]; then
274313498Sngie			MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
275313498Sngie			MSG="${MSG} Expected messages on stderr,"
276313498Sngie			MSG="${MSG} nothing produced"
277313498Sngie			fail=true
278313498Sngie		fi
279313498Sngie	fi
280313498Sngie	rm -f "${TEMP_FILE}"
281313498Sngie
282313498Sngie	# Remove newlines (use local shell for this)
283313498Sngie	oifs="$IFS"
284313498Sngie	IFS="$nl"
285313498Sngie	result="$(echo $result)"
286313498Sngie	IFS="$oifs"
287313498Sngie	if [ "$2" != "$result" ]
288313498Sngie	then
289313498Sngie		MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
290313498Sngie		MSG="${MSG} Expected output '$2', received '$result'"
291313498Sngie		fail=true
292313498Sngie	fi
293313498Sngie
294313498Sngie	if $fail
295313498Sngie	then
296313498Sngie		MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
297313498Sngie		MSG="${MSG} Full command: <<${CMD}>>"
298313498Sngie	fi
299313498Sngie
300313498Sngie	$fail && test -n "$TEST_ID" && {
301313498Sngie		TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+${nl}}"
302313498Sngie		TEST_FAILURES="${TEST_FAILURES}${TEST_ID}[$TEST_NUM]:"
303313498Sngie		TEST_FAILURES="${TEST_FAILURES} Test of '$1' failed.";
304313498Sngie		TEST_FAILURES="${TEST_FAILURES}${nl}${MSG}"
305313498Sngie		TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 ))
306313498Sngie		return 0
307313498Sngie	}
308313498Sngie	$fail && atf_fail "Test[$TEST_NUM] of '$1' failed${nl}${MSG}"
309313498Sngie	return 0
310313498Sngie}
311313498Sngie
312313498Sngieresults()
313313498Sngie{
314313498Sngie	test -z "${TEST_ID}" && return 0
315313498Sngie	test -z "${TEST_FAILURES}" && return 0
316313498Sngie
317313498Sngie	echo >&2 "=========================================="
318313498Sngie	echo >&2 "While testing '${TEST_ID}'"
319313498Sngie	echo >&2 " - - - - - - - - - - - - - - - - -"
320313498Sngie	echo >&2 "${TEST_FAILURES}"
321313498Sngie	atf_fail \
322313498Sngie "Test ${TEST_ID}: $TEST_FAIL_COUNT subtests (of $TEST_NUM) failed - see stderr"
323313498Sngie}
324313498Sngie
325313498Sngieatf_test_case shell_params
326313498Sngieshell_params_head() {
327313498Sngie	atf_set "descr" "Test correct operation of the numeric parameters"
328313498Sngie}
329313498Sngieshell_params_body() {
330313498Sngie	atf_require_prog mktemp
331313498Sngie
332313498Sngie	reset shell_params
333313498Sngie
334313498Sngie	check 'set -- a b c; echo "$#: $1 $2 $3"' '3: a b c' 0
335313498Sngie	check 'set -- a b c d e f g h i j k l m; echo "$#: ${1}0 ${10} $10"' \
336313498Sngie		'13: a0 j a0' 0
337313498Sngie	check 'x="$0"; set -- a b; y="$0";
338313498Sngie	      [ "x${x}y" = "x${y}y" ] && echo OK || echo x="$x" y="$y"' \
339313498Sngie		'OK' 0
340313498Sngie	check "${TEST_SH} -c 'echo 0=\$0 1=\$1 2=\$2' a b c" '0=a 1=b 2=c' 0
341313498Sngie
342313498Sngie	echo 'echo 0="$0" 1="$1" 2="$2"' > helper.sh
343313498Sngie	check "${TEST_SH} helper.sh a b c" '0=helper.sh 1=a 2=b' 0
344313498Sngie
345313498Sngie	check 'set -- a bb ccc dddd eeeee ffffff ggggggg hhhhhhhh \
346313498Sngie		iiiiiiiii jjjjjjjjjj kkkkkkkkkkk
347313498Sngie	       echo "${#}: ${#1} ${#2} ${#3} ${#4} ... ${#9} ${#10} ${#11}"' \
348313498Sngie		 '11: 1 2 3 4 ... 9 10 11' 0
349313498Sngie
350313498Sngie	check 'set -- a b c; echo "$#: ${1-A} ${2-B} ${3-C} ${4-D} ${5-E}"' \
351313498Sngie		'3: a b c D E' 0
352313498Sngie	check 'set -- a "" c "" e
353313498Sngie	       echo "$#: ${1:-A} ${2:-B} ${3:-C} ${4:-D} ${5:-E}"' \
354313498Sngie		'5: a B c D e' 0
355313498Sngie	check 'set -- a "" c "" e
356313498Sngie	       echo "$#: ${1:+A} ${2:+B} ${3:+C} ${4:+D} ${5:+E}"' \
357313498Sngie		'5: A  C  E' 0
358313498Sngie	check 'set -- "abab*cbb"
359313498Sngie	       echo "${1} ${1#a} ${1%b} ${1##ab} ${1%%b} ${1#*\*} ${1%\**}"' \
360313498Sngie	       'abab*cbb bab*cbb abab*cb ab*cbb abab*cb cbb abab' 0
361313498Sngie	check 'set -- "abab?cbb"
362313498Sngie    echo "${1}:${1#*a}+${1%b*}-${1##*a}_${1%%b*}%${1#[ab]}=${1%?*}/${1%\?*}"' \
363313498Sngie	       'abab?cbb:bab?cbb+abab?cb-b?cbb_a%bab?cbb=abab?cb/abab' 0
364313498Sngie	check 'set -- a "" c "" e; echo "${2:=b}"' '' 1
365313498Sngie
366313498Sngie	results
367313498Sngie}
368313498Sngie
369272343Sngieatf_init_test_cases() {
370272343Sngie	atf_add_test_case dollar_at
371272343Sngie	atf_add_test_case dollar_at_with_text
372272343Sngie	atf_add_test_case strip
373272343Sngie	atf_add_test_case varpattern_backslashes
374272343Sngie	atf_add_test_case arithmetic
375272343Sngie	atf_add_test_case iteration_on_null_parameter
376313498Sngie	atf_add_test_case iteration_on_quoted_null_parameter
377313498Sngie	atf_add_test_case iteration_on_null_or_null_parameter
378313498Sngie	atf_add_test_case iteration_on_null_or_missing_parameter
379313498Sngie	atf_add_test_case shell_params
380272343Sngie}
381