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