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