xref: /netbsd-src/tests/bin/sh/t_shift.sh (revision 2f76de8b9e6d8b5620c5a246613458e2e4bca96f)
1# $NetBSD: t_shift.sh,v 1.3 2024/09/21 20:48:50 kre Exp $
2#
3# Copyright (c) 2016 The NetBSD Foundation, Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26#
27# the implementation of "sh" to test
28: ${TEST_SH:="/bin/sh"}
29
30atf_test_case basic_shift_test
31basic_shift_test_head() {
32	atf_set "descr" "Test correct operation of valid shifts"
33}
34basic_shift_test_body() {
35
36	for a in			\
37	  "one-arg::0:one-arg"		\
38	  "one-arg:--:0:one-arg"	\
39	  "one-arg:1:0:one-arg"		\
40	  "one-arg:-- 1:0:one-arg"	\
41	  "one-arg:-- 0:1 one-arg"	\
42	  "a b c::2 b c:a"		\
43	  "a b c:1:2 b c:a"		\
44	  "a b c:2:1 c:a:b"		\
45	  "a b c:3:0:a:b:c"		\
46	  "a b c:0:3 a b c"		\
47	  "a b c d e f g h i j k l m n o p:1:15 b c d e f g h i j k l m n o p"\
48	  "a b c d e f g h i j k l m n o p:9:7 j k l m n o p:a:b:c:g:h:i"     \
49	  "a b c d e f g h i j k l m n o p:13:3 n o p:a:b:c:d:k:l:m"	      \
50	  "a b c d e f g h i j k l m n o p:16:0:a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p"
51	do
52		oIFS="${IFS}"
53		IFS=:; set -- $a
54		IFS="${oIFS}"
55
56		init="$1"; n="$2"; res="$3"; shift 3
57
58		not=
59		for b
60		do
61			not="${not} -o not-match:$b"
62		done
63
64		atf_check -s exit:0 -o "match:${res}" ${not} -e empty \
65			${TEST_SH} -c "set -- ${init}; shift $n;"' echo "$# $*"'
66	done
67
68	atf_check -s exit:0 -o match:complete -o not-match:ERR -e empty \
69		${TEST_SH} -c \
70    'set -- a b c d e;while [ $# -gt 0 ];do shift||echo ERR;done;echo complete'
71}
72
73atf_test_case basic_rotate
74basic_rotate_head() {
75	atf_set "descr" "Test correct operation of valid rotates"
76}
77basic_rotate_body() {
78
79	for a in			\
80	  "one-arg:-0:1 one-arg"	\
81	  "one-arg:-1:1 one-arg"	\
82	  "one-arg:-- -1:1 one-arg"	\
83	  "a b c:-1:3 c a b"		\
84	  "a b c:-2:3 b c a"		\
85	  "a b c:-3:3 a b c"		\
86	  "a b c:-- -3:3 a b c"		\
87	  "a b c:-0:3 a b c"		\
88	  "a b c d e f g h i j k l m n o:-1:15 o a b c d e f g h i j k l m n" \
89	  "a b c d e f g h i j k l m n o:-2:15 n o a b c d e f g h i j k l m" \
90	  "a b c d e f g h i j k l m n o:-9:15 g h i j k l m n o a b c d e f" \
91	  "a b c d e f g h i j k l m n o:-13:15 c d e f g h i j k l m n o a b" \
92	  "a b c d e f g h i j k l m n o:-14:15 b c d e f g h i j k l m n o a" \
93	  "a b c d e f g h i j k l m n o:-15:15 a b c d e f g h i j k l m n o"
94	do
95		oIFS="${IFS}"
96		IFS=:; set -- $a
97		IFS="${oIFS}"
98
99		init="$1"; n="$2"; res="$3"; shift 3
100
101		atf_check -s exit:0 -o "match:${res}" -e empty \
102			${TEST_SH} -c "set -- ${init}; shift $n;"' echo "$# $*"'
103	done
104}
105
106atf_test_case excessive_shift
107excessive_shift_head() {
108	atf_set "descr" "Test acceptable operation of shift too many"
109}
110# In:
111#
112#	http://pubs.opengroup.org/onlinepubs/9699919799
113#		/utilities/V3_chap02.html#tag_18_26_01
114#
115# (that URL should be one line, with the /util... immediately after ...9799)
116#
117# POSIX says of shift (in the "EXIT STATUS" paragraph):
118#
119#  If the n operand is invalid or is greater than "$#", this may be considered
120#  a syntax error and a non-interactive shell may exit; if the shell does not
121#  exit in this case, a non-zero exit status shall be returned.
122#  Otherwise, zero shall be returned.
123#
124# NetBSD's sh treats it as an error and exits (if non-interactive, as here),
125# other shells do not.
126#
127# Either behaviour is acceptable - so the test allows for both
128# (and checks that if the shell does not exit, "shift" returns status != 0)
129
130excessive_shift_body() {
131	for a in				\
132		"one-arg:2"			\
133		"one-arg:4"			\
134		"one-arg:13"			\
135		"one two:3"			\
136		"one two:7"			\
137		"one two three four five:6"	\
138		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:16"	\
139		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:17"	\
140		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:30"	\
141		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:9999"
142	do
143		oIFS="${IFS}"
144		IFS=:; set -- $a
145		IFS="${oIFS}"
146
147		atf_check -s not-exit:0 -o match:OK -o not-match:ERR \
148			-e ignore ${TEST_SH} -c \
149			"set -- $1 ;"'echo OK:$#-'"$2;shift $2 && echo ERR"
150	done
151}
152
153atf_test_case excessive_rotate
154excessive_rotate_head() {
155	atf_set "descr" "Test acceptable operation of rotate too many"
156}
157
158excessive_rotate_body() {
159	for a in				\
160		"one-arg:2"			\
161		"one-arg:4"			\
162		"one-arg:13"			\
163		"one two:3"			\
164		"one two:7"			\
165		"one two three four five:6"	\
166		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:16"	\
167		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:17"	\
168		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:30"	\
169		"I II III IV V VI VII VIII IX X XI XII XIII XIV XV:9999"
170	do
171		oIFS="${IFS}"
172		IFS=:; set -- $a
173		IFS="${oIFS}"
174
175		atf_check -s not-exit:0 -o match:OK -o not-match:ERR \
176			-e ignore ${TEST_SH} -c \
177			"set -- $1 ;echo OK:$#:-$2; shift -$2 && echo ERR"
178	done
179}
180
181atf_test_case function_shift
182function_shift_head() {
183	atf_set "descr" "Test that shift in a function does not affect outside"
184}
185function_shift_body() {
186	: # later...
187}
188
189atf_test_case non_numeric_shift
190non_numeric_shift_head() {
191	atf_set "descr" "Test that non-numeric args to shift are detected"
192}
193
194# from the DESCRIPTION section at the URL mentioned with the excessive_shift
195# test:
196#
197#	The value n shall be an unsigned decimal integer ...
198#
199# That is not hex (octal will be treated as if it were decimal, a leading 0
200# will simply be ignored - we test for this by giving an "octal" value that
201# would be OK if parsed as octal, but not if parsed (correctly) as decimal)
202#
203# Obviously total trash like roman numerals or alphabetic strings are out.
204#
205# Also no signed values (no + or -) and not a string that looks kind of like
206# a number,  but only if you're generous
207#
208# But as the EXIT STATUS section quoted above says, with an invalid 'n'
209# the shell has the option of exiting, or returning status != 0, so
210# again this test allows both.
211
212non_numeric_shift_body() {
213
214	# there are 9 args set, 010 is 8 if parsed octal, 10 decimal
215	for a in a I 0x12 010 5V ' ' '' +1 ' 1'
216	do
217		atf_check -s not-exit:0 -o empty -e ignore ${TEST_SH} -c \
218			"set -- a b c d e f g h i; shift '$a' && echo ERROR"
219		atf_check -s not-exit:0 -o empty -e ignore ${TEST_SH} -c \
220			"set -- a b c d e f g h i; shift -- '$a' && echo ERROR"
221	done
222}
223
224atf_test_case too_many_args
225too_many_args_head() {
226	# See PR bin/50896
227	atf_set "descr" "Test that sh detects invalid extraneous args to shift"
228}
229# This is a syntax error, a non-interactive shell (us) must exit $? != 0
230too_many_args_body() {
231	# This tests the bug in PR bin/50896 is fixed
232
233	for a in "1 1" "1 0" "1 2 3" "1 foo" "1 --" "-- 1 2" "-1 2"
234	do
235		atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
236			" set -- a b c d; shift ${a} ; echo FAILED "
237	done
238}
239
240atf_init_test_cases() {
241	atf_add_test_case basic_shift_test
242	atf_add_test_case excessive_shift
243	atf_add_test_case function_shift
244	atf_add_test_case non_numeric_shift
245	atf_add_test_case too_many_args
246	atf_add_test_case basic_rotate
247	atf_add_test_case excessive_rotate
248}
249