xref: /freebsd/contrib/netbsd-tests/bin/sh/t_redir.sh (revision 02e9120893770924227138ba49df1edb3896112a)
1# $NetBSD: t_redir.sh,v 1.9 2016/05/14 00:33:02 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
36atf_test_case basic_test_method_test
37basic_test_method_test_head()
38{
39	atf_set "descr" "Tests that test method works as expected"
40}
41basic_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
89atf_test_case do_input_redirections
90do_input_redirections_head()
91{
92	atf_set "descr" "Tests that simple input redirection works"
93}
94do_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
229atf_test_case do_output_redirections
230do_output_redirections_head()
231{
232	atf_set "descr" "Test Output redirections"
233}
234do_output_redirections_body()
235{
236nl='
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
329atf_test_case fd_redirections
330fd_redirections_head()
331{
332	atf_set "descr" "Tests redirections to/from specific descriptors"
333}
334fd_redirections_body()
335{
336	atf_require_prog /bin/echo
337
338	cat <<- 'DONE' > helper.sh
339		f() {
340			/bin/echo nothing "$1" >& "$1"
341		}
342		for n
343		do
344			eval "f $n $n"'> file-$n'
345		done
346	DONE
347	cat <<- 'DONE' > reread.sh
348		f() {
349			(read -r var; echo "${var}") <&"$1"
350		}
351		for n
352		do
353			x=$( eval "f $n $n"'< file-$n' )
354			test "${x}" = "nothing $n" || echo "$n"
355		done
356	DONE
357
358	validate()
359	{
360	    for n
361	    do
362		test -e "file-$n" || atf_fail "file-$n not created"
363		C=$(cat file-"$n")
364		test "$C" = "nothing $n" ||
365			atf_fail "file-$n contains '$C' not 'nothing $n'"
366	    done
367	}
368
369	atf_check -s exit:0 -e empty -o empty \
370		${TEST_SH} helper.sh 1 2 3 4 5 6 7 8 9
371	validate 1 2 3 4 5 6 7 8 9
372	atf_check -s exit:0 -e empty -o empty \
373		${TEST_SH} reread.sh 3 4 5 6 7 8 9
374
375	L=$(ulimit -n)
376	if [ "$L" -ge 30 ]
377	then
378		atf_check -s exit:0 -e empty -o empty \
379			${TEST_SH} helper.sh 10 15 19 20 25 29
380		validate 10 15 19 20 25 29
381		atf_check -s exit:0 -e empty -o empty \
382			${TEST_SH} reread.sh 10 15 19 20 25 29
383	fi
384	if [ "$L" -ge 100 ]
385	then
386		atf_check -s exit:0 -e empty -o empty \
387			${TEST_SH} helper.sh 32 33 49 50 51 63 64 65 77 88 99
388		validate 32 33 49 50 51 63 64 65 77 88 99
389		atf_check -s exit:0 -e empty -o empty \
390			${TEST_SH} reread.sh 32 33 49 50 51 63 64 65 77 88 99
391	fi
392	if [ "$L" -ge 500 ]
393	then
394		atf_check -s exit:0 -e empty -o empty \
395			${TEST_SH} helper.sh 100 101 199 200 222 333 444 499
396		validate 100 101 199 200 222 333 444 499
397		atf_check -s exit:0 -e empty -o empty \
398			${TEST_SH} reread.sh 100 101 199 200 222 333 444 499
399	fi
400	if [ "$L" -gt 1005 ]
401	then
402		atf_check -s exit:0 -e empty -o empty \
403			${TEST_SH} helper.sh 1000 1001 1002 1003 1004 1005
404		validate 1000 1001 1002 1003 1004 1005
405		atf_check -s exit:0 -e empty -o empty \
406			${TEST_SH} reread.sh 1000 1001 1002 1003 1004 1005
407	fi
408}
409
410atf_test_case local_redirections
411local_redirections_head()
412{
413	atf_set "descr" \
414	    "Tests that exec can reassign file descriptors in the shell itself"
415}
416local_redirections_body()
417{
418	cat <<- 'DONE' > helper.sh
419		for f
420		do
421			eval "exec $f"'> file-$f'
422		done
423
424		for f
425		do
426			printf '%s\n' "Hello $f" >&"$f"
427		done
428
429		for f
430		do
431			eval "exec $f"'>&-'
432		done
433
434		for f
435		do
436			eval "exec $f"'< file-$f'
437		done
438
439		for f
440		do
441			exec <& "$f"
442			read -r var || echo >&2 "No data in file-$f"
443			read -r x && echo >&2 "Too much data in file-${f}: $x"
444			test "${var}" = "Hello $f" ||
445			    echo >&2 "file-$f contains '${var}' not 'Hello $f'"
446		done
447	DONE
448
449	atf_check -s exit:0 -o empty -e empty \
450		${TEST_SH} helper.sh 3 4 5 6 7 8 9
451
452	L=$(ulimit -n)
453	if [ "$L" -ge 30 ]
454	then
455		atf_check -s exit:0 -o empty -e empty \
456			${TEST_SH} helper.sh 10 11 13 15 16 19 20 28 29
457	fi
458	if [ "$L" -ge 100 ]
459	then
460		atf_check -s exit:0 -o empty -e empty \
461			${TEST_SH} helper.sh 30 31 32 63 64 65 77 88 99
462	fi
463	if [ "$L" -ge 500 ]
464	then
465		atf_check -s exit:0 -o empty -e empty \
466			${TEST_SH} helper.sh 100 101 111 199 200 201 222 333 499
467	fi
468	if [ "$L" -ge 1005 ]
469	then
470		atf_check -s exit:0 -o empty -e empty \
471			${TEST_SH} helper.sh 1000 1001 1002 1003 1004 1005
472	fi
473}
474
475atf_test_case named_fd_redirections
476named_fd_redirections_head()
477{
478	atf_set "descr" "Tests redirections to /dev/stdout (etc)"
479
480}
481named_fd_redirections_body()
482{
483	if test -c /dev/stdout
484	then
485		atf_check -s exit:0 -o inline:'OK\n' -e empty \
486			${TEST_SH} -c 'echo OK >/dev/stdout'
487		atf_check -s exit:0 -o inline:'OK\n' -e empty \
488			${TEST_SH} -c '/bin/echo OK >/dev/stdout'
489	fi
490
491	if test -c /dev/stdin
492	then
493		atf_require_prog cat
494
495		echo GOOD | atf_check -s exit:0 -o inline:'GOOD\n' -e empty \
496			${TEST_SH} -c 'read var </dev/stdin; echo $var'
497		echo GOOD | atf_check -s exit:0 -o inline:'GOOD\n' -e empty \
498			${TEST_SH} -c 'cat </dev/stdin'
499	fi
500
501	if test -c /dev/stderr
502	then
503		atf_check -s exit:0 -e inline:'OK\n' -o empty \
504			${TEST_SH} -c 'echo OK 2>/dev/stderr >&2'
505		atf_check -s exit:0 -e inline:'OK\n' -o empty \
506			${TEST_SH} -c '/bin/echo OK 2>/dev/stderr >&2'
507	fi
508
509	if test -c /dev/fd/8 && test -c /dev/fd/9
510	then
511		atf_check -s exit:0 -o inline:'EIGHT\n' -e empty \
512			${TEST_SH} -c 'printf "%s\n" EIGHT 8>&1 >/dev/fd/8 |
513					cat 9<&0 </dev/fd/9'
514	fi
515
516	return 0
517}
518
519atf_test_case redir_in_case
520redir_in_case_head()
521{
522	atf_set "descr" "Tests that sh(1) allows just redirections " \
523	                "in case statements. (PR bin/48631)"
524}
525redir_in_case_body()
526{
527	atf_check -s exit:0 -o empty -e empty \
528	    ${TEST_SH} -c 'case x in (whatever) >foo;; esac'
529
530	atf_check -s exit:0 -o empty -e empty \
531	    ${TEST_SH} -c 'case x in (whatever) >foo 2>&1;; esac'
532
533	atf_check -s exit:0 -o empty -e empty \
534	    ${TEST_SH} -c 'case x in (whatever) >foo 2>&1 </dev/null;; esac'
535
536	atf_check -s exit:0 -o empty -e empty \
537	    ${TEST_SH} -c 'case x in (whatever) >${somewhere};; esac'
538}
539
540atf_test_case incorrect_redirections
541incorrect_redirections_head()
542{
543	atf_set "descr" "Tests that sh(1) correctly ignores non-redirections"
544}
545incorrect_redirections_body() {
546
547	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'echo foo>'
548	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'read foo<'
549	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'echo foo<>'
550	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
551		'echo x > '"$nl"
552	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
553		'read x < '"$nl"
554	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
555		'echo x <> '"$nl"
556	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
557		'echo x >< anything'
558	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
559		'echo x >>< anything'
560	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
561		'echo x >|< anything'
562	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
563		'echo x > ; read x < /dev/null || echo bad'
564	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
565		'read x < & echo y > /dev/null; wait && echo bad'
566
567	rm -f Output 2>/dev/null || :
568	atf_check -s exit:0 -e empty -o inline:'A Line > Output\n' \
569		${TEST_SH} -c 'echo A Line \> Output'
570	test -f Output && atf_file "File 'Output' appeared and should not have"
571
572	rm -f Output 2>/dev/null || :
573	atf_check -s exit:0 -e empty -o empty \
574		${TEST_SH} -c 'echo A Line \>> Output'
575	test -f Output || atf_file "File 'Output' not created when it should"
576	test "$(cat Output)" = 'A Line >' || atf_fail \
577		"Output file contains '$(cat Output)' instead of '"'A Line >'\'
578
579	rm -f Output \> 2>/dev/null || :
580	atf_check -s exit:0 -e empty -o empty \
581		${TEST_SH} -c 'echo A Line >\> Output'
582	test -f Output && atf_file "File 'Output' appeared and should not have"
583	test -f '>' || atf_file "File '>' not created when it should"
584	test "$(cat '>')" = 'A Line Output' || atf_fail \
585	    "Output file ('>') contains '$(cat '>')' instead of 'A Line Output'"
586}
587
588# Many more tests in t_here, so here we have just rudimentary checks
589atf_test_case redir_here_doc
590redir_here_doc_head()
591{
592	atf_set "descr" "Tests that sh(1) correctly processes 'here' doc " \
593	                "input redirections"
594}
595redir_here_doc_body()
596{
597	# nb: the printf is not executed, it is data
598	cat <<- 'DONE' |
599		cat <<EOF
600			printf '%s\n' 'hello\n'
601		EOF
602	DONE
603	atf_check -s exit:0 -o match:printf -o match:'hello\\n' \
604		-e empty ${TEST_SH}
605}
606
607atf_test_case subshell_redirections
608subshell_redirections_head()
609{
610	atf_set "descr" "Tests redirection interactions between shell and " \
611			"its sub-shell(s)"
612}
613subshell_redirections_body()
614{
615	atf_require_prog cat
616
617	LIM=$(ulimit -n)
618
619	cat <<- 'DONE' |
620		exec 6>output-file
621
622		( printf "hello\n" >&6 )
623
624		exec 8<output-file
625
626		( read hello <&8 ; test hello = "$hello" || echo >&2 Hello )
627
628		( printf "bye-bye\n" >&6 )
629
630		( exec 8<&- )
631		read bye <&8 || echo >&2 "Closed?"
632		echo Bye="$bye"
633	DONE
634	atf_check -s exit:0 -o match:Bye=bye-bye -e empty \
635		${TEST_SH}
636
637	cat <<- 'DONE' |
638		for arg in one-4 two-24 three-14
639		do
640			fd=${arg#*-}
641			file=${arg%-*}
642			eval "exec ${fd}>${file}"
643		done
644
645		for arg in one-5 two-7 three-19
646		do
647			fd=${arg#*-}
648			file=${arg%-*}
649			eval "exec ${fd}<${file}"
650		done
651
652		(
653			echo line-1 >&4
654			echo line-2 >&24
655			echo line-3 >&14
656			echo go
657		) | (
658			read go
659			read x <&5
660			read y <&7
661			read z <&19
662
663			printf "%s\n" "${x}" "${y}" "${z}"
664		)
665	DONE
666	atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \
667		-e empty ${TEST_SH}
668
669	cat <<- 'DONE' |
670		for arg in one-4-5 two-6-7 three-8-9 four-11-10 five-3-12
671		do
672			ofd=${arg##*-}
673			file=${arg%-*}
674			ifd=${file#*-}
675			file=${file%-*}
676			eval "exec ${ofd}>${file}"
677			eval "exec ${ifd}<${file}"
678		done
679
680		( ( ( echo line-1 >& 13 ) 13>&12 ) 12>&5 ) >stdout 2>errout
681		( ( ( echo line-2 >& 4) 13>&12 ) 4>&7 ) >>stdout 2>>errout
682		( ( ( echo line-3 >& 6) 8>&1 6>&11 >&12) 11>&9 >&7 ) >>stdout
683
684		( ( ( cat <&13 >&12 ) 13<&8 12>&10 ) 10>&1 8<&6 ) 6<&4
685		( ( ( cat <&4 ) <&4 6<&8 8<&11  )
686			<&4 4<&6 6<&8 8<&11 ) <&4 4<&6 6<&8 8<&11 11<&3
687		( ( ( cat <&7 >&1 ) 7<&6 >&10 ) 10>&2 6<&8 ) 2>&1
688	DONE
689	atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \
690		-e empty ${TEST_SH}
691}
692
693atf_test_case ulimit_redirection_interaction
694ulimit_redirection_interaction_head()
695{
696	atf_set "descr" "Tests interactions between redirect and ulimit -n "
697}
698ulimit_redirection_interaction_body()
699{
700	atf_require_prog ls
701
702	cat <<- 'DONE' > helper.sh
703		oLIM=$(ulimit -n)
704		HRD=$(ulimit -H -n)
705		test "${oLIM}" -lt "${HRD}"  && ulimit -n "${HRD}"
706		LIM=$(ulimit -n)
707
708		FDs=
709		LFD=-1
710		while [ ${LIM} -gt 16 ]
711		do
712			FD=$(( ${LIM} - 1 ))
713			if [ "${FD}" -eq "${LFD}" ]; then
714				echo >&2 "Infinite loop... (busted $(( )) ??)"
715				exit 1
716			fi
717			LFD="${FD}"
718
719			eval "exec ${FD}"'> /dev/null'
720			FDs="${FD}${FDs:+ }${FDs}"
721
722			(
723				FD=$(( ${LIM} + 1 ))
724				eval "exec ${FD}"'> /dev/null'
725				echo "Reached unreachable command"
726			) 2>/dev/null && echo >&2 "Opened beyond limit!"
727
728			(eval 'ls 2>&1 3>&1 4>&1 5>&1 '"${FD}"'>&1') >&"${FD}"
729
730			LIM=$(( ${LIM} / 2 ))
731			ulimit -S -n "${LIM}"
732		done
733
734		# Even though ulimit has been reduced, open fds should work
735		for FD in ${FDs}
736		do
737			echo ${FD} in ${FDs} >&"${FD}" || exit 1
738		done
739
740		ulimit -S -n "${oLIM}"
741
742		# maybe more later...
743
744	DONE
745
746	atf_check -s exit:0 -o empty -e empty ${TEST_SH} helper.sh
747}
748
749atf_test_case validate_fn_redirects
750validate_fn_redirects_head()
751{
752	# These test cases inspired by PR bin/48875 and the sh
753	# changes that were required to fix it.
754
755	atf_set "descr" "Tests various redirections applied to functions " \
756		"See PR bin/48875"
757}
758validate_fn_redirects_body()
759{
760	cat <<- 'DONE' > f-def
761		f() {
762			printf '%s\n' In-Func
763		}
764	DONE
765
766	atf_check -s exit:0 -o inline:'In-Func\nsuccess1\n' -e empty \
767		${TEST_SH} -c ". ./f-def; f ; printf '%s\n' success1"
768	atf_check -s exit:0 -o inline:'success2\n' -e empty \
769		${TEST_SH} -c ". ./f-def; f >/dev/null; printf '%s\n' success2"
770	atf_check -s exit:0 -o inline:'success3\n' -e empty \
771		${TEST_SH} -c ". ./f-def; f >&- ; printf '%s\n' success3"
772	atf_check -s exit:0 -o inline:'In-Func\nsuccess4\n' -e empty \
773		${TEST_SH} -c ". ./f-def; f & wait; printf '%s\n' success4"
774	atf_check -s exit:0 -o inline:'success5\n' -e empty \
775		${TEST_SH} -c ". ./f-def; f >&- & wait; printf '%s\n' success5"
776	atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess6\n' -e empty \
777		${TEST_SH} -c ". ./f-def; f;f; printf '%s\n' success6"
778	atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess7\n' -e empty \
779		${TEST_SH} -c ". ./f-def; { f;f;}; printf '%s\n' success7"
780	atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess8\n' -e empty \
781		${TEST_SH} -c ". ./f-def; { f;f;}& wait; printf '%s\n' success8"
782	atf_check -s exit:0 -o inline:'In-Func\nsuccess9\n' -e empty \
783		${TEST_SH} -c \
784		   ". ./f-def; { f>/dev/null;f;}& wait; printf '%s\n' success9"
785	atf_check -s exit:0 -o inline:'In-Func\nsuccess10\n' -e empty \
786		${TEST_SH} -c \
787		   ". ./f-def; { f;f>/dev/null;}& wait; printf '%s\n' success10"
788
789	# This one tests the issue etcupdate had with the original 48875 fix
790	atf_check -s exit:0 -o inline:'Func a\nFunc b\nFunc c\n' -e empty \
791		${TEST_SH} -c '
792			f() {
793				echo Func "$1"
794			}
795			exec 3<&0 4>&1
796			( echo x-a; echo y-b; echo z-c ) |
797			while read A
798			do
799				B=${A#?-}
800				f "$B" <&3 >&4
801			done >&2'
802
803	# And this tests a similar condition with that same fix
804	cat  <<- 'DONE' >Script
805		f() {
806			printf '%s' " hello $1"
807		}
808		exec 3>&1
809		echo $( for i in a b c
810			do printf '%s' @$i; f $i >&3; done >foo
811		)
812		printf '%s\n' foo=$(cat foo)
813	DONE
814	atf_check -s exit:0 -e empty \
815	    -o inline:' hello a hello b hello c\nfoo=@a@b@c\n' \
816	    ${TEST_SH} Script
817
818	# Tests with sh reading stdin, which is not quite the same internal
819	# mechanism.
820	echo ". ./f-def || echo >&2 FAIL
821		f
822		printf '%s\n' stdin1
823	"| atf_check -s exit:0 -o inline:'In-Func\nstdin1\n' -e empty ${TEST_SH}
824
825	echo '
826		. ./f-def || echo >&2 FAIL
827		f >&-
828		printf "%s\n" stdin2
829	' | atf_check -s exit:0 -o inline:'stdin2\n' -e empty ${TEST_SH}
830
831	cat <<- 'DONE' > fgh.def
832		f() {
833			echo -n f >&3
834			sleep 4
835			echo -n F >&3
836		}
837		g() {
838			echo -n g >&3
839			sleep 2
840			echo -n G >&3
841		}
842		h() {
843			echo -n h >&3
844		}
845	DONE
846
847	atf_check -s exit:0 -o inline:'fFgGh' -e empty \
848		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
849			exec 3>&1
850			f; g; h'
851
852	atf_check -s exit:0 -o inline:'fghGF' -e empty \
853		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
854			exec 3>&1
855			f & sleep 1; g & sleep 1; h; wait'
856
857	atf_check -s exit:0 -o inline:'fFgGhX Y\n' -e empty \
858		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
859			exec 3>&1
860			echo X $( f ; g ; h ) Y'
861
862	# This one is the real test for PR bin/48875.  If the
863	# cmdsub does not complete before f g (and h) exit,
864	# then the 'F' & 'G' will precede 'X Y' in the output.
865	# If the cmdsub finishes while f & g are still running,
866	# then the X Y will appear before the F and G.
867	# The trailing "sleep 3" is just so we catch all the
868	# output (otherwise atf_check will be finished while
869	# f & g are still sleeping).
870
871	atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty \
872		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
873			exec 3>&1
874			echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y
875			sleep 3
876			exec 4>&1 || echo FD_FAIL
877			'
878
879	# Do the test again to verify it also all works reading stdin
880	# (which is a slightly different path through the shell)
881	echo '
882		. ./fgh.def || echo >&2 FAIL
883		exec 3>&1
884		echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y
885		sleep 3
886		exec 4>&1 || echo FD_FAIL
887	' | atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty ${TEST_SH}
888}
889
890atf_init_test_cases() {
891	atf_add_test_case basic_test_method_test
892	atf_add_test_case do_input_redirections
893	atf_add_test_case do_output_redirections
894	atf_add_test_case fd_redirections
895	atf_add_test_case local_redirections
896	atf_add_test_case incorrect_redirections
897	atf_add_test_case named_fd_redirections
898	atf_add_test_case redir_here_doc
899	atf_add_test_case redir_in_case
900	atf_add_test_case subshell_redirections
901	atf_add_test_case ulimit_redirection_interaction
902	atf_add_test_case validate_fn_redirects
903}
904