Home | History | Annotate | Line # | Download | only in sh
t_redir.sh revision 1.11
      1 # $NetBSD: t_redir.sh,v 1.11 2021/05/19 22:43:18 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 		echo GOOD | atf_check -s exit:0 -o inline:'GOOD\n' -e empty \
    566 			${TEST_SH} -c 'cat </dev/stdin'
    567 	fi
    568 
    569 	if test -c /dev/stderr
    570 	then
    571 		atf_check -s exit:0 -e inline:'OK\n' -o empty \
    572 			${TEST_SH} -c 'echo OK 2>/dev/stderr >&2'
    573 		atf_check -s exit:0 -e inline:'OK\n' -o empty \
    574 			${TEST_SH} -c '/bin/echo OK 2>/dev/stderr >&2'
    575 	fi
    576 
    577 	if test -c /dev/fd/8 && test -c /dev/fd/9
    578 	then
    579 		atf_check -s exit:0 -o inline:'EIGHT\n' -e empty \
    580 			${TEST_SH} -c 'printf "%s\n" EIGHT 8>&1 >/dev/fd/8 |
    581 					cat 9<&0 </dev/fd/9'
    582 	fi
    583 
    584 	return 0
    585 }
    586 
    587 atf_test_case redir_in_case
    588 redir_in_case_head()
    589 {
    590 	atf_set "descr" "Tests that sh(1) allows just redirections " \
    591 	                "in case statements. (PR bin/48631)"
    592 }
    593 redir_in_case_body()
    594 {
    595 	atf_check -s exit:0 -o empty -e empty \
    596 	    ${TEST_SH} -c 'case x in (whatever) >foo;; esac'
    597 
    598 	atf_check -s exit:0 -o empty -e empty \
    599 	    ${TEST_SH} -c 'case x in (whatever) >foo 2>&1;; esac'
    600 
    601 	atf_check -s exit:0 -o empty -e empty \
    602 	    ${TEST_SH} -c 'case x in (whatever) >foo 2>&1 </dev/null;; esac'
    603 
    604 	atf_check -s exit:0 -o empty -e empty \
    605 	    ${TEST_SH} -c 'case x in (whatever) >${somewhere};; esac'
    606 }
    607 
    608 atf_test_case incorrect_redirections
    609 incorrect_redirections_head()
    610 {
    611 	atf_set "descr" "Tests that sh(1) correctly ignores non-redirections"
    612 }
    613 incorrect_redirections_body() {
    614 
    615 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'echo foo>'
    616 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'read foo<'
    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 \
    619 		'echo x > '"$nl"
    620 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    621 		'read x < '"$nl"
    622 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    623 		'echo x <> '"$nl"
    624 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    625 		'echo x >< anything'
    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 > ; read x < /dev/null || echo bad'
    632 	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
    633 		'read x < & echo y > /dev/null; wait && echo bad'
    634 
    635 	rm -f Output 2>/dev/null || :
    636 	atf_check -s exit:0 -e empty -o inline:'A Line > Output\n' \
    637 		${TEST_SH} -c 'echo A Line \> Output'
    638 	test -f Output && atf_file "File 'Output' appeared and should not have"
    639 
    640 	rm -f Output 2>/dev/null || :
    641 	atf_check -s exit:0 -e empty -o empty \
    642 		${TEST_SH} -c 'echo A Line \>> Output'
    643 	test -f Output || atf_file "File 'Output' not created when it should"
    644 	test "$(cat Output)" = 'A Line >' || atf_fail \
    645 		"Output file contains '$(cat Output)' instead of '"'A Line >'\'
    646 
    647 	rm -f Output \> 2>/dev/null || :
    648 	atf_check -s exit:0 -e empty -o empty \
    649 		${TEST_SH} -c 'echo A Line >\> Output'
    650 	test -f Output && atf_file "File 'Output' appeared and should not have"
    651 	test -f '>' || atf_file "File '>' not created when it should"
    652 	test "$(cat '>')" = 'A Line Output' || atf_fail \
    653 	    "Output file ('>') contains '$(cat '>')' instead of 'A Line Output'"
    654 
    655 	rm -fr OutDir
    656 	atf-check -s not-exit:0 -o empty -e not-empty \
    657 		${TEST_SH} -c ': > OutDir/stdout; printf foo'
    658 	atf-check -s not-exit:0 -o empty -e not-empty \
    659 		${TEST_SH} -c ': > OutDir/stdout || printf foo; printf bar'
    660 	atf-check -s exit:0 -o inline:bar -e not-empty \
    661 		${TEST_SH} -c '> OutDir/stdout; printf bar'
    662 	atf-check -s exit:0 -o inline:foobar -e not-empty \
    663 		${TEST_SH} -c '> OutDir/stdout || printf foo; printf bar'
    664 	atf-check -s exit:0 -o inline:bar -e not-empty \
    665 		${TEST_SH} -c 'command : > OutDir/stdout; printf bar'
    666 	atf-check -s exit:0 -o inline:foobar -e not-empty ${TEST_SH} -c \
    667 		'command : > OutDir/stdout || printf foo; printf bar'
    668 	atf-check -s not-exit:0 -o empty -e not-empty \
    669 		${TEST_SH} -c ': <> OutDir/stdout; printf foo'
    670 
    671 	atf-check -s not-exit:0 -o empty -e not-empty \
    672 		${TEST_SH} -c ': >&8 ; printf foo'
    673 	atf-check -s not-exit:0 -o empty -e not-empty \
    674 		${TEST_SH} -c ': >&8 || printf foo; printf bar'
    675 	atf-check -s exit:0 -o inline:bar -e not-empty \
    676 		${TEST_SH} -c '>&8 ; printf bar'
    677 	atf-check -s exit:0 -o inline:foobar -e not-empty \
    678 		${TEST_SH} -c '>&8 || printf foo; printf bar'
    679 	atf-check -s exit:0 -o inline:bar -e not-empty \
    680 		${TEST_SH} -c 'command : >&7; printf bar'
    681 	atf-check -s exit:0 -o inline:foobar -e not-empty ${TEST_SH} -c \
    682 		'command : >&7 || printf foo; printf bar'
    683 
    684 	return 0
    685 }
    686 
    687 # Many more tests in t_here, so here we have just rudimentary checks
    688 atf_test_case redir_here_doc
    689 redir_here_doc_head()
    690 {
    691 	atf_set "descr" "Tests that sh(1) correctly processes 'here' doc " \
    692 	                "input redirections"
    693 }
    694 redir_here_doc_body()
    695 {
    696 	# nb: the printf is not executed, it is data
    697 	cat <<- 'DONE' |
    698 		cat <<EOF
    699 			printf '%s\n' 'hello\n'
    700 		EOF
    701 	DONE
    702 	atf_check -s exit:0 -o match:printf -o match:'hello\\n' \
    703 		-e empty ${TEST_SH} 
    704 }
    705 
    706 atf_test_case subshell_redirections
    707 subshell_redirections_head()
    708 {
    709 	atf_set "descr" "Tests redirection interactions between shell and " \
    710 			"its sub-shell(s)"
    711 }
    712 subshell_redirections_body()
    713 {
    714 	atf_require_prog cat
    715 
    716 	LIM=$(ulimit -n)
    717 
    718 	cat <<- 'DONE' |
    719 		exec 6>output-file
    720 
    721 		( printf "hello\n" >&6 )
    722 
    723 		exec 8<output-file
    724 
    725 		( read hello <&8 ; test hello = "$hello" || echo >&2 Hello )
    726 
    727 		( printf "bye-bye\n" >&6 )
    728 
    729 		( exec 8<&- )
    730 		read bye <&8 || echo >&2 "Closed?"
    731 		echo Bye="$bye"
    732 	DONE
    733 	atf_check -s exit:0 -o match:Bye=bye-bye -e empty \
    734 		${TEST_SH}
    735 
    736 	cat <<- 'DONE' |
    737 		for arg in one-4 two-24 three-14
    738 		do
    739 			fd=${arg#*-}
    740 			file=${arg%-*}
    741 			eval "exec ${fd}>${file}"
    742 		done
    743 
    744 		for arg in one-5 two-7 three-19
    745 		do
    746 			fd=${arg#*-}
    747 			file=${arg%-*}
    748 			eval "exec ${fd}<${file}"
    749 		done
    750 
    751 		(
    752 			echo line-1 >&4
    753 			echo line-2 >&24
    754 			echo line-3 >&14
    755 			echo go
    756 		) | (
    757 			read go
    758 			read x <&5
    759 			read y <&7
    760 			read z <&19
    761 
    762 			printf "%s\n" "${x}" "${y}" "${z}"
    763 		)
    764 	DONE
    765 	atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \
    766 		-e empty ${TEST_SH}
    767 
    768 	cat <<- 'DONE' |
    769 		for arg in one-4-5 two-6-7 three-8-9 four-11-10 five-3-12
    770 		do
    771 			ofd=${arg##*-}
    772 			file=${arg%-*}
    773 			ifd=${file#*-}
    774 			file=${file%-*}
    775 			eval "exec ${ofd}>${file}"
    776 			eval "exec ${ifd}<${file}"
    777 		done
    778 
    779 		( ( ( echo line-1 >& 13 ) 13>&12 ) 12>&5 ) >stdout 2>errout
    780 		( ( ( echo line-2 >& 4) 13>&12 ) 4>&7 ) >>stdout 2>>errout
    781 		( ( ( echo line-3 >& 6) 8>&1 6>&11 >&12) 11>&9 >&7 ) >>stdout
    782 
    783 		( ( ( cat <&13 >&12 ) 13<&8 12>&10 ) 10>&1 8<&6 ) 6<&4
    784 		( ( ( cat <&4 ) <&4 6<&8 8<&11  )
    785 			<&4 4<&6 6<&8 8<&11 ) <&4 4<&6 6<&8 8<&11 11<&3
    786 		( ( ( cat <&7 >&1 ) 7<&6 >&10 ) 10>&2 6<&8 ) 2>&1
    787 	DONE
    788 	atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \
    789 		-e empty ${TEST_SH}
    790 }
    791 
    792 atf_test_case ulimit_redirection_interaction
    793 ulimit_redirection_interaction_head()
    794 {
    795 	atf_set "descr" "Tests interactions between redirect and ulimit -n "
    796 }
    797 ulimit_redirection_interaction_body()
    798 {
    799 	atf_require_prog ls
    800 
    801 	cat <<- 'DONE' > helper.sh
    802 		oLIM=$(ulimit -n)
    803 		HRD=$(ulimit -H -n)
    804 		test "${oLIM}" -lt "${HRD}"  && ulimit -n "${HRD}"
    805 		LIM=$(ulimit -n)
    806 
    807 		FDs=
    808 		LFD=-1
    809 		while [ ${LIM} -gt 16 ]
    810 		do
    811 			FD=$(( ${LIM} - 1 ))
    812 			if [ "${FD}" -eq "${LFD}" ]; then
    813 				echo >&2 "Infinite loop... (busted $(( )) ??)"
    814 				exit 1
    815 			fi
    816 			LFD="${FD}"
    817 
    818 			eval "exec ${FD}"'> /dev/null'
    819 			FDs="${FD}${FDs:+ }${FDs}"
    820 
    821 			(
    822 				FD=$(( ${LIM} + 1 ))
    823 				eval "exec ${FD}"'> /dev/null'
    824 				echo "Reached unreachable command"
    825 			) 2>/dev/null && echo >&2 "Opened beyond limit!"
    826 
    827 			(eval 'ls 2>&1 3>&1 4>&1 5>&1 '"${FD}"'>&1') >&"${FD}"
    828 
    829 			LIM=$(( ${LIM} / 2 ))
    830 			ulimit -S -n "${LIM}"
    831 		done
    832 
    833 		# Even though ulimit has been reduced, open fds should work
    834 		for FD in ${FDs}
    835 		do
    836 			echo ${FD} in ${FDs} >&"${FD}" || exit 1
    837 		done
    838 
    839 		ulimit -S -n "${oLIM}"
    840 
    841 		# maybe more later...
    842 
    843 	DONE
    844 
    845 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} helper.sh
    846 }
    847 
    848 atf_test_case validate_fn_redirects
    849 validate_fn_redirects_head()
    850 {
    851 	# These test cases inspired by PR bin/48875 and the sh
    852 	# changes that were required to fix it.
    853 
    854 	atf_set "descr" "Tests various redirections applied to functions " \
    855 		"See PR bin/48875"
    856 }
    857 validate_fn_redirects_body()
    858 {
    859 	cat <<- 'DONE' > f-def
    860 		f() {
    861 			printf '%s\n' In-Func
    862 		}
    863 	DONE
    864 
    865 	atf_check -s exit:0 -o inline:'In-Func\nsuccess1\n' -e empty \
    866 		${TEST_SH} -c ". ./f-def; f ; printf '%s\n' success1"
    867 	atf_check -s exit:0 -o inline:'success2\n' -e empty \
    868 		${TEST_SH} -c ". ./f-def; f >/dev/null; printf '%s\n' success2"
    869 	atf_check -s exit:0 -o inline:'success3\n' -e not-empty \
    870 		${TEST_SH} -c ". ./f-def; f >&- ; printf '%s\n' success3"
    871 	atf_check -s exit:0 -o inline:'In-Func\nsuccess4\n' -e empty \
    872 		${TEST_SH} -c ". ./f-def; f & wait; printf '%s\n' success4"
    873 	atf_check -s exit:0 -o inline:'success5\n' -e not-empty \
    874 		${TEST_SH} -c ". ./f-def; f >&- & wait; printf '%s\n' success5"
    875 	atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess6\n' -e empty \
    876 		${TEST_SH} -c ". ./f-def; f;f; printf '%s\n' success6"
    877 	atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess7\n' -e empty \
    878 		${TEST_SH} -c ". ./f-def; { f;f;}; printf '%s\n' success7"
    879 	atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess8\n' -e empty \
    880 		${TEST_SH} -c ". ./f-def; { f;f;}& wait; printf '%s\n' success8"
    881 	atf_check -s exit:0 -o inline:'In-Func\nsuccess9\n' -e empty \
    882 		${TEST_SH} -c \
    883 		   ". ./f-def; { f>/dev/null;f;}& wait; printf '%s\n' success9"
    884 	atf_check -s exit:0 -o inline:'In-Func\nsuccess10\n' -e empty \
    885 		${TEST_SH} -c \
    886 		   ". ./f-def; { f;f>/dev/null;}& wait; printf '%s\n' success10"
    887 
    888 	# This one tests the issue etcupdate had with the original 48875 fix
    889 	atf_check -s exit:0 -o inline:'Func a\nFunc b\nFunc c\n' -e empty \
    890 		${TEST_SH} -c '
    891 			f() {
    892 				echo Func "$1"
    893 			}
    894 			exec 3<&0 4>&1
    895 			( echo x-a; echo y-b; echo z-c ) |
    896 			while read A
    897 			do
    898 				B=${A#?-}
    899 				f "$B" <&3 >&4
    900 			done >&2'
    901 
    902 	# And this tests a similar condition with that same fix
    903 	cat  <<- 'DONE' >Script
    904 		f() {
    905 			printf '%s' " hello $1"
    906 		}
    907 		exec 3>&1
    908 		echo $( for i in a b c
    909 			do printf '%s' @$i; f $i >&3; done >foo
    910 		)
    911 		printf '%s\n' foo=$(cat foo)
    912 	DONE
    913 	atf_check -s exit:0 -e empty \
    914 	    -o inline:' hello a hello b hello c\nfoo=@a@b@c\n' \
    915 	    ${TEST_SH} Script
    916 
    917 	# Tests with sh reading stdin, which is not quite the same internal
    918 	# mechanism.
    919 	echo ". ./f-def || echo >&2 FAIL
    920 		f
    921 		printf '%s\n' stdin1
    922 	"| atf_check -s exit:0 -o inline:'In-Func\nstdin1\n' -e empty ${TEST_SH}
    923 
    924 	echo '
    925 		. ./f-def || echo >&2 FAIL
    926 		f >&-
    927 		printf "%s\n" stdin2
    928 	' | atf_check -s exit:0 -o inline:'stdin2\n' -e empty ${TEST_SH}
    929 
    930 	cat <<- 'DONE' > fgh.def
    931 		f() {
    932 			echo -n f >&3
    933 			sleep 4
    934 			echo -n F >&3
    935 		}
    936 		g() {
    937 			echo -n g >&3
    938 			sleep 2
    939 			echo -n G >&3
    940 		}
    941 		h() {
    942 			echo -n h >&3
    943 		}
    944 	DONE
    945 
    946 	atf_check -s exit:0 -o inline:'fFgGh' -e empty \
    947 		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
    948 			exec 3>&1
    949 			f; g; h'
    950 
    951 	atf_check -s exit:0 -o inline:'fghGF' -e empty \
    952 		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
    953 			exec 3>&1
    954 			f & sleep 1; g & sleep 1; h; wait'
    955 
    956 	atf_check -s exit:0 -o inline:'fFgGhX Y\n' -e empty \
    957 		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
    958 			exec 3>&1
    959 			echo X $( f ; g ; h ) Y'
    960 
    961 	# This one is the real test for PR bin/48875.  If the
    962 	# cmdsub does not complete before f g (and h) exit,
    963 	# then the 'F' & 'G' will precede 'X Y' in the output.
    964 	# If the cmdsub finishes while f & g are still running,
    965 	# then the X Y will appear before the F and G.
    966 	# The trailing "sleep 3" is just so we catch all the
    967 	# output (otherwise atf_check will be finished while
    968 	# f & g are still sleeping).
    969 
    970 	atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty \
    971 		${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL
    972 			exec 3>&1
    973 			echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y
    974 			sleep 3
    975 			exec 4>&1 || echo FD_FAIL
    976 			'
    977 
    978 	# Do the test again to verify it also all works reading stdin
    979 	# (which is a slightly different path through the shell)
    980 	echo '
    981 		. ./fgh.def || echo >&2 FAIL
    982 		exec 3>&1
    983 		echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y
    984 		sleep 3
    985 		exec 4>&1 || echo FD_FAIL
    986 	' | atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty ${TEST_SH}
    987 }
    988 
    989 atf_init_test_cases() {
    990 	atf_add_test_case basic_test_method_test
    991 	atf_add_test_case do_input_redirections
    992 	atf_add_test_case do_output_redirections
    993 	atf_add_test_case do_redirect_input_output
    994 	atf_add_test_case fd_redirections
    995 	atf_add_test_case local_redirections
    996 	atf_add_test_case incorrect_redirections
    997 	atf_add_test_case named_fd_redirections
    998 	atf_add_test_case redir_here_doc
    999 	atf_add_test_case redir_in_case
   1000 	atf_add_test_case subshell_redirections
   1001 	atf_add_test_case ulimit_redirection_interaction
   1002 	atf_add_test_case validate_fn_redirects
   1003 }
   1004