t_here.sh revision 1.9 1 # $NetBSD: t_here.sh,v 1.9 2021/11/22 05:21:54 kre Exp $
2 #
3 # Copyright (c) 2007 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
30 nl='
31 '
32
33 reset()
34 {
35 TEST_NUM=0
36 TEST_FAILURES=''
37 TEST_FAIL_COUNT=0
38 TEST_ID="$1"
39 }
40
41 check()
42 {
43 fail=false
44 TEMP_FILE=$( mktemp OUT.XXXXXX )
45 TEST_NUM=$(( $TEST_NUM + 1 ))
46
47 # our local shell (ATF_SHELL) better do quoting correctly...
48 # some of the tests expect us to expand $nl internally...
49 CMD="nl='${nl}'; $1"
50
51 result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" )"
52 STATUS=$?
53
54 if [ "${STATUS}" -ne "$3" ]; then
55 echo >&2 "[$TEST_NUM] expected exit code $3, got ${STATUS}"
56
57 # don't actually fail just because of wrong exit code
58 # unless we either expected, or received "good"
59 case "$3/${STATUS}" in
60 (*/0|0/*) fail=true;;
61 esac
62 fi
63
64 if [ "$3" -eq 0 ]; then
65 if [ -s "${TEMP_FILE}" ]; then
66 echo >&2 \
67 "[$TEST_NUM] Messages produced on stderr unexpected..."
68 cat "${TEMP_FILE}" >&2
69 fail=true
70 fi
71 else
72 if ! [ -s "${TEMP_FILE}" ]; then
73 echo >&2 \
74 "[$TEST_NUM] Expected messages on stderr, nothing produced"
75 fail=true
76 fi
77 fi
78 rm -f "${TEMP_FILE}"
79
80 # Remove newlines (use local shell for this)
81 result="$(
82 IFS="$nl"
83 set -f
84 set -- $result
85 IFS=' '
86 printf %s "$*"
87 )"
88 if [ "$2" != "$result" ]
89 then
90 echo >&2 "[$TEST_NUM] Expected output '$2', received '$result'"
91 fail=true
92 fi
93
94 if $fail
95 then
96 echo >&2 "[$TEST_NUM] Full command: <<${CMD}>>"
97 fi
98
99 $fail && test -n "$TEST_ID" && {
100 TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+
101 }${TEST_ID}[$TEST_NUM]: test of '$1' failed";
102 TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 ))
103 return 0
104 }
105 $fail && atf_fail "Test[$TEST_NUM] of '$1' failed"
106 return 0
107 }
108
109 results()
110 {
111 test -z "${TEST_ID}" && return 0
112 test -z "${TEST_FAILURES}" && return 0
113
114 echo >&2 "=========================================="
115 echo >&2 "While testing '${TEST_ID}'"
116 echo >&2 " - - - - - - - - - - - - - - - - -"
117 echo >&2 "${TEST_FAILURES}"
118 atf_fail \
119 "Test ${TEST_ID}: $TEST_FAIL_COUNT subtests (of $TEST_NUM) failed - see stderr"
120 }
121
122 atf_test_case do_simple
123 do_simple_head() {
124 atf_set "descr" "Basic tests for here documents"
125 }
126 do_simple_body() {
127 y=x
128
129 reset 'simple'
130 IFS=' '
131 check 'x=`cat <<EOF'$nl'text'${nl}EOF$nl'`; echo $x' 'text' 0
132 check 'x=`cat <<\EOF'$nl'text'${nl}EOF$nl'`; echo $x' 'text' 0
133
134 check "y=${y};"'x=`cat <<EOF'$nl'te${y}t'${nl}EOF$nl'`; echo $x' \
135 'text' 0
136 check "y=${y};"'x=`cat <<\EOF'$nl'te${y}t'${nl}EOF$nl'`; echo $x' \
137 'te${y}t' 0
138 check "y=${y};"'x=`cat <<"EOF"'$nl'te${y}t'${nl}EOF$nl'`; echo $x' \
139 'te${y}t' 0
140 check "y=${y};"'x=`cat <<'"'EOF'"$nl'te${y}t'${nl}EOF$nl'`; echo $x' \
141 'te${y}t' 0
142
143 # check that quotes in the here doc survive and cause no problems
144 check "cat <<EOF${nl}te'xt${nl}EOF$nl" "te'xt" 0
145 check "cat <<\EOF${nl}te'xt${nl}EOF$nl" "te'xt" 0
146 check "cat <<'EOF'${nl}te'xt${nl}EOF$nl" "te'xt" 0
147 check "cat <<EOF${nl}te\"xt${nl}EOF$nl" 'te"xt' 0
148 check "cat <<\EOF${nl}te\"xt${nl}EOF$nl" 'te"xt' 0
149 check "cat <<'EOF'${nl}te\"xt${nl}EOF$nl" 'te"xt' 0
150 check "cat <<'EO'F${nl}te\"xt${nl}EOF$nl" 'te"xt' 0
151
152 check "y=${y};"'x=`cat <<EOF'$nl'te'"'"'${y}t'${nl}EOF$nl'`; echo $x' \
153 'te'"'"'xt' 0
154 check "y=${y};"'x=`cat <<EOF'$nl'te'"''"'${y}t'${nl}EOF$nl'`; echo $x' \
155 'te'"''"'xt' 0
156
157 # note that the blocks of empty space in the following must
158 # be entirely tab characters, no spaces.
159
160 check 'x=`cat <<EOF'"$nl text${nl}EOF$nl"'`; echo "$x"' \
161 ' text' 0
162 check 'x=`cat <<-EOF'"$nl text${nl}EOF$nl"'`; echo $x' \
163 'text' 0
164 check 'x=`cat <<-EOF'"${nl}text${nl} EOF$nl"'`; echo $x' \
165 'text' 0
166 check 'x=`cat <<-\EOF'"$nl text${nl} EOF$nl"'`; echo $x' \
167 'text' 0
168 check 'x=`cat <<- "EOF"'"$nl text${nl}EOF$nl"'`; echo $x' \
169 'text' 0
170 check 'x=`cat <<- '"'EOF'${nl}text${nl} EOF$nl"'`; echo $x' \
171 'text' 0
172 results
173 }
174
175 atf_test_case end_markers
176 end_markers_head() {
177 atf_set "descr" "Tests for various end markers of here documents"
178 }
179 end_markers_body() {
180
181 reset 'end_markers'
182 for end in EOF 1 \! '$$$' "string " a\\\ a\\\ \ '&' '' ' ' ' ' \
183 --STRING-- . '~~~' ')' '(' '#' '()' '(\)' '(\/)' '--' '\' '{' '}' \
184 VERYVERYVERYVERYLONGLONGLONGin_fact_absurdly_LONG_LONG_HERE_DOCUMENT_TERMINATING_MARKER_THAT_goes_On_forever_and_ever_and_ever...
185 do
186 # check unquoted end markers
187 case "${end}" in
188 ('' | *[' ()\$&#*~']* ) ;; # skip unquoted endmark test for these
189 (*) check \
190 'x=$(cat << '"${end}${nl}text${nl}${end}${nl}"'); printf %s "$x"' 'text' 0
191 ;;
192 esac
193
194 # and quoted end markers
195 check \
196 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${nl}"'); printf %s "$x"' 'text' 0
197
198 # and see what happens if we encounter "almost" an end marker
199 case "${#end}" in
200 (0|1) ;; # too short to try truncation tests
201 (*) check \
202 'x=$(cat <<'"'${end}'${nl}text${nl}${end%?}${nl}${end}${nl}"'); printf %s "$x"' \
203 "text ${end%?}" 0
204 check \
205 'x=$(cat <<'"'${end}'${nl}text${nl}${end#?}${nl}${end}${nl}"'); printf %s "$x"' \
206 "text ${end#?}" 0
207 check \
208 'x=$(cat <<'"'${end}'${nl}text${nl}${end%?}+${nl}${end}${nl}"');printf %s "$x"' \
209 "text ${end%?}+" 0
210 ;;
211 esac
212
213 # or something that is a little longer
214 check \
215 'x=$(cat <<'"'${end}'${nl}text${nl}${end}x${nl}${end}${nl}"'); printf %s "$x"' \
216 "text ${end}x" 0
217 check \
218 'x=$(cat <<'"'${end}'${nl}text${nl}!${end}${nl}${end}${nl}"'); printf %s "$x"' \
219 "text !${end}" 0
220
221 # or which does not begin at start of line
222 check \
223 'x=$(cat <<'"'${end}'${nl}text${nl} ${end}${nl}${end}${nl}"'); printf %s "$x"' \
224 "text ${end}" 0
225 check \
226 'x=$(cat <<'"'${end}'${nl}text${nl} ${end}${nl}${end}${nl}"'); printf %s "$x"' \
227 "text ${end}" 0
228
229 # or end at end of line
230 check \
231 'x=$(cat <<'"'${end}'${nl}text${nl}${end} ${nl}${end}${nl}"'); printf %s "$x"' \
232 "text ${end} " 0
233
234 # or something that is correct much of the way, but then...
235
236 case "${#end}" in
237 (0) ;; # cannot test this one
238 (1) check \
239 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${end}${nl}${end}${nl}"'); printf %s "$x"' \
240 "text ${end}${end}" 0
241 ;;
242 (2-7) pfx="${end%?}"
243 check \
244 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${pfx}${nl}${end}${nl}"'); printf %s "$x"' \
245 "text ${end}${pfx}" 0
246 check \
247 'x=$(cat <<'"'${end}'${nl}text${nl}${pfx}${end}${nl}${end}${nl}"'); printf %s "$x"' \
248 "text ${pfx}${end}" 0
249 ;;
250 (*) pfx=${end%??????}; sfx=${end#??????}
251 check \
252 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${sfx}${nl}${end}${nl}"'); printf %s "$x"' \
253 "text ${end}${sfx}" 0
254 check \
255 'x=$(cat <<'"'${end}'${nl}text${nl}${pfx}${end}${nl}${end}${nl}"'); printf %s "$x"' \
256 "text ${pfx}${end}" 0
257 check \
258 'x=$(cat <<'"'${end}'${nl}text${nl}${pfx}${sfx}${nl}${end}${nl}"'); printf %s "$x"' \
259 "text ${pfx}${sfx}" 0
260 ;;
261 esac
262 done
263
264 # Add striptabs tests (in similar way) here one day...
265
266 results
267 }
268
269 atf_test_case incomplete
270 incomplete_head() {
271 atf_set "descr" "Basic tests for incomplete here documents"
272 }
273 incomplete_body() {
274 reset incomplete
275
276 check 'cat <<EOF' '' 2
277 check 'cat <<- EOF' '' 2
278 check 'cat <<\EOF' '' 2
279 check 'cat <<- \EOF' '' 2
280
281 check 'cat <<EOF'"${nl}" '' 2
282 check 'cat <<- EOF'"${nl}" '' 2
283 check 'cat <<'"'EOF'${nl}" '' 2
284 check 'cat <<- "EOF"'"${nl}" '' 2
285
286 check 'cat << EOF'"${nl}${nl}" '' 2
287 check 'cat <<-EOF'"${nl}${nl}" '' 2
288 check 'cat << '"'EOF'${nl}${nl}" '' 2
289 check 'cat <<-"EOF"'"${nl}${nl}" '' 2
290
291 check 'cat << EOF'"${nl}"'line 1'"${nl}" '' 2
292 check 'cat <<-EOF'"${nl}"' line 1'"${nl}" '' 2
293 check 'cat << EOF'"${nl}"'line 1'"${nl}"' line 2'"${nl}" '' 2
294 check 'cat <<-EOF'"${nl}"' line 1'"${nl}"'line 2'"${nl}" '' 2
295
296 check 'cat << EOF'"${nl}line 1${nl}${nl}line3${nl}${nl}5!${nl}" '' 2
297
298 results
299 }
300
301 atf_test_case lineends
302 lineends_head() {
303 atf_set "descr" "Tests for line endings in here documents"
304 }
305 lineends_body() {
306 reset lineends
307
308 # note that "check" removes newlines from stdout before comparing.
309 # (they become blanks, provided there is something before & after)
310
311 check 'cat << \echo'"${nl}"'\'"${nl}echo${nl}echo${nl}" '\' 0
312 check 'cat << echo'"${nl}"'\'"${nl}echo${nl}echo${nl}" 'echo' 0
313 check 'cat << echo'"${nl}"'\\'"${nl}echo${nl}echo${nl}" '\' 0
314
315 check 'X=3; cat << ec\ho'"${nl}"'$X\'"${nl}echo${nl}echo${nl}" \
316 '$X\' 0
317 check 'X=3; cat << echo'"${nl}"'$X'"${nl}echo${nl}echo${nl}" \
318 '3' 0
319 check 'X=3; cat << echo'"${nl}"'$X\'"${nl}echo${nl}echo${nl}" \
320 '' 0
321 check 'X=3; cat << echo'"${nl}"'${X}\'"${nl}echo${nl}echo${nl}" \
322 '3echo' 0
323 check 'X=3; cat << echo'"${nl}"'\$X\'"${nl}echo${nl}echo${nl}" \
324 '$Xecho' 0
325 check 'X=3; cat << echo'"${nl}"'\\$X \'"${nl}echo${nl}echo${nl}" \
326 '\3 echo' 0
327
328 check \
329 'cat << "echo"'"${nl}"'line1\'"${nl}"'line2\'"${nl}echo${nl}echo${nl}" \
330 'line1\ line2\' 0
331 check \
332 'cat << echo'"${nl}"'line1\'"${nl}"'line2\'"${nl}echo${nl}echo${nl}" \
333 'line1line2echo' 0
334
335 results
336 }
337
338 atf_test_case multiple
339 multiple_head() {
340 atf_set "descr" "Tests for multiple here documents on one cmd line"
341 }
342 multiple_body() {
343 reset multiple
344
345 check \
346 "(cat ; cat <&3) <<EOF0 3<<EOF3${nl}STDIN${nl}EOF0${nl}-3-${nl}EOF3${nl}" \
347 'STDIN -3-' 0
348
349 check "(read line; echo \"\$line\"; cat <<EOF1; echo \"\$line\") <<EOF2
350 The File
351 EOF1
352 The Line
353 EOF2
354 " 'The Line The File The Line' 0
355
356 check "(read line; echo \"\$line\"; cat <<EOF; echo \"\$line\") <<EOF
357 The File
358 EOF
359 The Line
360 EOF
361 " 'The Line The File The Line' 0
362
363 check "V=1; W=2; cat <<-1; cat <<2; cat <<- 3; cat <<'4';"' cat <<\5
364 $V
365 $W
366 3
367 4
368 5
369 1
370 2
371 5
372 4*$W+\$V
373 3
374 $W
375 1
376 2
377 3
378 4
379 7+$V
380 $W+6
381 5
382 ' '1 2 3 4 5 5 4*2+$V $W 1 2 3 7+$V $W+6' 0
383
384 results
385 }
386
387 atf_test_case nested
388 nested_head() {
389 atf_set "descr" "Tests for nested here documents for one cmd"
390 }
391 nested_body() {
392 reset nested
393
394 check \
395 'cat << EOF1'"${nl}"'$(cat << EOF2'"${nl}LINE${nl}EOF2${nl}"')'"${nl}EOF1${nl}"\
396 'LINE' 0
397
398 # This next one fails ... and correctly, so we will omit it (bad test)
399 # Reasoning is that the correct data "$(cat << EOF2)\nLINE\nEOF2\n" is
400 # collected for the outer (EOF1) heredoc, when that is parsed, it looks
401 # like
402 # $(cat <<EOF2)
403 # LINE
404 # EOF2
405 # which looks like a good command - except it is being parsed in "heredoc"
406 # syntax, which means it is enclosed in double quotes, which means that
407 # the newline after the ')' in the first line is not a newline token, but
408 # just a character. The EOF2 heredoc cannot start until after the next
409 # newline token, of which there are none here... LINE and EOF2 are just
410 # more data in the outer EOF1 heredoc for its "cat" command to read & write.
411 #
412 # The previous sub-test works because there the \n comes inside the
413 # $( ), and in there, the outside quoting rules are suspended, and it
414 # all starts again - so that \n is a newline token, and the EOF2 heredoc
415 # is processed.
416 #
417 # check \
418 # 'cat << EOF1'"${nl}"'$(cat << EOF2 )'"${nl}LINE${nl}EOF2${nl}EOF1${nl}" \
419 # 'LINE' 0
420
421 L='cat << EOF1'"${nl}"'LINE1$(cat << EOF2'"${nl}"
422 L="${L}"'LINE2$(cat << EOF3'"${nl}"
423 L="${L}"'LINE3$(cat << EOF4'"${nl}"
424 L="${L}"'LINE4$(cat << EOF5'"${nl}"
425 L="${L}LINE5${nl}EOF5${nl})4${nl}EOF4${nl})3${nl}"
426 L="${L}EOF3${nl})2${nl}EOF2${nl})1${nl}EOF1${nl}"
427
428 # That mess is ...
429 #
430 # cat <<EOF1
431 # LINE1$(cat << EOF2
432 # LINE2$(cat << EOF3
433 # LINE3$(cat << EOF4
434 # LINE4$(cat << EOF5
435 # LINE5
436 # EOF5
437 # )4
438 # EOF4
439 # )3
440 # EOF3
441 # )2
442 # EOF2
443 # )1
444 # EOF1
445
446 check "${L}" 'LINE1LINE2LINE3LINE4LINE54321' 0
447
448 results
449 }
450
451 atf_test_case quoting
452 quoting_head() {
453 atf_set "descr" "Tests for use of quotes inside here documents"
454 }
455 quoting_body() {
456 reset quoting
457
458 check 'X=!; cat <<- E\0F
459 <'\''"'\'' \\$X\$X "'\''" \\>
460 E0F
461 ' '<'\''"'\'' \\$X\$X "'\''" \\>' 0
462
463 check 'X=!; cat <<- E0F
464 <'\''"'\'' \\$X\$X "'\''" \\>
465 E0F
466 ' '<'\''"'\'' \!$X "'\''" \>' 0
467
468 check 'cat <<- END
469 $( echo "'\''" ) $( echo '\''"'\'' ) $( echo \\ )
470 END
471 ' "' \" \\" 0
472
473 check 'X=12345; Y="string1 line1?-line2"; Z=; unset W; cat <<-EOF
474 ${#X}${Z:-${Y}}${W+junk}${Y%%l*}${Y#*\?}
475 "$Z"'\''$W'\'' ${Y%" "*} $(( X + 54321 ))
476 EOF
477 ' '5string1 line1?-line2string1 -line2 ""'\'\'' string1 66666' 0
478
479 # check that \ only quotes the magic chars, otherwise is retained
480 check 'p=A; cat <<-EOF
481 ${p+\%$p\%}
482 ${p+%$p%}
483 EOF
484 ' '\%A\% %A%' 0
485
486 # and check that " is not magic, so \ does not quote it
487 check 'p=A; cat <<-EOF
488 ${p+\"$p\"}
489 ${p+"$p"}
490 EOF
491 ' '\"A\" "A"' 0
492
493 # except in a ${var%<word>} word, base syntax reapplies, and
494 # there quotes are magic again
495 check 'p=ABCD; cat <<-EOF
496 ${p%B?D}
497 ${p%B\?D}
498 ${p%"BCD"}
499 "${p%??}"
500 ${p#"${p%??}"}
501 "${p#"${p%?"?"}"}"
502 EOF
503 ' 'A ABCD A "AB" CD ""' 0
504
505 check 'p=AB??; cat <<-EOF
506 ${p%B?D}
507 ${p%B\??}
508 ${p%"B??"}
509 "${p%??}"
510 ${p#"${p%??}"}
511 "${p#"${p%?"?"}"}"
512 EOF
513 ' 'AB?? A A "AB" ?? "??"' 0
514
515 results
516 }
517
518 #
519 # This next test is really just testing what our shell happens to do.
520 # There doesn't seem to be any spec on in which context expansions
521 # in redirects are processed. Most shells do them in the parent
522 # shell context, meaning that side effects of the expansion become
523 # visible to the shell - a couple process redirect expansions in
524 # a subshell, meaning that side effects are lost.
525 #
526 # Before PR bin/53550 was fixed, the NetBSD sh evaluated all redirect
527 # expansions, except here documents, in the context of the shell, and
528 # here document redirects in a subshell context. That distinction
529 # makes no real sense (and only an old, and maybe still current, FreeBSD
530 # shell shares that pecadillo.) Afer that fix, the NetBSD shell joins
531 # almost all others in expanding redirects (all of them) in the shell
532 # context, meaning that side effects of here documenty expansions become
533 # visible in the shell.
534 #
535 # Before the fix, we used to get "2\n1\n" as the output from this
536 # test, now the variable assignment in the here document persists
537 # and we get "2\n2\n" as do most other shells. (bash is a notable
538 # exception, but it does all redirect expansions in a subshell context)
539 #
540
541 atf_test_case side_effects
542 side_effects_head() {
543 atf_set "descr" "Tests how side effects in here documents are handled"
544 }
545 side_effects_body() {
546
547 atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c '
548 unset X
549 cat <<-EOF
550 ${X=2}
551 EOF
552 echo "${X-1}"
553 '
554 }
555
556 # This is a test for the specific bug reported in PR bin/53550
557 # This should work in any shell.
558
559 atf_test_case exit_status
560 exit_status_head() {
561 atf_set descr "Tests exit status of a command substitution in a heredoc"
562 }
563 exit_status_body() {
564
565 # PR bin/53550 test
566 atf_check -s exit:7 -o empty -e empty ${TEST_SH} -c '
567 <<-EOF
568 $(exit 7)
569 EOF
570 '
571 }
572
573 # The following tests a problem reported on the austin-list 2021-09-08
574 # by oguzismailuysal (at] gmail.com ... it affected all ash derived shells
575 atf_test_case hard_cases
576 hard_cases_head() {
577 atf_set "descr" \
578 "Tests here docs in positions that have confised our parser"
579 }
580 hard_cases_body() {
581
582 atf_check -s exit:0 -o inline:'xxx\n' -e empty ${TEST_SH} -c '
583 : <<- do | for x in xxx
584 do
585 do echo $x
586 done'
587
588 atf_check -s exit:0 -o inline:'xxx\n' -e empty ${TEST_SH} -c '
589 set -- xxx
590 : <<- done | for x in xxx
591 done
592 do echo $x
593 done'
594
595 atf_check -s exit:0 -o inline:'xxx\n' -e empty ${TEST_SH} -c '
596 : <<- in | case xxx
597 in
598 in xxx) echo xxx;;
599 esac'
600 }
601
602 atf_test_case vicious
603 vicious_head() {
604 atf_set "descr" "Tests for obscure and obnoxious uses of here docs"
605 }
606 vicious_body() {
607 reset
608
609 cat <<- \END_SCRIPT > script
610 cat <<ONE && cat \
611 <<TWO
612 a
613 ONE
614 b
615 TWO
616 END_SCRIPT
617
618 atf_check -s exit:0 -o inline:'a\nb\n' -e empty ${TEST_SH} script
619
620 # This next one is causing discussion currently (late Feb 2016)
621 # amongst stds writers & implementors. Consequently we
622 # will not check what it produces. The eventual result
623 # seems unlikely to be what we currently output, which
624 # is:
625 # A:echo line 1
626 # B:echo line 2)" && prefix DASH_CODE <<DASH_CODE
627 # B:echo line 3
628 # line 4
629 # line 5
630 #
631 # The likely intended output is ...
632 #
633 # A:echo line 3
634 # B:echo line 1
635 # line 2
636 # DASH_CODE:echo line 4)"
637 # DASH_CODE:echo line 5
638 #
639 # The difference is explained by differing opinions on just
640 # when processing of a here doc should start
641
642 cat <<- \END_SCRIPT > script
643 prefix() { sed -e "s/^/$1:/"; }
644 DASH_CODE() { :; }
645
646 prefix A <<XXX && echo "$(prefix B <<XXX
647 echo line 1
648 XXX
649 echo line 2)" && prefix DASH_CODE <<DASH_CODE
650 echo line 3
651 XXX
652 echo line 4)"
653 echo line 5
654 DASH_CODE
655 END_SCRIPT
656
657 # we will just verify that the shell can parse the
658 # script somehow, and doesn't fall over completely...
659
660 atf_check -s exit:0 -o ignore -e empty ${TEST_SH} script
661 }
662
663 atf_init_test_cases() {
664 atf_add_test_case do_simple # not worthy of a comment
665 atf_add_test_case end_markers # the mundane, the weird, the bizarre
666 atf_add_test_case exit_status # PR bin/53550, cmdsub in heredoc
667 atf_add_test_case incomplete # where the end marker isn't...
668 atf_add_test_case lineends # test weird line endings in heredocs
669 atf_add_test_case multiple # multiple << operators on one cmd
670 atf_add_test_case nested # here docs inside here docs
671 atf_add_test_case quoting # stuff quoted inside
672 atf_add_test_case side_effects # here docs that modify environment
673 atf_add_test_case hard_cases # here doc bodies appearing mid command
674 atf_add_test_case vicious # evil test from the austin-l list...
675 }
676