t_syntax.sh revision 1.5 1 # $NetBSD: t_syntax.sh,v 1.5 2017/06/24 11:09:42 kre Exp $
2 #
3 # Copyright (c) 2017 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 : ${TEST_SH:=/bin/sh}
28
29 # This set of tests verifies various requirementgs relating to correct
30 # (and incorrect) syntax of shell input
31 #
32 # There is no intent in these tests to verify correct operation
33 # (though that sometimes cannot be separated from correct parsing.)
34 # That is (or should be) verified elsewhere.
35 #
36 # Also, some very basic syntax is tested in almost every test
37 # (they cannot work without basic parsing of elementary commands)
38 # so that is also not explicitly tested here.
39 #
40 # Similarly word expansion, field splitting, redirection, all have
41 # tests of their own (though we do test parsing of redirect ops).
42 #
43 # Note that in order to test the basic facilities, other shell operations
44 # are used - a test failure here does not necessarily mean that the
45 # operation intended to be tested is faulty, just that something is.
46
47 atf_test_case a_basic_tokenisation
48 a_basic_tokenisation_head() {
49 atf_set "descr" "Test the shell correctly finds various tokens"
50 }
51 a_basic_tokenisation_body() {
52 atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
53 'set -- a b c; echo $#'
54 atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \
55 'set -- a""b c; echo $#'
56 atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
57 'set -- a"" b c; echo $#'
58 atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
59 'set -- ""a b c\;; echo $#'
60
61 atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
62 'set -- set -- c; echo $#'
63 atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
64 'set --;set -- c; echo $#'
65 atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
66 'set --&set -- c; echo $#'
67 atf_check -s exit:0 -o 'inline:1\n' -e empty ${TEST_SH} -c \
68 'set -- a b&&set -- c; echo $#'
69 atf_check -s exit:0 -o 'inline:2\n' -e empty ${TEST_SH} -c \
70 'set -- a b||set -- c; echo $#'
71 }
72
73 atf_test_case b_comments
74 b_comments_head() {
75 atf_set "descr" "Test the shell correctly handles comments"
76 }
77 b_comments_body() {
78
79 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '#'
80 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '# exit 1'
81 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'true # exit 1'
82 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c 'false # exit 0'
83
84 atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
85 'echo foo # bar'
86 atf_check -s exit:0 -o 'inline:foo # bar\n' -e empty ${TEST_SH} -c \
87 'echo foo \# bar'
88 atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
89 'echo foo; # echo bar'
90 atf_check -s exit:0 -o 'inline:foo#bar\n' -e empty ${TEST_SH} -c \
91 'echo foo#bar'
92 atf_check -s exit:0 -o 'inline:foo# bar\n' -e empty ${TEST_SH} -c \
93 'echo foo# bar'
94 atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
95 'x=foo; echo ${x#bar}'
96
97 atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
98 'echo "#"'
99 atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
100 "echo '#'"
101 atf_check -s exit:0 -o 'inline:#\n' -e empty ${TEST_SH} -c \
102 'echo \#'
103 atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
104 'echo "#"#'
105 atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
106 "echo '#'#"
107 atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
108 'echo \##'
109 atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
110 'echo "#"# #"#"'
111 atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
112 "echo '#'# #'#'"
113 atf_check -s exit:0 -o 'inline:##\n' -e empty ${TEST_SH} -c \
114 'echo \## #\#'
115
116 cat <<-'DONE'|atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH}
117 # test comments do not provoke synax errors !\
118 echo foo # ( { " hello
119 while : # that's forever
120 do # the following command list
121 # starting with nothing ${unset?error}
122 break # done loop terminate $( echo bar; exit 1 )
123 done #####################################################
124 # "hello
125 exit 0
126 DONE
127 }
128
129 atf_test_case c_line_wrapping
130 c_line_wrapping_head() {
131 atf_set "descr" "check aspects of command line wrapping"
132 }
133 c_line_wrapping_body() {
134 atf_require_prog ls
135 atf_require_prog printf
136
137 cat <<- 'DONE' | atf_check -s exit:0 -o ignore -e empty ${TEST_SH} -e
138 l\
139 s
140 DONE
141
142 cat <<- 'DONE' | atf_check -s exit:7 -o empty -e empty ${TEST_SH}
143 e\
144 x\
145 it \
146 7
147 DONE
148
149 # Have to do this twice as cannot say "any exit code but 0 or 7" ...
150 cat <<- 'DONE' | atf_check -s not-exit:0 -o empty -e not-empty \
151 ${TEST_SH}
152 e\
153 x\
154 it\
155 7
156 DONE
157 cat <<- 'DONE' | atf_check -s not-exit:7 -o empty -e not-empty \
158 ${TEST_SH}
159 e\
160 x\
161 it\
162 7
163 DONE
164
165 cat <<- 'DONE' | atf_check -s exit:0 -o empty -e empty ${TEST_SH}
166 wh\
167 il\
168 e \
169 f\a\
170 \l\s\e
171 do
172 :\
173 ;
174 done
175 DONE
176
177 cat <<- 'DONE' | atf_check -s exit:0 -o inline:'hellohellohellohello' \
178 -e empty ${TEST_SH}
179 V\
180 AR=hel\
181 lo
182 unset U V1
183 pri\
184 ntf '%s' ${\
185 VAR\
186 }
187 p\
188 r\
189 i\
190 n\
191 t\
192 f\
193 \
194 '%s' \
195 $\
196 {\
197 V\
198 A\
199 R}
200 printf '%s' ${U\
201 -\
202 "$\
203 {V\
204 1:\
205 =$\
206 {V\
207 AR+\
208 ${V\
209 AR}\
210 }\
211 }"}
212 printf '%s' ${V\
213 1?V1\
214 \
215 FAIL}
216 DONE
217
218 cat <<- 'DONE' | atf_check -s exit:0 -o inline:'2\n' ${TEST_SH}
219 l\
220 s=7 bi\
221 n\
222 =\
223 3
224 echo $(\
225 ( ls /bin )\
226 )
227 DONE
228
229 # Inspired by src/etc/MAKEDEV.tmpl failure with (broken)
230 # sh LINENO code... avoid it happening again...
231 for VARS in 1:0:0:0 0:1:0:0 0:0:1:0 0:0:0:1 \
232 1:0:0:1 1:0:1:0 1:1:0:0 0:1:1:0 \
233 0:0:0:0 1:1:0:1 0:1:1:1 1:1:1:1
234 do
235 eval $(
236 IFS=:
237 set -- $VARS
238 test $(( $1 + $2 + $3 + $4 )) -eq 1 &&
239 R=OK || R=BAD
240 printf "R=%s;" $R
241 for v in a b c d
242 do
243 case $1 in
244 (0) printf "export %s=false;" $v;;
245 (1) printf "export %s=true;" $v;;
246 esac
247 shift
248 done
249 )
250
251 cat <<- 'DONE' | atf_check -s exit:0 -o inline:"${R}" ${TEST_SH}
252 case $(( $($a && echo 1 || echo 0) + \
253 $($b && echo 1 || echo 0) + \
254 $($c && echo 1 || echo 0) + \
255 $($d && echo 1 || echo 0) ))
256 in
257 (1) printf OK ;;
258 (*) printf BAD ;;
259 esac
260 DONE
261 done
262
263 # inspired by pkgsrc/pkgtools/cwrappers :: libnbcompat/configure
264 # failure with (broken) sh LINENO core .. avoid recurrence
265 # This test would have failed.
266 cat <<- 'DONE' | atf_check -s exit:0 -o inline:'/tmp\n' ${TEST_SH}
267 dn=/tmp/foo
268
269 D=`dirname -- "${dn}" ||
270 expr X"${dn}" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
271 X"${dn}" : 'X\(//\)[^/]' \| \
272 X"${dn}" : 'X\(//\)$' \| \
273 X"${dn}" : 'X\(/\)' \| . 2>/dev/null ||
274 echo X"${dn}" |
275 sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
276 s//\1/
277 q
278 }
279 /^X\(\/\/\)[^/].*/{
280 s//\1/
281 q
282 }
283 /^X\(\/\/\)$/{
284 s//\1/
285 q
286 }
287 /^X\(\/\).*/{
288 s//\1/
289 q
290 }
291 s/.*/./; q'`
292
293 echo "${D}"
294 DONE
295 return 0
296 }
297
298 atf_test_case d_redirects
299 d_redirects_head() {
300 atf_set "descr" "Check parsing of redirect operators"
301 }
302 d_redirects_body() {
303
304 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
305 '>/dev/null'
306 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
307 '</dev/null'
308 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
309 '>>/dev/null'
310 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
311 '<>/dev/null'
312 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
313 '</dev/null>/dev/null'
314 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
315 '>|/dev/null'
316 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
317 '>/dev/null>/dev/null>/dev/null'
318
319 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
320 'echo hello >/dev/null'
321 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
322 'echo >/dev/null hello'
323 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
324 '>/dev/null echo hello'
325 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
326 'echo hello >/dev/null world'
327 atf_check -s exit:0 -o 'inline:hello world\n' -e empty ${TEST_SH} -c \
328 'echo hello </dev/null world'
329
330 atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
331 'echo hello </dev/null'
332 atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
333 'echo hello 3</dev/null'
334 atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
335 'echo hello 3 </dev/null'
336 atf_check -s exit:0 -o 'inline:hello 3\n' -e empty ${TEST_SH} -c \
337 'echo hello \3</dev/null'
338 atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
339 'echo hello</dev/null'
340 atf_check -s exit:0 -o 'inline:3\n' -e empty ${TEST_SH} -c \
341 'hello=3; echo ${hello}</dev/null'
342
343 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
344 '2>&1'
345 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
346 '2>& 1'
347 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
348 'FD=1; 2>&"${FD}"'
349 atf_check -s exit:0 -o 'inline:hello\n' -e empty ${TEST_SH} -c \
350 'FD=1; echo hello 2>&"${FD}" >&2'
351
352 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
353 '2>&- 3<&- 4>&-'
354
355 return 0
356 }
357
358 atf_test_case f_variable_syntax
359 f_variable_syntax_head() {
360 atf_set "descr" "Check that var names of all legal forms work"
361 }
362 f_variable_syntax_body() {
363 # don't test _ as a variable, it can be "unusual"
364 for vname in a ab _a _9 a123 a_1_2_3 __ ___ ____ __1__ _0 \
365 A AA AAA AaBb _A_a A_a_ a1_ abc_123 ab_12_cd_ef_34_99 \
366 abcdefghijklmnopqrstuvwzyz ABCDEFGHIJKLMNOPQRSTUVWXYZ_ \
367 A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \
368 Then_Make_it_Even_Longer_by_Multiplying_it___A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all__A_VERY_LONG_VARIABLE_NAME_that_is_probably_longer_than_most_used_in_the_average_shell_script_already_about_100_chars_in_this_one_but_there_is_not_supposed_to_be_any_limit_on_the_length_at_all \
369 xyzzy __0123454321__ _0_1_2_3_4_5_6_7_8_9_ ABBA X_ Y__ Z___ \
370 _____________________________________________________________
371 do
372 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
373 "unset ${vname}"
374 atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
375 "unset ${vname}; printf %s \${${vname}-OK}"
376 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
377 "${vname}=GOOD; printf %s \${${vname}-OK}"
378 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
379 "${vname}=GOOD; printf %s \$${vname}"
380 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
381 "unset ${vname};${vname}=GOOD;printf %s \${${vname}-OK}"
382 atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -c \
383 "${vname}=GOOD;unset ${vname};printf %s \${${vname}-OK}"
384 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
385 "${vname}=GOOD; unset ${vname}x; printf %s \$${vname}"
386 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
387 "unset ${vname}x; ${vname}=GOOD; printf %s \$${vname}x"
388 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
389 "${vname}=GOOD; ${vname}_=BAD; printf %s \$${vname}"
390
391 case "${vname}" in
392 ?) continue;;
393 esac
394
395 # The following tests do not work for 1 char var names.
396 # hence the check and "continue" above to skip the remaining
397 # tests for that case
398
399 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
400 "${vname}=GOOD; unset ${vname%?}; printf %s \$${vname}"
401
402 # (this next would work, but becomes just a duplicate of
403 # an earlier test, so is pointless for 1 ch names)
404 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
405 "${vname}=GOOD; unset ${vname}x ${vname%?}; printf %s \$${vname}"
406
407 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
408 "unset ${vname%?};${vname}=GOOD; printf %s \$${vname%?}"
409
410 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
411 "${vname}=GOOD; ${vname%?}=BAD; printf %s \$${vname}"
412
413 # all the remaining tests require the 2nd char of the
414 # variable name to be a legal first character. That
415 # is, not a digit, so skip the rest if we have a digit
416 # second...
417 case "${vname}" in
418 ?[0-9]*) continue;;
419 esac
420
421 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
422 "${vname}=GOOD; unset ${vname#?}; printf %s \$${vname}"
423 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
424 "unset ${vname#?};${vname}=GOOD; printf %s \$${vname#?}"
425
426 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
427 "${vname}=GOOD; ${vname#?}=BAD; printf %s \$${vname}"
428
429 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
430 "unset ${vname%?} ${vname#?} ${vname}x; ${vname}=GOOD;
431 printf %s \$${vname%?}\$${vname#?}\$${vname}x"
432
433 atf_check -s exit:0 -o match:GOOD -e empty ${TEST_SH} -c \
434 "${vname}=GOOD; ${vname%?}=BAD; ${vname}_=BAD;
435 ${vname#?}=BAD; printf %s \$${vname}"
436 done
437
438 # don't test '.' in var names, some shells permit that (in ${} anyway)
439 # this test also cannot check for embedded - + ? = : % or #
440 for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' '?!' ';'
441 do
442 atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
443 "echo \${${vname}}"
444 done
445
446 for vname in ,x -p +h :def 0_1 'x*x' '()' '"' "'" 'a b c' x,y,z '?!' \
447 ';' a-b a+b 'a?b' 'a:b' 'a%b' 'a#b' 0 1 99 @ '*' '!' '?'
448 do
449 # failure modes will differ, but they should all fail somehow
450 atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
451 "${vname}="
452 done
453
454 }
455
456 atf_test_case g_var_assign
457 g_var_assign_head() {
458 atf_set "descr" "Check var assignments "
459 }
460 g_var_assign_body() {
461 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
462 'a=b'
463 atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
464 '\a=b'
465 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
466 'a=b c=d'
467 atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
468 'a=b c=d echo e=f'
469 atf_check -s exit:0 -e empty -o 'inline:e=f\n' ${TEST_SH} -c \
470 'a=b 2>/dev/null c=d </dev/null echo e=f'
471
472 # We need to make sure that we do not accidentally
473 # find a command called 'a=b' ...
474
475 for d in /foo /foo/bar /not-dir /no/such/directory '/!!!' \
476 '/-/--/#' '/"/""/"""' -
477 do
478 test -d "${d}" || break
479 done
480 test "${#d}" -gt 1 || atf-skip 'Wacky directories seem to exist!'
481
482 atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
483 "PATH='${d}';"'a=\b'
484 atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
485 "PATH='${d}';"'a\=b'
486 atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
487 "PATH='${d}';"'\a=b'
488 atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
489 "PATH='${d}';"'X=; c=d ${X} a=b'
490 }
491
492 atf_test_case i_pipelines
493 i_pipelines_head() {
494 atf_set "descr" "Check pipelines"
495 }
496 i_pipelines_body() {
497
498 cmd='printf "%s\n" foo'
499 for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
500 do
501 atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
502 "${cmd}"
503 cmd="${cmd} | cat"
504 done
505
506 cmd='printf "%s\n" foo'
507 for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
508 do
509 atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
510 "${cmd}"
511 cmd="${cmd} |
512 cat"
513 done
514
515 cmd='printf "%s\n" foo'
516 for n in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
517 do
518 atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
519 "${cmd}"
520 cmd="${cmd} |
521
522
523
524
525 cat"
526 done
527
528 cmd='! printf "%s\n" foo'
529 for n in 1 2 3 4 5 6 7 8 9 10
530 do
531 atf_check -s exit:1 -o inline:'foo\n' -e empty ${TEST_SH} -c \
532 "${cmd}"
533 cmd="${cmd} | cat"
534 done
535
536 cmd='exec 4>&2 3<&0; printf "%s\n" foo'
537 for n in 1 2 3
538 do
539 pfx=
540 for xtra in 'x=y' 'a=b' '6>&1' '5<&3'
541 do
542 atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
543 "${cmd} | ${xtra} cat"
544
545 atf_check -s exit:0 -o inline:'foo\n' -e empty ${TEST_SH} -c \
546 "${cmd} | ${pfx} cat"
547
548 pfx="${pfx} ${xtra}"
549 done
550 cmd="${cmd} | ${pfx} cat"
551 done
552
553 # pipelines are not required to contain commands ...
554 # they don't do anything useful (at all) but the syntax is legal
555 base='4>&2'; cmd="${base}"
556 for pipe in 'a=b' '3<&0' '>>/dev/null' 'a= b= c=' '${x}' 'cat'
557 do
558 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
559 "${base} | ${pipe}"
560
561 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
562 "${cmd} | ${pipe}"
563
564 cmd="${cmd} | ${pipe}"
565 done
566
567 # but the command cannot be empty, or a reserved word used improperly
568 base='printf "%s\n" foo'; cmd="${base}"
569 for pipe in '' do done then else fi esac
570 do
571 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
572 "${base} | ${pipe}"
573
574 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
575 "${pipe} | ${base}"
576
577 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
578 "${cmd} | ${pipe}"
579
580 cmd="${cmd} | ${pipe}"
581 done
582 }
583
584 atf_test_case j_and_or_lists
585 j_and_or_lists_head() {
586 atf_set "descr" "Check && and || command lists"
587 }
588 j_and_or_lists_body() {
589 and=true
590 or=false
591 and_or=false
592 for i in 1 2 3 4 5 6 7 8 9 10
593 do
594 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
595 "${and}"
596
597 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
598 "${or}"
599
600 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
601 "${and_or}"
602
603 and="${and} && true"
604 or="${or} || false"
605 and_or="${and_or} || true && false"
606 done
607
608 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
609 'true &&'
610 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
611 '&& true'
612 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
613 '|| true'
614 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
615 'true ||'
616 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
617 'true || && false'
618 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
619 'false || && true'
620
621 cmd='printf "%s" foo | cat | cat>/dev/null'
622 line="${cmd}"
623 for i in 1 2 3 4
624 do
625 line="${line} && ! ${cmd} || ${cmd}"
626
627 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
628 "${line}"
629 done
630
631 }
632
633 atf_test_case k_lists
634 k_lists_head() {
635 atf_set "descr" "Check ; command lists"
636 }
637 k_lists_body() {
638 line=
639 for N in 1 2 3 4
640 do
641 for cmd in \
642 true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
643 do
644 line="${line}${line:+;}${cmd}"
645 atf_check -s exit:0 -o 'inline:hello\nworld\n' \
646 -e empty ${TEST_SH} -c \
647 "echo hello; ${line}; echo world"
648 atf_check -s exit:0 -o 'inline:hello\nworld\n' \
649 -e empty ${TEST_SH} -c \
650 "echo hello; ${line}; echo world;"
651 done
652 done
653
654 for cmd in ';' ';;' 'false ;; true' 'false; ;true' '; true'
655 do
656 atf_check -s not-exit:0 -o ignore -e not-empty \
657 ${TEST_SH} -c "${cmd}"
658 done
659 }
660
661 atf_test_case l_async_lists
662 l_async_lists_head() {
663 atf_set "descr" "Check & command lists"
664 }
665 l_async_lists_body() {
666 line=
667 for N in 1 2 3 4
668 do
669 for cmd in \
670 true false : 'cat</dev/null>/dev/null' x=3 'exec 4>&-'
671 do
672 line="${line:+${line}&}${cmd}"
673 atf_check -s exit:0 -o 'inline:hello\nworld\n' \
674 -e empty ${TEST_SH} -c \
675 "echo hello; ${line}& echo world"
676 atf_check -s exit:0 -o 'inline:hello\nworld\n' \
677 -e empty ${TEST_SH} -c \
678 "echo hello; ${line}& echo world"
679 done
680 done
681
682 for cmd in '&' ';&' '&;' '& true' 'false& &true'
683 do
684 atf_check -s not-exit:0 -o ignore -e not-empty \
685 ${TEST_SH} -c "${cmd}"
686 done
687 }
688
689 atf_test_case m_compound_lists
690 m_compound_lists_head() {
691 atf_set "descr" "Check subshells () and { ;} command grouping"
692 }
693 m_compound_lists_body() {
694 # Note: (( is an unspecified (reserved) operator, don't use it...
695 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
696 '( true )'
697 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
698 '( false )'
699 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
700 '( (:) )'
701 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
702 '( ( true ))'
703 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
704 '( ( ( ( ( true )))))'
705 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
706 '( ( ( ( (true);:));true))'
707
708 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
709 '()'
710 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
711 '( )'
712
713 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
714 '{ true; }'
715 atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
716 '{ false; }'
717 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
718 '{ { :; };}'
719 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
720 '{ { { { { :;};};};};}'
721
722 atf_check -s exit:0 -o 'inline:}\n' -e empty ${TEST_SH} -c \
723 '{ echo } ; }'
724 atf_check -s exit:0 -o 'inline:{\n' -e empty ${TEST_SH} -c \
725 '{ echo { ; }'
726 }
727
728 atf_test_case q_for_loop
729 q_for_loop_head() {
730 atf_set "descr" "Check for loop parsing"
731 }
732 q_for_loop_body() {
733 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
734 'for x; do : ; done'
735 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
736 'for x in ; do : ; done'
737 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
738 'for x in a b c ; do : ; done'
739
740 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
741 'for x in in;do : ; done'
742 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
743 'for x in for;do : ; done'
744 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
745 'for x in do;do : ; done'
746 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
747 'for for in in;do :;done'
748 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
749 'for for in for;do :; done'
750 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
751 'for for in do;do : ;done'
752 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
753 'for in in in;do : ; done'
754 atf_check -s exit:0 -o 'inline:do\nin\ndo\n' -e empty ${TEST_SH} -c \
755 'for in in in do in;do case $in in in)echo do;;do)echo in;;esac; done'
756 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
757 'for in in for;do : ; done'
758 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
759 'for in in do;do : ; done'
760 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
761 'for do in in;do : ; done'
762 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
763 'for do in for;do : ; done'
764 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
765 'for do in do;do : ; done'
766 atf_check -s exit:0 -o 'inline:dodo\n' -e empty ${TEST_SH} -c \
767 'for do in do;do echo ${do}do ; done'
768 }
769
770 atf_test_case r_case
771 r_case_head() {
772 atf_set "descr" "Check case statement parsing"
773 }
774 r_case_body() {
775 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
776 'case x in esac'
777 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
778 'case x in x) esac'
779 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
780 'case x in (x) esac'
781 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
782 'case x in x) ;; esac'
783 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
784 'case x in (x) ;; esac'
785 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
786 'case x in x|y) ;; esac'
787 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
788 'case x in (x|y) ;; esac'
789
790 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
791 'case x in x|esac) ;; esac'
792 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
793 'case x in x|esac|y) ;; esac'
794 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
795 'case x in (x|esac) ;; esac'
796 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
797 'case x in (x|esac|y) ;; esac'
798
799 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
800 'case x in in) esac'
801 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
802 'case x in in) ;; esac'
803 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
804 'case x in x|in) ;; esac'
805 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
806 'case x in x|in|y) ;; esac'
807 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
808 'case x in (x|in) ;; esac'
809 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
810 'case x in (in|x) ;; esac'
811 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
812 'case x in (x|in|y) ;; esac'
813
814 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
815 'case case in case) esac'
816 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
817 'case in in in) esac'
818 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
819 'case esac in (in) esac'
820 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
821 'case in in esac|cat'
822 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
823 'case esac in esac|cat'
824 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
825 'case in in esac|case x in u) echo foo;; esac'
826 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
827 'case esac in esac|case x in u) echo foo;; esac'
828 atf_check -s exit:0 -o 'inline:foo\n' -e empty ${TEST_SH} -c \
829 'case in in esac|case x in x) echo foo;; esac'
830
831 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
832 'case in in (esac|cat'
833
834 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
835 'case x in x );;esac'
836 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
837 'case x in ( x );;esac'
838 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
839 'case x in x | y );;esac'
840 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
841 'case x in ( x | y );;esac'
842
843 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
844 'case x
845 in
846 ( x | y )
847
848 ;;
849
850
851 esac
852 '
853 }
854
855 atf_test_case s_if
856 s_if_head() {
857 atf_set "descr" "Check if statement parsing"
858 }
859 s_if_body() {
860 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
861 'if :; then :; fi'
862 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
863 'if :; then :; else :; fi'
864 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
865 'if :; then :; elif :; then :; else :; fi'
866 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
867 'if :; then :; elif :; then :; elif :; then :; else :; fi'
868
869 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
870 'if :; then : else :; fi'
871 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
872 'if : then :; then :; fi'
873
874 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
875 'if if :;then :;fi then :;fi'
876 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
877 'if if :;then if :;then :;fi fi;then :;fi'
878 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
879 'if if :;then :;fi;then :;else if :;then :;fi fi'
880
881 for a in true false; do
882 for b in true false; do
883 for c in true false; do
884
885 $a && out=a || {
886 $b && out=b || {
887 $c && out=c || {
888 out=d; };};}
889
890 atf_check -s exit:0 -e empty \
891 -o "inline:${out}"'\n' \
892 ${TEST_SH} -c \
893 "if $a; then echo a
894 elif $b; then echo b
895 elif $c; then echo c
896 else echo d
897 fi"
898 done
899 done
900 done
901 }
902
903 atf_test_case t_loops
904 t_loops_head() {
905 atf_set "descr" "Check while/until loop parsing"
906 }
907 t_loops_body() {
908 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
909 'while false; do :; done'
910 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
911 'while false; do \done; done'
912 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
913 'until :; do :; done'
914 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
915 'until :; do \done; done'
916
917 atf_check -s exit:0 -o 'inline:x\n1\n' -e empty ${TEST_SH} -c \
918 ':; while (exit) do echo x; false; done; echo $?'
919 atf_check -s exit:0 -o 'inline:x\n0\n' -e empty ${TEST_SH} -c \
920 'false; until (exit) do echo x; done; echo $?'
921 }
922
923 atf_test_case u_case_cont
924 u_case_cont_head() {
925 atf_set "descr" "Check case stmt parsing using ;& [optional]"
926 }
927 u_case_cont_body() {
928
929 ${TEST_SH} -c 'case x in (x) false ;& (y) : ;; esac' 2>/dev/null ||
930 atf_skip ";& case list terminator unsupported in ${TEST_SH}"
931
932 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
933 'case x in x) ;& esac'
934 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
935 'case x in (x) ;& esac'
936 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
937 'case x in x|y) ;& esac'
938 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
939 'case x in (x|y) ;& esac'
940
941 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
942 'case x in x );&esac'
943 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
944 'case x in ( x );&esac'
945 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
946 'case x in x | y );&esac'
947 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
948 'case x in ( x | y );&esac'
949
950 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
951 'case x in x) ;& (y) esac'
952 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
953 'case x in (x) ;& esac'
954 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
955 'case x in x|y) ;& esac'
956 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
957 'case x in (x|y) ;& esac'
958 }
959
960 atf_test_case x_functions
961 x_functions_head() {
962 atf_set "descr" "Check function definition parsing"
963 }
964 x_functions_body() {
965 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
966 'func() { :; }'
967 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
968 'func() { :; }; func'
969 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
970 'func()(:)'
971 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
972 'func()if :; then false; else true; fi'
973 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
974 'func()while false; do :;done'
975 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
976 'func () for a in b c; do :; done'
977 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
978 'func() case x in (y) esac'
979
980 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
981 'f1() { f2() { :; }; }; f1; f2'
982
983 # f2 should be not found, but f1 clears $?
984 atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \
985 'f1() { f2() { :; }; }; f2; f1'
986
987 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
988 'f1() { eval "$1() { :; }"; }; f1 f2; f2'
989 }
990
991 atf_init_test_cases() {
992 atf_add_test_case a_basic_tokenisation
993 atf_add_test_case b_comments
994 atf_add_test_case c_line_wrapping
995 atf_add_test_case d_redirects
996 atf_add_test_case f_variable_syntax
997 atf_add_test_case g_var_assign
998 atf_add_test_case i_pipelines
999 atf_add_test_case j_and_or_lists
1000 atf_add_test_case k_lists
1001 atf_add_test_case l_async_lists
1002 atf_add_test_case m_compound_lists
1003 atf_add_test_case q_for_loop
1004 atf_add_test_case r_case
1005 atf_add_test_case s_if
1006 atf_add_test_case t_loops
1007 atf_add_test_case u_case_cont
1008 atf_add_test_case x_functions
1009 }
1010