t_redir.sh revision 1.13 1 # $NetBSD: t_redir.sh,v 1.13 2021/11/16 11:15:26 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
30 # Any failures in this first test means it is not worth bothering looking
31 # for causes of failures in any other tests, make this one work first.
32
33 # Problems with this test usually mean inadequate ATF_SHELL used for testing.
34 # (though if all pass but the last, it might be a TEST_SH problem.)
35
36 atf_test_case basic_test_method_test
37 basic_test_method_test_head()
38 {
39 atf_set "descr" "Tests that test method works as expected"
40 }
41 basic_test_method_test_body()
42 {
43 cat <<- 'DONE' |
44 DONE
45 atf_check -s exit:0 -o empty -e empty ${TEST_SH}
46 cat <<- 'DONE' |
47 DONE
48 atf_check -s exit:0 -o match:0 -e empty ${TEST_SH} -c 'wc -l'
49
50 cat <<- 'DONE' |
51 echo hello
52 DONE
53 atf_check -s exit:0 -o match:hello -e empty ${TEST_SH}
54 cat <<- 'DONE' |
55 echo hello
56 DONE
57 atf_check -s exit:0 -o match:1 -e empty ${TEST_SH} -c 'wc -l'
58
59 cat <<- 'DONE' |
60 echo hello\
61 world
62 DONE
63 atf_check -s exit:0 -o match:helloworld -e empty ${TEST_SH}
64 cat <<- 'DONE' |
65 echo hello\
66 world
67 DONE
68 atf_check -s exit:0 -o match:2 -e empty ${TEST_SH} -c 'wc -l'
69
70 printf '%s\n%s\n%s\n' Line1 Line2 Line3 > File
71 atf_check -s exit:0 -o inline:'Line1\nLine2\nLine3\n' -e empty \
72 ${TEST_SH} -c 'cat File'
73
74 cat <<- 'DONE' |
75 set -- X "" '' Y
76 echo ARGS="${#}"
77 echo '' -$1- -$2- -$3- -$4-
78 cat <<EOF
79 X=$1
80 EOF
81 cat <<\EOF
82 Y=$4
83 EOF
84 DONE
85 atf_check -s exit:0 -o match:ARGS=4 -o match:'-X- -- -- -Y-' \
86 -o match:X=X -o match:'Y=\$4' -e empty ${TEST_SH}
87 }
88
89 atf_test_case do_input_redirections
90 do_input_redirections_head()
91 {
92 atf_set "descr" "Tests that simple input redirection works"
93 }
94 do_input_redirections_body()
95 {
96 printf '%s\n%s\n%s\nEND\n' 'First Line' 'Second Line' 'Line 3' >File
97
98 atf_check -s exit:0 -e empty \
99 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
100 ${TEST_SH} -c 'cat < File'
101 atf_check -s exit:0 -e empty \
102 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
103 ${TEST_SH} -c 'cat <File'
104 atf_check -s exit:0 -e empty \
105 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
106 ${TEST_SH} -c 'cat< File'
107 atf_check -s exit:0 -e empty \
108 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
109 ${TEST_SH} -c 'cat < "File"'
110 atf_check -s exit:0 -e empty \
111 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
112 ${TEST_SH} -c '< File cat'
113
114 ln File wc
115 atf_check -s exit:0 -e empty \
116 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
117 ${TEST_SH} -c '< wc cat'
118
119 mv wc cat
120 atf_check -s exit:0 -e empty -o match:4 \
121 ${TEST_SH} -c '< cat wc'
122
123
124 cat <<- 'EOF' |
125 for l in 1 2 3; do
126 read line < File
127 echo "$line"
128 done
129 EOF
130 atf_check -s exit:0 -e empty \
131 -o inline:'First Line\nFirst Line\nFirst Line\n' \
132 ${TEST_SH}
133
134 cat <<- 'EOF' |
135 for l in 1 2 3; do
136 read line
137 echo "$line"
138 done <File
139 EOF
140 atf_check -s exit:0 -e empty \
141 -o inline:'First Line\nSecond Line\nLine 3\n' \
142 ${TEST_SH}
143
144 cat <<- 'EOF' |
145 for l in 1 2 3; do
146 read line < File
147 echo "$line"
148 done <File
149 EOF
150 atf_check -s exit:0 -e empty \
151 -o inline:'First Line\nFirst Line\nFirst Line\n' \
152 ${TEST_SH}
153
154 cat <<- 'EOF' |
155 line=
156 while [ "$line" != END ]; do
157 read line || exit 1
158 echo "$line"
159 done <File
160 EOF
161 atf_check -s exit:0 -e empty \
162 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
163 ${TEST_SH}
164
165 cat <<- 'EOF' |
166 while :; do
167 read line || exit 0
168 echo "$line"
169 done <File
170 EOF
171 atf_check -s exit:0 -e empty \
172 -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \
173 ${TEST_SH}
174
175 cat <<- 'EOF' |
176 l=''
177 while read line < File
178 do
179 echo "$line"
180 l="${l}x"
181 [ ${#l} -ge 3 ] && break
182 done
183 echo DONE
184 EOF
185 atf_check -s exit:0 -e empty \
186 -o inline:'First Line\nFirst Line\nFirst Line\nDONE\n' \
187 ${TEST_SH}
188
189 cat <<- 'EOF' |
190 while read line
191 do
192 echo "$line"
193 done <File
194 echo DONE
195 EOF
196 atf_check -s exit:0 -e empty \
197 -o inline:'First Line\nSecond Line\nLine 3\nEND\nDONE\n' \
198 ${TEST_SH}
199
200 cat <<- 'EOF' |
201 l=''
202 while read line
203 do
204 echo "$line"
205 l="${l}x"
206 [ ${#l} -ge 3 ] && break
207 done <File
208 echo DONE
209 EOF
210 atf_check -s exit:0 -e empty \
211 -o inline:'First Line\nSecond Line\nLine 3\nDONE\n' ${TEST_SH}
212
213 cat <<- 'EOF' |
214 l=''
215 while read line1 <File
216 do
217 read line2
218 echo "$line1":"$line2"
219 l="${l}x"
220 [ ${#l} -ge 2 ] && break
221 done <File
222 echo DONE
223 EOF
224 atf_check -s exit:0 -e empty \
225 -o inline:'First Line:First Line\nFirst Line:Second Line\nDONE\n' \
226 ${TEST_SH}
227 }
228
229 atf_test_case do_output_redirections
230 do_output_redirections_head()
231 {
232 atf_set "descr" "Test Output redirections"
233 }
234 do_output_redirections_body()
235 {
236 nl='
237 '
238 T=0
239 i() { T=$(expr "$T" + 1); }
240
241 rm -f Output 2>/dev/null || :
242 test -f Output && atf_fail "Unable to remove Output file"
243 #1
244 i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '> Output'
245 test -f Output || atf_fail "#$T: Did not make Output file"
246 #2
247 rm -f Output 2>/dev/null || :
248 i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '>> Output'
249 test -f Output || atf_fail "#$T: Did not make Output file"
250 #3
251 rm -f Output 2>/dev/null || :
252 i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '>| Output'
253 test -f Output || atf_fail "#$T: Did not make Output file"
254
255 #4
256 rm -f Output 2>/dev/null || :
257 i
258 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'echo Hello >Output'
259 test -s Output || atf_fail "#$T: Did not make non-empty Output file"
260 test "$(cat Output)" = "Hello" ||
261 atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'"
262 #5
263 i
264 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'echo Hello>!Output'
265 test -s Output || atf_fail "#$T: Did not make non-empty Output file"
266 test "$(cat Output)" = "Hello" ||
267 atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'"
268 #6
269 i
270 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'echo Bye >>Output'
271 test -s Output || atf_fail "#$T: Removed Output file"
272 test "$(cat Output)" = "Hello${nl}Bye" || atf_fail \
273 "#$T: Incorrect Output: Should be 'Hello\\nBye' is '$(cat Output)'"
274 #7
275 i; atf_check -s exit:0 -o inline:'line 1\nline 2\n' -e empty \
276 ${TEST_SH} -c \
277 'echo line 1 > Output; echo line 2 >> Output; cat Output'
278 test "$(cat Output)" = "line 1${nl}line 2" || atf_fail \
279 "#$T: Incorrect Output: Should be 'line 1\\nline 2' is '$(cat Output)'"
280 #8
281 i; atf_check -s exit:0 -o inline:'line 2\n' -e empty \
282 ${TEST_SH} -c 'echo line 1 > Output; echo line 2'
283 test "$(cat Output)" = "line 1" || atf_fail \
284 "#$T: Incorrect Output: Should be 'line 1' is '$(cat Output)'"
285 #9
286 i; atf_check -s exit:0 -o empty -e empty \
287 ${TEST_SH} -c '(echo line 1; echo line 2 > Out2) > Out1'
288 test "$(cat Out1)" = "line 1" || atf_fail \
289 "#$T: Incorrect Out1: Should be 'line 1' is '$(cat Out1)'"
290 test "$(cat Out2)" = "line 2" || atf_fail \
291 "#$T: Incorrect Out2: Should be 'line 2' is '$(cat Out2)'"
292 #10
293 i; atf_check -s exit:0 -o empty -e empty \
294 ${TEST_SH} -c '{ echo line 1; echo line 2 > Out2;} > Out1'
295 test "$(cat Out1)" = "line 1" || atf_fail \
296 "#$T: Incorrect Out1: Should be 'line 1' is '$(cat Out1)'"
297 test "$(cat Out2)" = "line 2" || atf_fail \
298 "#$T: Incorrect Out2: Should be 'line 2' is '$(cat Out2)'"
299 #11
300 i; rm -f Out1 Out2 2>/dev/null || :
301 cat <<- 'EOF' |
302 for arg in 'line 1' 'line 2' 'line 3'
303 do
304 echo "$arg"
305 echo "$arg" > Out1
306 done > Out2
307 EOF
308 atf_check -s exit:0 -o empty -e empty ${TEST_SH}
309 test "$(cat Out1)" = "line 3" || atf_fail \
310 "#$T: Incorrect Out1: Should be 'line 3' is '$(cat Out1)'"
311 test "$(cat Out2)" = "line 1${nl}line 2${nl}line 3" || atf_fail \
312 "#$T: Incorrect Out2: Should be 'line 1\\nline 2\\nline 3' is '$(cat Out2)'"
313 #12
314 i; rm -f Out1 Out2 2>/dev/null || :
315 cat <<- 'EOF' |
316 for arg in 'line 1' 'line 2' 'line 3'
317 do
318 echo "$arg"
319 echo "$arg" >> Out1
320 done > Out2
321 EOF
322 atf_check -s exit:0 -o empty -e empty ${TEST_SH}
323 test "$(cat Out1)" = "line 1${nl}line 2${nl}line 3" || atf_fail \
324 "#$T: Incorrect Out1: Should be 'line 1\\nline 2\\nline 3' is '$(cat Out1)'"
325 test "$(cat Out2)" = "line 1${nl}line 2${nl}line 3" || atf_fail \
326 "#$T: Incorrect Out2: Should be 'line 1\\nline 2\\nline 3' is '$(cat Out2)'"
327 }
328
329 atf_test_case do_redirect_input_output
330 do_redirect_input_output_head()
331 {
332 atf_set "descr" "Test Input+Output (BiDir) redirections"
333 }
334 do_redirect_input_output_body()
335 {
336 nl='
337 '
338 T=0
339 i() { T=$(expr "$T" + 1); }
340
341 rm -f Output 2>/dev/null || :
342 test -f Output && atf_fail "Unable to remove Output file"
343 #1
344 i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '<> Output'
345 test -f Output || atf_fail "#$T: Did not make Output file"
346
347 #2
348 echo data >Output 2>/dev/null || :
349 i
350 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
351 '<>Output'
352 test -f Output || atf_fail "#$T: Removed Output file"
353 test -s Output || atf_fail "#$T: Did not keep data in Output file"
354 test "$(cat Output)" = "data" ||
355 atf_fail "#$T: Incorrect Output: Should be 'data' is '$(cat Output)'"
356
357 #3
358 rm -f Output 2>/dev/null || :
359 i
360 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
361 'echo Hello 1<>Output'
362 test -s Output || atf_fail "#$T: Did not keep non-empty Output file"
363 test "$(cat Output)" = "Hello" ||
364 atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'"
365
366 #4
367 printf data >Output 2>/dev/null || :
368 i
369 atf_check -s exit:0 -o inline:'data' -e empty ${TEST_SH} -c \
370 'cat <>Output'
371 test -f Output || atf_fail "#$T: Removed Output file"
372 test -s Output || atf_fail "#$T: Did not keep data in Output file"
373 test "$(cat Output)" = "data" ||
374 atf_fail "#$T: Incorrect Output: Should be 'data' is '$(cat Output)'"
375
376 #5
377 echo data >Output 2>/dev/null || :
378 i
379 atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
380 'echo Hello 1<>Output'
381 test -s Output || atf_fail "#$T: Did not make non-empty Output file"
382 test "$(cat Output)" = "Hello" ||
383 atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'"
384
385 #6
386 printf data >Output 2>/dev/null || :
387 i
388 atf_check -s exit:0 -o inline:data -e empty ${TEST_SH} -c \
389 '{ cat >&3; printf file; } <>Output 3>&1 >&0'
390 test -f Output || atf_fail "#$T: Removed Output file"
391 test -s Output || atf_fail "#$T: Did not keep data in Output file"
392 test "$(cat Output)" = "datafile" ||
393 atf_fail \
394 "#$T: Incorrect Output: Should be 'datafile' is '$(cat Output)'"
395 }
396
397 atf_test_case fd_redirections
398 fd_redirections_head()
399 {
400 atf_set "descr" "Tests redirections to/from specific descriptors"
401 }
402 fd_redirections_body()
403 {
404 atf_require_prog /bin/echo
405
406 cat <<- 'DONE' > helper.sh
407 f() {
408 /bin/echo nothing "$1" >& "$1"
409 }
410 for n
411 do
412 eval "f $n $n"'> file-$n'
413 done
414 DONE
415 cat <<- 'DONE' > reread.sh
416 f() {
417 (read -r var; echo "${var}") <&"$1"
418 }
419 for n
420 do
421 x=$( eval "f $n $n"'< file-$n' )
422 test "${x}" = "nothing $n" || echo "$n"
423 done
424 DONE
425
426 validate()
427 {
428 for n
429 do
430 test -e "file-$n" || atf_fail "file-$n not created"
431 C=$(cat file-"$n")
432 test "$C" = "nothing $n" ||
433 atf_fail "file-$n contains '$C' not 'nothing $n'"
434 done
435 }
436
437 atf_check -s exit:0 -e empty -o empty \
438 ${TEST_SH} helper.sh 1 2 3 4 5 6 7 8 9
439 validate 1 2 3 4 5 6 7 8 9
440 atf_check -s exit:0 -e empty -o empty \
441 ${TEST_SH} reread.sh 3 4 5 6 7 8 9
442
443 L=$(ulimit -n)
444 if [ "$L" -ge 30 ]
445 then
446 atf_check -s exit:0 -e empty -o empty \
447 ${TEST_SH} helper.sh 10 15 19 20 25 29
448 validate 10 15 19 20 25 29
449 atf_check -s exit:0 -e empty -o empty \
450 ${TEST_SH} reread.sh 10 15 19 20 25 29
451 fi
452 if [ "$L" -ge 100 ]
453 then
454 atf_check -s exit:0 -e empty -o empty \
455 ${TEST_SH} helper.sh 32 33 49 50 51 63 64 65 77 88 99
456 validate 32 33 49 50 51 63 64 65 77 88 99
457 atf_check -s exit:0 -e empty -o empty \
458 ${TEST_SH} reread.sh 32 33 49 50 51 63 64 65 77 88 99
459 fi
460 if [ "$L" -ge 500 ]
461 then
462 atf_check -s exit:0 -e empty -o empty \
463 ${TEST_SH} helper.sh 100 101 199 200 222 333 444 499
464 validate 100 101 199 200 222 333 444 499
465 atf_check -s exit:0 -e empty -o empty \
466 ${TEST_SH} reread.sh 100 101 199 200 222 333 444 499
467 fi
468 if [ "$L" -gt 1005 ]
469 then
470 atf_check -s exit:0 -e empty -o empty \
471 ${TEST_SH} helper.sh 1000 1001 1002 1003 1004 1005
472 validate 1000 1001 1002 1003 1004 1005
473 atf_check -s exit:0 -e empty -o empty \
474 ${TEST_SH} reread.sh 1000 1001 1002 1003 1004 1005
475 fi
476 }
477
478 atf_test_case local_redirections
479 local_redirections_head()
480 {
481 atf_set "descr" \
482 "Tests that exec can reassign file descriptors in the shell itself"
483 }
484 local_redirections_body()
485 {
486 cat <<- 'DONE' > helper.sh
487 for f
488 do
489 eval "exec $f"'> file-$f'
490 done
491
492 for f
493 do
494 printf '%s\n' "Hello $f" >&"$f"
495 done
496
497 for f
498 do
499 eval "exec $f"'>&-'
500 done
501
502 for f
503 do
504 eval "exec $f"'< file-$f'
505 done
506
507 for f
508 do
509 exec <& "$f"
510 read -r var || echo >&2 "No data in file-$f"
511 read -r x && echo >&2 "Too much data in file-${f}: $x"
512 test "${var}" = "Hello $f" ||
513 echo >&2 "file-$f contains '${var}' not 'Hello $f'"
514 done
515 DONE
516
517 atf_check -s exit:0 -o empty -e empty \
518 ${TEST_SH} helper.sh 3 4 5 6 7 8 9
519
520 L=$(ulimit -n)
521 if [ "$L" -ge 30 ]
522 then
523 atf_check -s exit:0 -o empty -e empty \
524 ${TEST_SH} helper.sh 10 11 13 15 16 19 20 28 29
525 fi
526 if [ "$L" -ge 100 ]
527 then
528 atf_check -s exit:0 -o empty -e empty \
529 ${TEST_SH} helper.sh 30 31 32 63 64 65 77 88 99
530 fi
531 if [ "$L" -ge 500 ]
532 then
533 atf_check -s exit:0 -o empty -e empty \
534 ${TEST_SH} helper.sh 100 101 111 199 200 201 222 333 499
535 fi
536 if [ "$L" -ge 1005 ]
537 then
538 atf_check -s exit:0 -o empty -e empty \
539 ${TEST_SH} helper.sh 1000 1001 1002 1003 1004 1005
540 fi
541 }
542
543 atf_test_case named_fd_redirections
544 named_fd_redirections_head()
545 {
546 atf_set "descr" "Tests redirections to /dev/stdout (etc)"
547
548 }
549 named_fd_redirections_body()
550 {
551 if test -c /dev/stdout
552 then
553 atf_check -s exit:0 -o inline:'OK\n' -e empty \
554 ${TEST_SH} -c 'echo OK >/dev/stdout'
555 atf_check -s exit:0 -o inline:'OK\n' -e empty \
556 ${TEST_SH} -c '/bin/echo OK >/dev/stdout'
557 fi
558
559 if test -c /dev/stdin
560 then
561 atf_require_prog cat
562
563 echo GOOD | atf_check -s exit:0 -o inline:'GOOD\n' -e empty \
564 ${TEST_SH} -c 'read var </dev/stdin; echo $var' ||
565 atf_fail "/dev/stdin test 1"
566 echo GOOD | atf_check -s exit:0 -o inline:'GOOD\n' -e empty \
567 ${TEST_SH} -c 'cat </dev/stdin' ||
568 atf_fail "/dev/stdin test 2"
569 fi
570
571 if test -c /dev/stderr
572 then
573 atf_check -s exit:0 -e inline:'OK\n' -o empty \
574 ${TEST_SH} -c 'echo OK 2>/dev/stderr >&2'
575 atf_check -s exit:0 -e inline:'OK\n' -o empty \
576 ${TEST_SH} -c '/bin/echo OK 2>/dev/stderr >&2'
577 fi
578
579 if test -c /dev/fd/8 && test -c /dev/fd/9
580 then
581 atf_check -s exit:0 -o inline:'EIGHT\n' -e empty \
582 ${TEST_SH} -c 'printf "%s\n" EIGHT 8>&1 >/dev/fd/8 |
583 cat 9<&0 </dev/fd/9'
584 fi
585
586 return 0
587 }
588
589 atf_test_case redir_in_case
590 redir_in_case_head()
591 {
592 atf_set "descr" "Tests that sh(1) allows just redirections " \
593 "in case statements. (PR bin/48631)"
594 }
595 redir_in_case_body()
596 {
597 atf_check -s exit:0 -o empty -e empty \
598 ${TEST_SH} -c 'case x in (whatever) >foo;; esac'
599
600 atf_check -s exit:0 -o empty -e empty \
601 ${TEST_SH} -c 'case x in (whatever) >foo 2>&1;; esac'
602
603 atf_check -s exit:0 -o empty -e empty \
604 ${TEST_SH} -c 'case x in (whatever) >foo 2>&1 </dev/null;; esac'
605
606 atf_check -s exit:0 -o empty -e empty \
607 ${TEST_SH} -c 'case x in (whatever) >${somewhere};; esac'
608 }
609
610 atf_test_case incorrect_redirections
611 incorrect_redirections_head()
612 {
613 atf_set "descr" "Tests that sh(1) correctly ignores non-redirections"
614 }
615 incorrect_redirections_body() {
616
617 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'echo foo>'
618 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'read foo<'
619 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'echo foo<>'
620 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
621 'echo x > '"$nl"
622 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
623 'read x < '"$nl"
624 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
625 'echo x <> '"$nl"
626 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
627 'echo x >< anything'
628 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
629 'echo x >>< anything'
630 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
631 'echo x >|< anything'
632 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
633 'echo x > ; read x < /dev/null || echo bad'
634 atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
635 'read x < & echo y > /dev/null; wait && echo bad'
636
637 rm -f Output 2>/dev/null || :
638 atf_check -s exit:0 -e empty -o inline:'A Line > Output\n' \
639 ${TEST_SH} -c 'echo A Line \> Output'
640 test -f Output && atf_file "File 'Output' appeared and should not have"
641
642 rm -f Output 2>/dev/null || :
643 atf_check -s exit:0 -e empty -o empty \
644 ${TEST_SH} -c 'echo A Line \>> Output'
645 test -f Output || atf_file "File 'Output' not created when it should"
646 test "$(cat Output)" = 'A Line >' || atf_fail \
647 "Output file contains '$(cat Output)' instead of '"'A Line >'\'
648
649 rm -f Output \> 2>/dev/null || :
650 atf_check -s exit:0 -e empty -o empty \
651 ${TEST_SH} -c 'echo A Line >\> Output'
652 test -f Output && atf_file "File 'Output' appeared and should not have"
653 test -f '>' || atf_file "File '>' not created when it should"
654 test "$(cat '>')" = 'A Line Output' || atf_fail \
655 "Output file ('>') contains '$(cat '>')' instead of 'A Line Output'"
656
657 rm -fr OutDir
658 atf-check -s not-exit:0 -o empty -e not-empty \
659 ${TEST_SH} -c ': > OutDir/stdout; printf foo'
660 atf-check -s not-exit:0 -o empty -e not-empty \
661 ${TEST_SH} -c ': > OutDir/stdout || printf foo; printf bar'
662 atf-check -s exit:0 -o inline:bar -e not-empty \
663 ${TEST_SH} -c '> OutDir/stdout; printf bar'
664 atf-check -s exit:0 -o inline:foobar -e not-empty \
665 ${TEST_SH} -c '> OutDir/stdout || printf foo; printf bar'
666 atf-check -s exit:0 -o inline:bar -e not-empty \
667 ${TEST_SH} -c 'command : > OutDir/stdout; printf bar'
668 atf-check -s exit:0 -o inline:foobar -e not-empty ${TEST_SH} -c \
669 'command : > OutDir/stdout || printf foo; printf bar'
670 atf-check -s not-exit:0 -o empty -e not-empty \
671 ${TEST_SH} -c ': <> OutDir/stdout; printf foo'
672
673 atf-check -s not-exit:0 -o empty -e not-empty \
674 ${TEST_SH} -c ': >&8 ; printf foo'
675 atf-check -s not-exit:0 -o empty -e not-empty \
676 ${TEST_SH} -c ': >&8 || printf foo; printf bar'
677 atf-check -s exit:0 -o inline:bar -e not-empty \
678 ${TEST_SH} -c '>&8 ; printf bar'
679 atf-check -s exit:0 -o inline:foobar -e not-empty \
680 ${TEST_SH} -c '>&8 || printf foo; printf bar'
681 atf-check -s exit:0 -o inline:bar -e not-empty \
682 ${TEST_SH} -c 'command : >&7; printf bar'
683 atf-check -s exit:0 -o inline:foobar -e not-empty ${TEST_SH} -c \
684 'command : >&7 || printf foo; printf bar'
685
686 return 0
687 }
688
689 # Many more tests in t_here, so here we have just rudimentary checks
690 atf_test_case redir_here_doc
691 redir_here_doc_head()
692 {
693 atf_set "descr" "Tests that sh(1) correctly processes 'here' doc " \
694 "input redirections"
695 }
696 redir_here_doc_body()
697 {
698 # nb: the printf is not executed, it is data
699 cat <<- 'DONE' |
700 cat <<EOF
701 printf '%s\n' 'hello\n'
702 EOF
703 DONE
704 atf_check -s exit:0 -o match:printf -o match:'hello\\n' \
705 -e empty ${TEST_SH}
706 }
707
708 atf_test_case subshell_redirections
709 subshell_redirections_head()
710 {
711 atf_set "descr" "Tests redirection interactions between shell and " \
712 "its sub-shell(s)"
713 }
714 subshell_redirections_body()
715 {
716 atf_require_prog cat
717
718 LIM=$(ulimit -n)
719
720 cat <<- 'DONE' |
721 exec 6>output-file
722
723 ( printf "hello\n" >&6 )
724
725 exec 8<output-file
726
727 ( read hello <&8 ; test hello = "$hello" || echo >&2 Hello )
728
729 ( printf "bye-bye\n" >&6 )
730
731 ( exec 8<&- )
732 read bye <&8 || echo >&2 "Closed?"
733 echo Bye="$bye"
734 DONE
735 atf_check -s exit:0 -o match:Bye=bye-bye -e empty \
736 ${TEST_SH}
737
738 cat <<- 'DONE' |
739 for arg in one-4 two-24 three-14
740 do
741 fd=${arg#*-}
742 file=${arg%-*}
743 eval "exec ${fd}>${file}"
744 done
745
746 for arg in one-5 two-7 three-19
747 do
748 fd=${arg#*-}
749 file=${arg%-*}
750 eval "exec ${fd}<${file}"
751 done
752
753 (
754 echo line-1 >&4
755 echo line-2 >&24
756 echo line-3 >&14
757 echo go
758 ) | (
759 read go
760 read x <&5
761 read y <&7
762 read z <&19
763
764 printf "%s\n" "${x}" "${y}" "${z}"
765 )
766 DONE
767 atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \
768 -e empty ${TEST_SH}
769
770 cat <<- 'DONE' |
771 for arg in one-4-5 two-6-7 three-8-9 four-11-10 five-3-12
772 do
773 ofd=${arg##*-}
774 file=${arg%-*}
775 ifd=${file#*-}
776 file=${file%-*}
777 eval "exec ${ofd}>${file}"
778 eval "exec ${ifd}<${file}"
779 done
780
781 ( ( ( echo line-1 >& 13 ) 13>&12 ) 12>&5 ) >stdout 2>errout
782 ( ( ( echo line-2 >& 4) 13>&12 ) 4>&7 ) >>stdout 2>>errout
783 ( ( ( echo line-3 >& 6) 8>&1 6>&11 >&12) 11>&9 >&7 ) >>stdout
784
785 ( ( ( cat <&13 >&12 ) 13<&8 12>&10 ) 10>&1 8<&6 ) 6<&4
786 ( ( ( cat <&4 ) <&4 6<&8 8<&11 )
787 <&4 4<&6 6<&8 8<&11 ) <&4 4<&6 6<&8 8<&11 11<&3
788 ( ( ( cat <&7 >&1 ) 7<&6 >&10 ) 10>&2 6<&8 ) 2>&1
789 DONE
790 atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \
791 -e empty ${TEST_SH}
792 }
793
794 atf_test_case ulimit_redirection_interaction
795 ulimit_redirection_interaction_head()
796 {
797 atf_set "descr" "Tests interactions between redirect and ulimit -n "
798 }
799 ulimit_redirection_interaction_body()
800 {
801 atf_require_prog ls
802
803 cat <<- 'DONE' > helper.sh
804 oLIM=$(ulimit -n)
805 HRD=$(ulimit -H -n)
806 test "${oLIM}" -lt "${HRD}" && ulimit -n "${HRD}"
807 LIM=$(ulimit -n)
808
809 FDs=
810 LFD=-1
811 while [ ${LIM} -gt 16 ]
812 do
813 FD=$(( ${LIM} - 1 ))
814 if [ "${FD}" -eq "${LFD}" ]; then
815 echo >&2 "Infinite loop... (busted $(( )) ??)"
816 exit 1
817 fi
818 LFD="${FD}"
819
820 eval "exec ${FD}"'> /dev/null'
821 FDs="${FD}${FDs:+ }${FDs}"
822
823 (
824 FD=$(( ${LIM} + 1 ))
825 eval "exec ${FD}"'> /dev/null'
826 echo "Reached unreachable command"
827 ) 2>/dev/null && echo >&2 "Opened beyond limit!"
828
829 (eval 'ls 2>&1 3>&1 4>&1 5>&1 '"${FD}"'>&1') >&"${FD}"
830
831 LIM=$(( ${LIM} / 2 ))
832 ulimit -S -n "${LIM}"
833 done
834
835 # Even though ulimit has been reduced, open fds should work
836 for FD in ${FDs}
837 do
838 echo ${FD} in ${FDs} >&"${FD}" || exit 1
839 done
840
841 ulimit -S -n "${oLIM}"
842
843 # maybe more later...
844
845 DONE
846
847 atf_check -s exit:0 -o empty -e empty ${TEST_SH} helper.sh
848 }
849
850 atf_test_case validate_fn_redirects
851 validate_fn_redirects_head()
852 {
853 # These test cases inspired by PR bin/48875 and the sh
854 # changes that were required to fix it.
855
856 atf_set "descr" "Tests various redirections applied to functions " \
857 "See PR bin/48875"
858 }
859 validate_fn_redirects_body()
860 {
861 cat <<- 'DONE' > f-def
862 f() {
863 printf '%s\n' In-Func
864 }
865 DONE
866
867 atf_check -s exit:0 -o inline:'In-Func\nsuccess1\n' -e empty \
868 ${TEST_SH} -c ". ./f-def; f ; printf '%s\n' success1"
869 atf_check -s exit:0 -o inline:'success2\n' -e empty \
870 ${TEST_SH} -c ". ./f-def; f >/dev/null; printf '%s\n' success2"
871 atf_check -s exit:0 -o inline:'success3\n' -e not-empty \
872 ${TEST_SH} -c ". ./f-def; f >&- ; printf '%s\n' success3"
873 atf_check -s exit:0 -o inline:'In-Func\nsuccess4\n' -e empty \
874 ${TEST_SH} -c ". ./f-def; f & wait; printf '%s\n' success4"
875 atf_check -s exit:0 -o inline:'success5\n' -e not-empty \
876 ${TEST_SH} -c ". ./f-def; f >&- & wait; printf '%s\n' success5"
877 atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess6\n' -e empty \
878 ${TEST_SH} -c ". ./f-def; f;f; printf '%s\n' success6"
879 atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess7\n' -e empty \
880 ${TEST_SH} -c ". ./f-def; { f;f;}; printf '%s\n' success7"
881 atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess8\n' -e empty \
882 ${TEST_SH} -c ". ./f-def; { f;f;}& wait; printf '%s\n' success8"
883 atf_check -s exit:0 -o inline:'In-Func\nsuccess9\n' -e empty \
884 ${TEST_SH} -c \
885 ". ./f-def; { f>/dev/null;f;}& wait; printf '%s\n' success9"
886 atf_check -s exit:0 -o inline:'In-Func\nsuccess10\n' -e empty \
887 ${TEST_SH} -c \
888 ". ./f-def; { f;f>/dev/null;}& wait; printf '%s\n' success10"
889
890 # This one tests the issue etcupdate had with the original 48875 fix
891 atf_check -s exit:0 -o inline:'Func a\nFunc b\nFunc c\n' -e empty \
892 ${TEST_SH} -c '
893 f() {
894 echo Func "$1"
895 }
896 exec 3<&0 4>&1
897 ( echo x-a; echo y-b; echo z-c ) |
898 while read A
899 do
900 B=${A#?-}
901 f "$B" <&3 >&4
902 done >&2'
903
904 # And this tests a similar condition with that same fix
905 cat <<- 'DONE' >Script
906 f() {
907 printf '%s' " hello $1"
908 }
909 exec 3>&1
910 echo $( for i in a b c
911 do printf '%s' @$i; f $i >&3; done >foo
912 )
913 printf '%s\n' foo=$(cat foo)
914 DONE
915 atf_check -s exit:0 -e empty \
916 -o inline:' hello a hello b hello c\nfoo=@a@b@c\n' \
917 ${TEST_SH} Script
918
919 # Tests with sh reading stdin, which is not quite the same internal
920 # mechanism.
921 echo ". ./f-def || echo >&2 FAIL
922 f
923 printf '%s\n' stdin1
924 " | atf_check -s exit:0 -o inline:'In-Func\nstdin1\n' -e empty \
925 ${TEST_SH} ||
926 atf_fail "stdin1 test failure"
927
928 echo '
929 . ./f-def || echo >&2 FAIL
930 f >&- 2>/dev/null
931 printf "%s\n" stdin2
932 ' | atf_check -s exit:0 -o inline:'stdin2\n' -e empty ${TEST_SH} ||
933 atf_fail "stdin2 test failure"
934
935 cat <<- 'DONE' > fgh.def
936 f() {
937 echo -n f >&3
938 sleep 4
939 echo -n F >&3
940 }
941 g() {
942 echo -n g >&3
943 sleep 2
944 echo -n G >&3
945 }
946 h() {
947 echo -n h >&3
948 }
949 DONE
950
951 atf_check -s exit:0 -o inline:'fFgGh' -e empty \
952 ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
953 exec 3>&1
954 f; g; h'
955
956 atf_check -s exit:0 -o inline:'fghGF' -e empty \
957 ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
958 exec 3>&1
959 f & sleep 1; g & sleep 1; h; wait'
960
961 atf_check -s exit:0 -o inline:'fFgGhX Y\n' -e empty \
962 ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
963 exec 3>&1
964 echo X $( f ; g ; h ) Y'
965
966 # This one is the real test for PR bin/48875. If the
967 # cmdsub does not complete before f g (and h) exit,
968 # then the 'F' & 'G' will precede 'X Y' in the output.
969 # If the cmdsub finishes while f & g are still running,
970 # then the X Y will appear before the F and G.
971 # The trailing "sleep 3" is just so we catch all the
972 # output (otherwise atf_check will be finished while
973 # f & g are still sleeping).
974
975 atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty \
976 ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
977 exec 3>&1
978 echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y
979 sleep 3
980 exec 4>&1 || echo FD_FAIL
981 '
982
983 # Do the test again to verify it also all works reading stdin
984 # (which is a slightly different path through the shell)
985 echo '
986 . ./fgh.def || echo >&2 FAIL
987 exec 3>&1
988 echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y
989 sleep 3
990 exec 4>&1 || echo FD_FAIL
991 ' | atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty ${TEST_SH} ||
992 atf_fail "48875 stdin variant failure"
993 }
994
995 atf_init_test_cases() {
996 atf_add_test_case basic_test_method_test
997 atf_add_test_case do_input_redirections
998 atf_add_test_case do_output_redirections
999 atf_add_test_case do_redirect_input_output
1000 atf_add_test_case fd_redirections
1001 atf_add_test_case local_redirections
1002 atf_add_test_case incorrect_redirections
1003 atf_add_test_case named_fd_redirections
1004 atf_add_test_case redir_here_doc
1005 atf_add_test_case redir_in_case
1006 atf_add_test_case subshell_redirections
1007 atf_add_test_case ulimit_redirection_interaction
1008 atf_add_test_case validate_fn_redirects
1009 }
1010