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