diff options
Diffstat (limited to 'contrib/netbsd-tests/bin/sh')
19 files changed, 5377 insertions, 236 deletions
diff --git a/contrib/netbsd-tests/bin/sh/dotcmd/scoped_command b/contrib/netbsd-tests/bin/sh/dotcmd/scoped_command index fda4e53..36e712b 100755 --- a/contrib/netbsd-tests/bin/sh/dotcmd/scoped_command +++ b/contrib/netbsd-tests/bin/sh/dotcmd/scoped_command @@ -1,6 +1,6 @@ #!/bin/sh # -# $NetBSD: scoped_command,v 1.1 2014/05/31 14:29:06 christos Exp $ +# $NetBSD: scoped_command,v 1.2 2016/03/27 14:57:50 christos Exp $ # # Copyright (c) 2014 The NetBSD Foundation, Inc. # All rights reserved. @@ -30,6 +30,27 @@ # POSSIBILITY OF SUCH DAMAGE. # +: ${TEST_SH:=/bin/sh} + +sane_sh() +{ + set -- ${TEST_SH} + case "$#" in + (0) set /bin/sh;; + (1|2) ;; + (*) set "$1";; # Just ignore options if we cannot make them work + esac + + case "$1" in + /*) TEST_SH="$1${2+ }$2";; + ./*) TEST_SH="${PWD}${1#.}${2+ }$2";; + */*) TEST_SH="${PWD}/$1${2+ }$2";; + *) TEST_SH="$( command -v "$1" )${2+ }$2";; + esac +} + +sane_sh + set -e # USAGE: @@ -52,7 +73,7 @@ cmd="echo 'before ${3}' ${2} echo 'after ${3}, return value:' ${?}" -echo "#!/bin/sh" +echo "#!${TEST_SH}" [ 'func' = "${1}" ] && cat <<EOF func() diff --git a/contrib/netbsd-tests/bin/sh/dotcmd/t_dotcmd.sh b/contrib/netbsd-tests/bin/sh/dotcmd/t_dotcmd.sh index b365b1d..8e9b277 100755 --- a/contrib/netbsd-tests/bin/sh/dotcmd/t_dotcmd.sh +++ b/contrib/netbsd-tests/bin/sh/dotcmd/t_dotcmd.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_dotcmd.sh,v 1.1 2014/05/31 14:29:06 christos Exp $ +# $NetBSD: t_dotcmd.sh,v 1.2 2016/03/27 14:57:50 christos Exp $ # # Copyright (c) 2014 The NetBSD Foundation, Inc. # All rights reserved. @@ -33,6 +33,10 @@ # in C/C++ so, for example, if the dotcmd is in a loop's body, a break in # the sourced file can be used to break out of that loop. +# Note that the standard does not require this, and allows lexically +# scoped interpretation of break/continue (and permits dynamic scope +# as an optional extension.) + cmds='return break continue' scopes='case compound file for func subshell until while' diff --git a/contrib/netbsd-tests/bin/sh/t_arith.sh b/contrib/netbsd-tests/bin/sh/t_arith.sh new file mode 100755 index 0000000..d7b5083 --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_arith.sh @@ -0,0 +1,1035 @@ +# $NetBSD: t_arith.sh,v 1.5 2016/05/12 14:25:11 kre Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# Requirement is to support at least "signed long" whatever that means +# (number of bits in "long" is not specified - but should be at least 32). + +# These tests use -o inline:"..." rather than -o match:'...' as we have +# only digits to examine, and it is good to be sure that 1 + 1 really gives 2 +# and that 42 or 123 don't look like success because there is a 2 in them. + +ARITH_BITS='?' +discover_range() +{ + # cannot use arithmetic "test" operators, range of test in + # ATF_SHELL (or even TEST_SH) might not be as big as that + # supported by $(( )) in TEST_SH + + if ! ${TEST_SH} -c ': $(( 0x10000 ))' 2>/dev/null + then + # 16 bits or less, or hex unsupported, just give up... + return + fi + test $( ${TEST_SH} -c 'echo $(( 0x1FFFF ))' ) = 131071 || return + + # when attempting to exceed the number of available bits + # the shell may react in any of 3 (rational) ways + # 1. syntax error (maybe even core dump...) and fail + # 2. represent a positive number input as negative value + # 3. keep the number positive, but not the value expected + # (perhaps pegged at the max possible value) + # any of those may be accompanied by a message to stderr + + # Must check all 3 possibilities for each plausible size + # Tests do not use 0x8000... because that value can have weird + # other side effects that are not relevant to discover here. + # But we do want to try and force the sign bit set. + + if ! ${TEST_SH} -c ': $(( 0xC0000000 ))' 2>/dev/null + then + # proobably shell detected overflow and complained + ARITH_BITS=32 + return + fi + if ${TEST_SH} 2>/dev/null \ + -c 'case $(( 0xC0000000 )); in (-*) exit 0;; esac; exit 1' + then + ARITH_BITS=32 + return + fi + if ${TEST_SH} -c '[ $(( 0xC0000000 )) != 3221225472 ]' 2>/dev/null + then + ARITH_BITS=32 + return + fi + + if ! ${TEST_SH} -c ': $(( 0xC000000000000000 ))' 2>/dev/null + then + ARITH_BITS=64 + return + fi + if ${TEST_SH} 2>/dev/null \ + -c 'case $(( 0xC000000000000000 )); in (-*) exit 0;; esac; exit 1' + then + ARITH_BITS=64 + return + fi + if ${TEST_SH} 2>/dev/null \ + -c '[ $((0xC000000000000000)) != 13835058055282163712 ]' + then + ARITH_BITS=64 + return + fi + + if ${TEST_SH} 2>/dev/null -c \ + '[ $((0x123456781234567812345678)) = 5634002657842756053938493048 ]' + then + # just assume... (for now anyway, revisit when it happens...) + ARITH_BITS=96 + return + fi +} + +atf_test_case constants +constants_head() +{ + atf_set "descr" "Tests that arithmetic expansion can handle constants" +} +constants_body() +{ + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $((0x0))' + + # atf_expect_fail "PR bin/50959" + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $((0X0))' + # atf_expect_pass + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $((000))' + + atf_check -s exit:0 -o inline:'1\n' -e empty \ + ${TEST_SH} -c 'echo $(( 000000001 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty \ + ${TEST_SH} -c 'echo $(( 0x000000 ))' + + atf_check -s exit:0 -o inline:'99999\n' -e empty \ + ${TEST_SH} -c 'echo $((99999))' + + [ ${ARITH_BITS} -gt 44 ] && + atf_check -s exit:0 -o inline:'9191919191919\n' -e empty \ + ${TEST_SH} -c 'echo $((9191919191919))' + + atf_check -s exit:0 -o inline:'13\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xD ))' + atf_check -s exit:0 -o inline:'11\n' -e empty ${TEST_SH} -c \ + 'echo $(( 013 ))' + atf_check -s exit:0 -o inline:'7\n' -e empty ${TEST_SH} -c \ + 'x=7;echo $(($x))' + atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \ + 'x=9;echo $((x))' + + atf_check -s exit:0 -o inline:'11\n' -e empty \ + ${TEST_SH} -c 'x=0xB; echo $(( $x ))' + atf_check -s exit:0 -o inline:'27\n' -e empty \ + ${TEST_SH} -c 'x=0X1B; echo $(( x ))' + atf_check -s exit:0 -o inline:'27\n' -e empty \ + ${TEST_SH} -c 'X=033; echo $(( $X ))' + atf_check -s exit:0 -o inline:'219\n' -e empty \ + ${TEST_SH} -c 'X=0333; echo $(( X ))' + atf_check -s exit:0 -o inline:'0\n' -e empty \ + ${TEST_SH} -c 'NULL=; echo $(( NULL ))' + + # Not clear if this is 0, nothing, or an error, so omit for now + # atf_check -s exit:0 -o inline:'0\n' -e empty \ + # ${TEST_SH} -c 'echo $(( ))' + + # not clear whether this should return 0 or an error, so omit for now + # atf_check -s exit:0 -o inline:'0\n' -e empty \ + # ${TEST_SH} -c 'echo $(( UNDEFINED_VAR ))' +} + + +atf_test_case do_unary_plus +do_unary_plus_head() +{ + atf_set "descr" "Tests that unary plus works as expected" +} +do_unary_plus_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( +0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( +1 ))' + atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \ + 'echo $(( + 6 ))' + atf_check -s exit:0 -o inline:'4321\n' -e empty ${TEST_SH} -c \ + 'echo $(( + 4321 ))' + atf_check -s exit:0 -o inline:'17185\n' -e empty ${TEST_SH} -c \ + 'echo $(( + 0x4321 ))' +} + +atf_test_case do_unary_minus +do_unary_minus_head() +{ + atf_set "descr" "Tests that unary minus works as expected" +} +do_unary_minus_body() +{ + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( -1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( - 0 ))' + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( - 1 ))' + atf_check -s exit:0 -o inline:'-6\n' -e empty ${TEST_SH} -c \ + 'echo $(( - 6 ))' + atf_check -s exit:0 -o inline:'-4321\n' -e empty ${TEST_SH} -c \ + 'echo $(( - 4321 ))' + atf_check -s exit:0 -o inline:'-2257\n' -e empty ${TEST_SH} -c \ + 'echo $(( - 04321 ))' + atf_check -s exit:0 -o inline:'-7\n' -e empty ${TEST_SH} -c \ + 'echo $((-7))' +} + +atf_test_case do_unary_not +do_unary_not_head() +{ + atf_set "descr" "Tests that unary not (boolean) works as expected" +} +do_unary_not_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( ! 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( ! 0 ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( !1234 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( !0xFFFF ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( ! 000000 ))' +} + +atf_test_case do_unary_tilde +do_unary_tilde_head() +{ + atf_set "descr" "Tests that unary not (bitwise) works as expected" +} +do_unary_tilde_body() +{ + # definitely 2's complement arithmetic here... + + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( ~ 0 ))' + atf_check -s exit:0 -o inline:'-2\n' -e empty ${TEST_SH} -c \ + 'echo $(( ~ 1 ))' + + atf_check -s exit:0 -o inline:'-1235\n' -e empty ${TEST_SH} -c \ + 'echo $(( ~1234 ))' + atf_check -s exit:0 -o inline:'-256\n' -e empty ${TEST_SH} -c \ + 'echo $(( ~0xFF ))' +} + +atf_test_case elementary_add +elementary_add_head() +{ + atf_set "descr" "Tests that simple addition works as expected" +} +elementary_add_body() +{ + # some of these tests actually test unary ops & op precedence... + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 + 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 + 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 + 1 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 + 1 ))' + atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \ + 'echo $(( 4 + 6 ))' + atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \ + 'echo $(( 6 + 4 ))' + atf_check -s exit:0 -o inline:'5555\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1234 + 4321 ))' + atf_check -s exit:0 -o inline:'3333\n' -e empty ${TEST_SH} -c \ + 'echo $((1111+2222))' + atf_check -s exit:0 -o inline:'5555\n' -e empty ${TEST_SH} -c \ + 'echo $((+3333+2222))' + atf_check -s exit:0 -o inline:'7777\n' -e empty ${TEST_SH} -c \ + 'echo $((+3333 + +4444))' + atf_check -s exit:0 -o inline:'-7777\n' -e empty ${TEST_SH} -c \ + 'echo -$((+4125+ +3652))' +} + +atf_test_case elementary_sub +elementary_sub_head() +{ + atf_set "descr" "Tests that simple subtraction works as expected" +} +elementary_sub_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 - 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 - 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 - 1 ))' + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 - 1 ))' + atf_check -s exit:0 -o inline:'488\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1066 - 578 ))' + atf_check -s exit:0 -o inline:'-3662\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2016-5678 ))' + atf_check -s exit:0 -o inline:'-3662\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2016+-5678 ))' + atf_check -s exit:0 -o inline:'-3662\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2016-+5678 ))' + atf_check -s exit:0 -o inline:'-7694\n' -e empty ${TEST_SH} -c \ + 'echo $(( -2016-5678 ))' + atf_check -s exit:0 -o inline:'--1\n' -e empty ${TEST_SH} -c \ + 'echo -$(( -1018 - -1017 ))' +} + +atf_test_case elementary_mul +elementary_mul_head() +{ + atf_set "descr" "Tests that simple multiplication works as expected" +} +elementary_mul_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 * 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 * 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 * 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 * 1 ))' + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( -1 * 1 ))' + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 * -1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( -1 * -1 ))' + atf_check -s exit:0 -o inline:'391\n' -e empty ${TEST_SH} -c \ + 'echo $(( 17 * 23 ))' + atf_check -s exit:0 -o inline:'169\n' -e empty ${TEST_SH} -c \ + 'echo $(( 13*13 ))' + atf_check -s exit:0 -o inline:'-11264\n' -e empty ${TEST_SH} -c \ + 'echo $(( -11 *1024 ))' + atf_check -s exit:0 -o inline:'-16983\n' -e empty ${TEST_SH} -c \ + 'echo $(( 17* -999 ))' + atf_check -s exit:0 -o inline:'9309\n' -e empty ${TEST_SH} -c \ + 'echo $(( -29*-321 ))' +} + +atf_test_case elementary_div +elementary_div_head() +{ + atf_set "descr" "Tests that simple division works as expected" +} +elementary_div_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 / 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 / 1 ))' + test ${ARITH_BITS} -ge 38 && + atf_check -s exit:0 -o inline:'99999999999\n' -e empty \ + ${TEST_SH} -c 'echo $(( 99999999999 / 1 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 / 1 ))' + + atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 / 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 / 2 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 / 3 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 / 4 ))' + + atf_check -s exit:0 -o inline:'173\n' -e empty ${TEST_SH} -c \ + 'echo $(( 123456 / 713 ))' + atf_check -s exit:0 -o inline:'13\n' -e empty ${TEST_SH} -c \ + 'echo $(( 169 / 13 ))' +} + +atf_test_case elementary_rem +elementary_rem_head() +{ + atf_set "descr" "Tests that simple modulus works as expected" +} +elementary_rem_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 % 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 % 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 % 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9999 % 1 ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 % 2 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 % 2 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 % 2 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xFFFF % 2 ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 % 3 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 % 3 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 % 3 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 % 3 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3123 % 3 ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9999 % 2 ))' + + atf_check -s exit:0 -o inline:'107\n' -e empty ${TEST_SH} -c \ + 'echo $(( 123456%173 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $((169%13))' +} + +atf_test_case elementary_shl +elementary_shl_head() +{ + atf_set "descr" "Tests that simple shift left works as expected" +} +elementary_shl_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 << 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 << 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 << 17 ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 << 0 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 << 1 ))' + atf_check -s exit:0 -o inline:'131072\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 << 17 ))' + + atf_check -s exit:0 -o inline:'2021161080\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x3C3C3C3C << 1 ))' + + test "${ARITH_BITS}" -ge 40 && + atf_check -s exit:0 -o inline:'129354309120\n' -e empty \ + ${TEST_SH} -c 'echo $(( 0x3C3C3C3C << 7 ))' + test "${ARITH_BITS}" -ge 72 && + atf_check -s exit:0 -o inline:'1111145054534149079040\n' \ + -e empty ${TEST_SH} -c 'echo $(( 0x3C3C3C3C << 40 ))' + + return 0 +} + +atf_test_case elementary_shr +elementary_shr_head() +{ + atf_set "descr" "Tests that simple shift right works as expected" +} +elementary_shr_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 >> 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 >> 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 >> 17 ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 >> 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 >> 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 >> 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 >> 1 ))' + + atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x10 >> 2 ))' + atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \ + 'echo $(( 022 >> 2 ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 131072 >> 17 ))' + + test ${ARITH_BITS} -ge 40 && + atf_check -s exit:0 -o inline:'8\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x4000000000 >> 35 ))' + test ${ARITH_BITS} -ge 80 && + atf_check -s exit:0 -o inline:'4464\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x93400FACE005C871000 >> 64 ))' + + return 0 +} + +atf_test_case elementary_eq +elementary_eq_head() +{ + atf_set "descr" "Tests that simple equality test works as expected" +} +elementary_eq_body() +{ + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 == 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 == 0000 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 == 0x00 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 == 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'X=30; Y=0x1E; echo $(( X == Y ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1234 == 4660 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1234 == 011064 ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 == 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 == 0000000000000001 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 == 0x10000000000000 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 == 2 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'X=3; Y=7; echo $(( X == Y ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1234 == 0x4660 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 01234 == 0x11064 ))' +} +atf_test_case elementary_ne +elementary_ne_head() +{ + atf_set "descr" "Tests that simple inequality test works as expected" +} +elementary_ne_body() +{ + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 != 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x71 != 17 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1234 != 01234 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1234 != 01234 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'X=3; echo $(( X != 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'X=3; Y=0x11; echo $(( X != Y ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 != 3 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 != 0x0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xA != 012 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'X=1; echo $(( X != 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'X=0xC; Y=014; echo $(( X != Y ))' +} +atf_test_case elementary_lt +elementary_lt_head() +{ + atf_set "descr" "Tests that simple less than test works as expected" +} +elementary_lt_body() +{ + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 < 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( -1 < 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 < 10 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 100 < 101 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xA1 < 200 ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 < 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 < 0 ))' + + test ${ARITH_BITS} -ge 40 && + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1BEEFF00D < 0x1FACECAFE ))' + + return 0 +} +atf_test_case elementary_le +elementary_le_head() +{ + atf_set "descr" "Tests that simple less or equal test works as expected" +} +elementary_le_body() +{ + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 <= 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( -1 <= 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 <= 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 <= 10 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 100 <= 101 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xA1 <= 161 ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 <= 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( -100 <= -200 ))' + + test ${ARITH_BITS} -ge 40 && + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'cost=; AUD=; echo $(( $cost 0x2FEEDBABE <= $AUD 12866927294 ))' + + return 0 +} +atf_test_case elementary_gt +elementary_gt_head() +{ + atf_set "descr" "Tests that simple greater than works as expected" +} +elementary_gt_body() +{ + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 > 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 > -1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 11 > 012 ))' + + # atf_expect_fail "PR bin/50959" + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2147483647 > 0X7FFFFF0 ))' + # atf_expect_pass + + test ${ARITH_BITS} -gt 32 && + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x80000000 > 0x7FFFFFFF ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 > 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 > 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( -1 > 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 > 10 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2015 > 2016 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xA1 > 200 ))' + + test ${ARITH_BITS} -ge 44 && + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x7F07F07F0 > 34099628014 ))' + + return 0 +} +atf_test_case elementary_ge +elementary_ge_head() +{ + atf_set "descr" "Tests that simple greater or equal works as expected" +} +elementary_ge_body() +{ + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 >= 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 >= 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( -100 >= -101 ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( -1 >= 0 ))' +} + +atf_test_case fiddle_bits_and +fiddle_bits_and_head() +{ + atf_set "descr" "Test bitwise and operations in arithmetic expressions" +} +fiddle_bits_and_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 & 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 & 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 & 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 & 1 ))' + + atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xFF & 0xFF ))' + atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xFFFF & 0377 ))' + + test "${ARITH_BITS}" -ge 48 && + atf_check -s exit:0 -o inline:'70377641607203\n' -e empty \ + ${TEST_SH} -c 'echo $(( 0x5432FEDC0123 & 0x42871357BAB3 ))' + + return 0 +} +atf_test_case fiddle_bits_or +fiddle_bits_or_head() +{ + atf_set "descr" "Test bitwise or operations in arithmetic expressions" +} +fiddle_bits_or_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 | 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 | 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 | 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 | 1 ))' + + atf_check -s exit:0 -o inline:'4369\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1111 | 0x1111 ))' + atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xAA | 0125 ))' + + test "${ARITH_BITS}" -ge 48 && + atf_check -s exit:0 -o inline:'95348271856563\n' -e empty \ + ${TEST_SH} -c 'echo $(( 0x5432FEDC0123 | 0x42871357BAB3 ))' + + return 0 +} +atf_test_case fiddle_bits_xor +fiddle_bits_xor_head() +{ + atf_set "descr" "Test bitwise xor operations in arithmetic expressions" +} +fiddle_bits_xor_body() +{ + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 ^ 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 ^ 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 ^ 1 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 ^ 1 ))' + + atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xF0 ^ 0x0F ))' + atf_check -s exit:0 -o inline:'15\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xF0 ^ 0xFF ))' + + test "${ARITH_BITS}" -ge 48 && + atf_check -s exit:0 -o inline:'24970630249360\n' -e empty \ + ${TEST_SH} -c 'echo $(( 0x5432FEDC0123 ^ 0x42871357BAB3 ))' + + return 0 +} + +atf_test_case logical_and +logical_and_head() +{ + atf_set "descr" "Test logical and operations in arithmetic expressions" +} +logical_and_body() +{ + # cannot test short-circuit eval until sh implements side effects... + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 && 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 && 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 && 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 && 1 ))' + + # atf_expect_fail "PR bin/50960" + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1111 && 01234 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xFFFF && 0xF0F0 ))' +} +atf_test_case logical_or +logical_or_head() +{ + atf_set "descr" "Test logical or operations in arithmetic expressions" +} +logical_or_body() +{ + # cannot test short-circuit eval until sh implements side effects... + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 || 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 || 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 || 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 || 1 ))' + + # atf_expect_fail "PR bin/50960" + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1111 || 01234 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x33 || 0xF0F0 ))' +} + +atf_test_case make_selection +make_selection_head() +{ + atf_set "descr" "Test ?: operator in arithmetic expressions" +} +make_selection_body() +{ + # atf_expect_fail "PR bin/50958" + + atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0 ? 2 : 3 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 ? 2 : 3 ))' + + atf_check -s exit:0 -o inline:'111\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0x1234 ? 111 : 222 ))' + + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 < 1 ? -1 : 1 > 1 ? 1 : 0 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 < 1 ? -1 : 2 > 1 ? 1 : 0 ))' +} + +atf_test_case operator_precedence +operator_precedence_head() +{ + atf_set "descr" "Test operator precedence without parentheses" +} +operator_precedence_body() +{ + # NB: apart from $(( )) ** NO ** parentheses in the expressions. + + atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 + 2 + 3 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 - 2 + 3 ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 - 2 - 1 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 - 2 + 1 ))' + + atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \ + 'echo $(( - 2 + 1 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 + -1 ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( ! 2 + 1 ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 + !1 ))' + + atf_check -s exit:0 -o inline:'8\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 * 2 + 2 ))' + atf_check -s exit:0 -o inline:'7\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 + 2 * 2 ))' + atf_check -s exit:0 -o inline:'12\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 * 2 * 2 ))' + + atf_check -s exit:0 -o inline:'5\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 / 3 + 2 ))' + atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 + 3 / 2 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 / 3 / 2 ))' + + atf_check -s exit:0 -o inline:'72\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 << 1 + 2 ))' + atf_check -s exit:0 -o inline:'48\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 + 3 << 2 ))' + atf_check -s exit:0 -o inline:'288\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 << 3 << 2 ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 >> 1 + 2 ))' + atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 + 3 >> 2 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 19 >> 3 >> 1 ))' + + atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \ + 'echo $(( 19 >> 3 << 1 ))' + atf_check -s exit:0 -o inline:'76\n' -e empty ${TEST_SH} -c \ + 'echo $(( 19 << 3 >> 1 ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 + 3 < 3 * 2 ))' + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 << 3 >= 3 << 2 ))' + + # sh inherits C's crazy operator precedence... + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 0xfD & 0xF == 0xF ))' +} + +parentheses_head() +{ + atf_set "descr" "Test use of () to group sub-expressions" +} +parentheses_body() +{ + atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \ + 'echo $(( (1 + 2) + 3 ))' + atf_check -s exit:0 -o inline:'-4\n' -e empty ${TEST_SH} -c \ + 'echo $(( 1 - (2 + 3) ))' + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 - (2 - 1) ))' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 - ( 2 + 1 ) ))' + + atf_check -s exit:0 -o inline:'-3\n' -e empty ${TEST_SH} -c \ + 'echo $(( - (2 + 1) ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( ! (2 + 1) ))' + + atf_check -s exit:0 -o inline:'12\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 * (2 + 2) ))' + atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \ + 'echo $(( (3 + 2) * 2 ))' + atf_check -s exit:0 -o inline:'12\n' -e empty ${TEST_SH} -c \ + 'echo $(( 3 * (2 * 2) ))' + + atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 / (3 + 2) ))' + atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \ + 'echo $(( ( 9 + 3 ) / 2 ))' + atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 / ( 3 / 2 ) ))' + + atf_check -s exit:0 -o inline:'20\n' -e empty ${TEST_SH} -c \ + 'echo $(( ( 9 << 1 ) + 2 ))' + atf_check -s exit:0 -o inline:'21\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 + (3 << 2) ))' + atf_check -s exit:0 -o inline:'36864\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 << (3 << 2) ))' + + atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \ + 'echo $(( (9 >> 1) + 2 ))' + atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \ + 'echo $(( 9 + (3 >> 2) ))' + atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \ + 'echo $(( 19 >> (3 >> 1) ))' + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( 19 >> (3 << 1) ))' + atf_check -s exit:0 -o inline:'38\n' -e empty ${TEST_SH} -c \ + 'echo $(( 19 << (3 >> 1) ))' + + atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 + (3 < 3) * 2 ))' + atf_check -s exit:0 -o inline:'32\n' -e empty ${TEST_SH} -c \ + 'echo $(( 2 << ((3 >= 3) << 2) ))' + + # sh inherits C's crazy operator precedence... + + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'echo $(( (0xfD & 0xF) == 0xF ))' +} + +atf_test_case arithmetic_fails +arithmetic_fails_head() +{ + atf_set "descr" "Dummy test to force failure" +} +arithmetic_fails_body() +{ + atf_fail "Cannot estimate number of bits supported by $(( ))" +} + +atf_init_test_cases() { + + discover_range + + test "${ARITH_BITS}" = '?' && { + atf_add_test_case arithmetic_fails + return 0 + } + + # odd names are to get atf's sort order semi-rational + + atf_add_test_case constants + atf_add_test_case do_unary_plus + atf_add_test_case do_unary_minus + atf_add_test_case do_unary_not + atf_add_test_case do_unary_tilde + atf_add_test_case elementary_add + atf_add_test_case elementary_sub + atf_add_test_case elementary_mul + atf_add_test_case elementary_div + atf_add_test_case elementary_rem + atf_add_test_case elementary_shl + atf_add_test_case elementary_shr + atf_add_test_case elementary_eq + atf_add_test_case elementary_ne + atf_add_test_case elementary_lt + atf_add_test_case elementary_le + atf_add_test_case elementary_gt + atf_add_test_case elementary_ge + atf_add_test_case fiddle_bits_and + atf_add_test_case fiddle_bits_or + atf_add_test_case fiddle_bits_xor + atf_add_test_case logical_and + atf_add_test_case logical_or + atf_add_test_case make_selection + atf_add_test_case operator_precedence + atf_add_test_case parentheses + # atf_add_test_case progressive # build up big expr + # atf_add_test_case test_errors # erroneous input + # atf_add_test_case torture # hard stuff (if there is any) + # atf_add_test_case var_assign # assignment ops + # atf_add_test_case vulgarity # truly evil inputs (syntax in vars...) +} diff --git a/contrib/netbsd-tests/bin/sh/t_cmdsub.sh b/contrib/netbsd-tests/bin/sh/t_cmdsub.sh new file mode 100755 index 0000000..f3ee210 --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_cmdsub.sh @@ -0,0 +1,783 @@ +# $NetBSD: t_cmdsub.sh,v 1.4 2016/04/04 12:40:13 christos Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# +# This file tests command substitutions ( `...` and $( ... ) ) +# +# CAUTION: +# Be careful attempting running these tests outside the ATF environment +# Some of the tests run "rm *" in the current directory to clean up +# An ATF test directory should be empty already, outside ATF, anything + +atf_test_case a_basic_cmdsub +a_basic_cmdsub_head() { + atf_set "descr" 'Test operation of simple $( ) substitutions' +} +a_basic_cmdsub_body() { + atf_check -s exit:0 -o match:'Result is true today' -e empty \ + ${TEST_SH} -c \ + 'echo Result is $( true && echo true || echo false ) today' + + atf_check -s exit:0 -o match:'Result is false today' -e empty \ + ${TEST_SH} -c \ + 'echo Result is $( false && echo true || echo false ) today' + + atf_check -s exit:0 -o match:'aaabbbccc' -e empty \ + ${TEST_SH} -c 'echo aaa$( echo bbb )ccc' + atf_check -s exit:0 -o match:'aaabbb cccddd' -e empty \ + ${TEST_SH} -c 'echo aaa$( echo bbb ccc )ddd' + atf_check -s exit:0 -o inline:'aaabbb cccddd\n' -e empty \ + ${TEST_SH} -c 'echo aaa$( echo bbb; echo ccc )ddd' + atf_check -s exit:0 -o inline:'aaabbb\ncccddd\n' -e empty \ + ${TEST_SH} -c 'echo "aaa$( echo bbb; echo ccc )ddd"' + + atf_check -s exit:0 -o inline:'some string\n' -e empty \ + ${TEST_SH} -c 'X=$( echo some string ); echo "$X"' + atf_check -s exit:0 -o inline:'weird; string *\n' -e empty \ + ${TEST_SH} -c 'X=$( echo "weird; string *" ); echo "$X"' + + rm -f * 2>/dev/null || : + for f in file-1 file-2 + do + cp /dev/null "$f" + done + + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found $( echo * )' + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found "$( echo * )"' + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found $('" echo '*' )" + atf_check -s exit:0 -o match:'Found \*' -e empty \ + ${TEST_SH} -c 'echo Found "$('" echo '*' "')"' + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found $('" echo \\* )" + atf_check -s exit:0 -o match:'Found \*' -e empty \ + ${TEST_SH} -c 'echo Found "$('" echo \\* )"\" +} + +atf_test_case b_basic_backticks +b_basic_backticks_head() { + atf_set "descr" 'Test operation of old style ` ` substitutions' +} +b_basic_backticks_body() { + atf_check -s exit:0 -o match:'Result is true today' -e empty \ + ${TEST_SH} -c \ + 'echo Result is `true && echo true || echo false` today' + + atf_check -s exit:0 -o match:'Result is false today' -e empty \ + ${TEST_SH} -c \ + 'echo Result is `false && echo true || echo false` today' + + atf_check -s exit:0 -o match:'aaabbbccc' -e empty \ + ${TEST_SH} -c 'echo aaa` echo bbb `ccc' + atf_check -s exit:0 -o match:'aaabbb cccddd' -e empty \ + ${TEST_SH} -c 'echo aaa` echo bbb ccc `ddd' + atf_check -s exit:0 -o inline:'aaabbb cccddd\n' -e empty \ + ${TEST_SH} -c 'echo aaa` echo bbb; echo ccc `ddd' + atf_check -s exit:0 -o inline:'aaabbb\ncccddd\n' -e empty \ + ${TEST_SH} -c 'echo "aaa` echo bbb; echo ccc `ddd"' + + atf_check -s exit:0 -o inline:'some string\n' -e empty \ + ${TEST_SH} -c 'X=` echo some string `; echo "$X"' + atf_check -s exit:0 -o inline:'weird; string *\n' -e empty \ + ${TEST_SH} -c 'X=` echo "weird; string *" `; echo "$X"' + + rm -f * 2>/dev/null || : + for f in file-1 file-2 + do + cp /dev/null "$f" + done + + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found ` echo * `' + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found "` echo * `"' + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found `'" echo '*' "'`' + atf_check -s exit:0 -o match:'Found \*' -e empty \ + ${TEST_SH} -c 'echo Found "`'" echo '*' "'`"' + atf_check -s exit:0 -o match:'Found file-1 file-2' -e empty \ + ${TEST_SH} -c 'echo Found `'" echo \\* "'`' + atf_check -s exit:0 -o match:'Found \*' -e empty \ + ${TEST_SH} -c 'echo Found "`'" echo \\* "'`"' +} + +atf_test_case c_nested_cmdsub +c_nested_cmdsub_head() { + atf_set "descr" "Test that cmd substitutions can be nested" +} +c_nested_cmdsub_body() { + atf_check -s exit:0 -o match:'__foobarbletch__' -e empty \ + ${TEST_SH} -c 'echo __$( echo foo$(echo bar)bletch )__' + atf_check -s exit:0 -o match:'_abcde_' -e empty \ + ${TEST_SH} -c 'echo _$(echo a$(echo $(echo b)c$(echo d))e )_' + atf_check -s exit:0 -o match:'123454321' -e empty \ + ${TEST_SH} -c 'echo 1$(echo 2$(echo 3$(echo 4$(echo 5)4)3)2)1' +} + +atf_test_case d_nested_backticks +d_nested_backticks_head() { + atf_set "descr" "Tests that old style backtick cmd subs can be nested" +} +d_nested_backticks_body() { + atf_check -s exit:0 -o match:'__foobarbletch__' -e empty \ + ${TEST_SH} -c 'echo __` echo foo\`echo bar\`bletch `__' + atf_check -s exit:0 -o match:'_abcde_' -e empty \ + ${TEST_SH} -c \ + 'echo _`echo a\`echo \\\`echo b\\\`c\\\`echo d\\\`\`e `_' + atf_check -s exit:0 -o match:'123454321' -e empty \ + ${TEST_SH} -c \ + 'echo 1`echo 2\`echo 3\\\`echo 4\\\\\\\`echo 5\\\\\\\`4\\\`3\`2`1' +} + +atf_test_case e_perverse_mixing +e_perverse_mixing_head() { + atf_set "descr" \ + "Checks various mixed new and old style cmd substitutions" +} +e_perverse_mixing_body() { + atf_check -s exit:0 -o match:'__foobarbletch__' -e empty \ + ${TEST_SH} -c 'echo __$( echo foo`echo bar`bletch )__' + atf_check -s exit:0 -o match:'__foobarbletch__' -e empty \ + ${TEST_SH} -c 'echo __` echo foo$(echo bar)bletch `__' + atf_check -s exit:0 -o match:'_abcde_' -e empty \ + ${TEST_SH} -c 'echo _$(echo a`echo $(echo b)c$(echo d)`e )_' + atf_check -s exit:0 -o match:'_abcde_' -e empty \ + ${TEST_SH} -c 'echo _`echo a$(echo \`echo b\`c\`echo d\`)e `_' + atf_check -s exit:0 -o match:'12345654321' -e empty \ + ${TEST_SH} -c \ + 'echo 1`echo 2$(echo 3\`echo 4\\\`echo 5$(echo 6)5\\\`4\`3)2`1' +} + +atf_test_case f_redirect_in_cmdsub +f_redirect_in_cmdsub_head() { + atf_set "descr" "Checks that redirects work in command substitutions" +} +f_redirect_in_cmdsub_body() { + atf_require_prog cat + atf_require_prog rm + + rm -f file 2>/dev/null || : + atf_check -s exit:0 -o match:'_aa_' -e empty \ + ${TEST_SH} -c 'echo _$( echo a$( echo b > file )a)_' + atf_check -s exit:0 -o match:b -e empty ${TEST_SH} -c 'cat file' + atf_check -s exit:0 -o match:'_aba_' -e empty \ + ${TEST_SH} -c 'echo _$( echo a$( cat < file )a)_' + atf_check -s exit:0 -o match:'_aa_' -e empty \ + ${TEST_SH} -c 'echo _$( echo a$( echo d >> file )a)_' + atf_check -s exit:0 -o inline:'b\nd\n' -e empty ${TEST_SH} -c 'cat file' + atf_check -s exit:0 -o match:'_aa_' -e match:'not error' \ + ${TEST_SH} -c 'echo _$( echo a$( echo not error >&2 )a)_' +} + +atf_test_case g_redirect_in_backticks +g_redirect_in_backticks_head() { + atf_set "descr" "Checks that redirects work in old style cmd sub" +} +g_redirect_in_backticks_body() { + atf_require_prog cat + atf_require_prog rm + + rm -f file 2>/dev/null || : + atf_check -s exit:0 -o match:'_aa_' -e empty \ + ${TEST_SH} -c 'echo _` echo a\` echo b > file \`a`_' + atf_check -s exit:0 -o match:b -e empty ${TEST_SH} -c 'cat file' + atf_check -s exit:0 -o match:'_aba_' -e empty \ + ${TEST_SH} -c 'echo _` echo a\` cat < file \`a`_' + atf_check -s exit:0 -o match:'_aa_' -e empty \ + ${TEST_SH} -c 'echo _` echo a\` echo d >> file \`a`_' + atf_check -s exit:0 -o inline:'b\nd\n' -e empty ${TEST_SH} -c 'cat file' + atf_check -s exit:0 -o match:'_aa_' -e match:'not error' \ + ${TEST_SH} -c 'echo _` echo a\` echo not error >&2 \`a`_' +} + +atf_test_case h_vars_in_cmdsub +h_vars_in_cmdsub_head() { + atf_set "descr" "Check that variables work in command substitutions" +} +h_vars_in_cmdsub_body() { + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo __$( echo ${X} )__' + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo __$( echo "${X}" )__' + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo "__$( echo ${X} )__"' + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo "__$( echo "${X}" )__"' + + atf_check -s exit:0 -o inline:'a\n\nb\n\nc\n' -e empty \ + ${TEST_SH} -c "for X in a '' b '' c"'; do echo $( echo "$X" ); done' + + atf_check -s exit:0 -o match:'__acd__' -e empty \ + ${TEST_SH} -c 'X=; unset Y; echo "__$( echo a${X-b}${Y-c}d)__"' + atf_check -s exit:0 -o match:'__abcd__' -e empty \ + ${TEST_SH} -c 'X=; unset Y; echo "__$( echo a${X:-b}${Y:-c}d)__"' + atf_check -s exit:0 -o match:'__XYX__' -e empty \ + ${TEST_SH} -c 'X=X; echo "__${X}$( X=Y; echo ${X} )${X}__"' + atf_check -s exit:0 -o match:'__def__' -e empty \ + ${TEST_SH} -c 'X=abc; echo "__$(X=def; echo "${X}" )__"' + atf_check -s exit:0 -o inline:'abcdef\nabc\n' -e empty \ + ${TEST_SH} -c 'X=abc; echo "$X$(X=def; echo ${X} )"; echo $X' +} + +atf_test_case i_vars_in_backticks +i_vars_in_backticks_head() { + atf_set "descr" "Checks that variables work in old style cmd sub" +} +i_vars_in_backticks_body() { + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo __` echo ${X} `__' + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo __` echo "${X}" `__' + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo "__` echo ${X} `__"' + atf_check -s exit:0 -o match:'__abc__' -e empty \ + ${TEST_SH} -c 'X=abc; echo "__` echo \"${X}\" `__"' + + atf_check -s exit:0 -o inline:'a\n\nb\n\nc\n' -e empty \ + ${TEST_SH} -c "for X in a '' b '' c"'; do echo $( echo "$X" ); done' + + atf_check -s exit:0 -o match:'__acd__' -e empty \ + ${TEST_SH} -c 'X=; unset Y; echo "__$( echo a${X-b}${Y-c}d)__"' + atf_check -s exit:0 -o match:'__abcd__' -e empty \ + ${TEST_SH} -c 'X=; unset Y; echo "__$( echo a${X:-b}${Y:-c}d)__"' + atf_check -s exit:0 -o match:'__XYX__' -e empty \ + ${TEST_SH} -c 'X=X; echo "__${X}$( X=Y; echo ${X} )${X}__"' + atf_check -s exit:0 -o inline:'abcdef\nabc\n' -e empty \ + ${TEST_SH} -c 'X=abc; echo "$X`X=def; echo \"${X}\" `";echo $X' + + # The following is nonsense, so is not included ... + # atf_check -s exit:0 -o match:'__abc__' -e empty \ + # oV cV oV cV + # ${TEST_SH} -c 'X=abc; echo "__`X=def echo "${X}" `__"' + # `start in " ^ " ends, ` not yet +} + +atf_test_case j_cmdsub_in_varexpand +j_cmdsub_in_varexpand_head() { + atf_set "descr" "Checks that command sub can be used in var expansion" +} +j_cmdsub_in_varexpand_body() { + atf_check -s exit:0 -o match:'foo' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X+$(echo foo)}' + atf_check -s exit:0 -o match:'set' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X-$(echo foo)}' + rm -f bar 2>/dev/null || : + atf_check -s exit:0 -o match:'set' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X-$(echo foo > bar)}' + test -f bar && atf_fail "bar should not exist, but does" + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X+$(echo foo > bar)}' + test -f bar || atf_fail "bar should exist, but does not" +} + +atf_test_case k_backticks_in_varexpand +k_backticks_in_varexpand_head() { + atf_set "descr" "Checks that old style cmd sub works in var expansion" +} +k_backticks_in_varexpand_body() { + atf_check -s exit:0 -o match:'foo' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X+`echo foo`}' + atf_check -s exit:0 -o match:'set' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X-`echo foo`}' + rm -f bar 2>/dev/null || : + atf_check -s exit:0 -o match:'set' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X-`echo foo > bar`}' + test -f bar && atf_fail "bar should not exist, but does" + atf_check -s exit:0 -o inline:'\n' -e empty \ + ${TEST_SH} -c 'X=set; echo ${X+`echo foo > bar`}' + test -f bar || atf_fail "bar should exist, but does not" +} + +atf_test_case l_arithmetic_in_cmdsub +l_arithmetic_in_cmdsub_head() { + atf_set "descr" "Checks that arithmetic works in cmd substitutions" +} +l_arithmetic_in_cmdsub_body() { + atf_check -s exit:0 -o inline:'1 + 1 = 2\n' -e empty \ + ${TEST_SH} -c 'echo 1 + 1 = $( echo $(( 1 + 1 )) )' + atf_check -s exit:0 -o inline:'X * Y = 6\n' -e empty \ + ${TEST_SH} -c 'X=2; Y=3; echo X \* Y = $( echo $(( X * Y )) )' + atf_check -s exit:0 -o inline:'Y % X = 1\n' -e empty \ + ${TEST_SH} -c 'X=2; Y=3; echo Y % X = $( echo $(( $Y % $X )) )' +} + +atf_test_case m_arithmetic_in_backticks +m_arithmetic_in_backticks_head() { + atf_set "descr" "Checks that arithmetic works in old style cmd sub" +} +m_arithmetic_in_backticks_body() { + atf_check -s exit:0 -o inline:'2 + 3 = 5\n' -e empty \ + ${TEST_SH} -c 'echo 2 + 3 = ` echo $(( 2 + 3 )) `' + atf_check -s exit:0 -o inline:'X * Y = 6\n' -e empty \ + ${TEST_SH} -c 'X=2; Y=3; echo X \* Y = ` echo $(( X * Y )) `' + atf_check -s exit:0 -o inline:'Y % X = 1\n' -e empty \ + ${TEST_SH} -c 'X=2; Y=3; echo Y % X = ` echo $(( $Y % $X )) `' +} + +atf_test_case n_cmdsub_in_arithmetic +n_cmdsub_in_arithmetic_head() { + atf_set "descr" "Tests uses of command substitutions in arithmetic" +} +n_cmdsub_in_arithmetic_body() { + atf_check -s exit:0 -o inline:'7\n' -e empty \ + ${TEST_SH} -c 'echo $(( $( echo 3 ) $( echo + ) $( echo 4 ) ))' + atf_check -s exit:0 -o inline:'11\n7\n18\n4\n1\n' -e empty \ + ${TEST_SH} -c \ + 'for op in + - \* / % + do + echo $(( $( echo 9 ) $( echo "${op}" ) $( echo 2 ) )) + done' +} + +atf_test_case o_backticks_in_arithmetic +o_backticks_in_arithmetic_head() { + atf_set "descr" "Tests old style cmd sub used in arithmetic" +} +o_backticks_in_arithmetic_body() { + atf_check -s exit:0 -o inline:'33\n' -e empty \ + ${TEST_SH} -c 'echo $(( `echo 77` `echo -` `echo 44`))' + atf_check -s exit:0 -o inline:'14\n8\n33\n3\n2\n' -e empty \ + ${TEST_SH} -c \ + 'for op in + - \* / % + do + echo $((`echo 11``echo "${op}"``echo 3`)) + done' +} + +atf_test_case p_cmdsub_in_heredoc +p_cmdsub_in_heredoc_head() { + atf_set "descr" "Checks that cmdsubs work inside a here document" +} +p_cmdsub_in_heredoc_body() { + atf_require_prog cat + + atf_check -s exit:0 -o inline:'line 1+1\nline 2\nline 3\n' -e empty \ + ${TEST_SH} -c \ + 'cat <<- EOF + $( echo line 1 )$( echo +1 ) + $( echo line 2;echo line 3 ) + EOF' +} + +atf_test_case q_backticks_in_heredoc +q_backticks_in_heredoc_head() { + atf_set "descr" "Checks that old style cmdsubs work in here docs" +} +q_backticks_in_heredoc_body() { + atf_require_prog cat + + atf_check -s exit:0 -o inline:'Mary had a\nlittle\nlamb\n' -e empty \ + ${TEST_SH} -c \ + 'cat <<- EOF + `echo Mary ` `echo had a ` + ` echo little; echo lamb ` + EOF' +} + +atf_test_case r_heredoc_in_cmdsub +r_heredoc_in_cmdsub_head() { + atf_set "descr" "Checks that here docs work inside cmd subs" +} +r_heredoc_in_cmdsub_body() { + atf_require_prog cat + + atf_check -s exit:0 -o inline:'Mary had a\nlittle\nlamb\n' -e empty \ + ${TEST_SH} -c 'echo "$( cat <<- \EOF + Mary had a + little + lamb + EOF + )"' + + atf_check -s exit:0 -e empty \ + -o inline:'Mary had 1\nlittle\nlamb\nMary had 4\nlittle\nlambs\n' \ + ${TEST_SH} -c 'for N in 1 4; do echo "$( cat <<- EOF + Mary had ${N} + little + lamb$( [ $N -gt 1 ] && echo s ) + EOF + )"; done' + + + atf_check -s exit:0 -o inline:'A Calculation:\n2 * 7 = 14\n' -e empty \ + ${TEST_SH} -c 'echo "$( cat <<- EOF + A Calculation: + 2 * 7 = $(( 2 * 7 )) + EOF + )"' +} + +atf_test_case s_heredoc_in_backticks +s_heredoc_in_backticks_head() { + atf_set "descr" "Checks that here docs work inside old style cmd subs" +} +s_heredoc_in_backticks_body() { + atf_require_prog cat + + atf_check -s exit:0 -o inline:'Mary had a little lamb\n' -e empty \ + ${TEST_SH} -c 'echo ` cat <<- \EOF + Mary had a + little + lamb + EOF + `' + + atf_check -s exit:0 -o inline:'A Calculation:\n17 / 3 = 5\n' -e empty \ + ${TEST_SH} -c 'echo "` cat <<- EOF + A Calculation: + 17 / 3 = $(( 17 / 3 )) + EOF + `"' +} + +atf_test_case t_nested_cmdsubs_in_heredoc +t_nested_cmdsubs_in_heredoc_head() { + atf_set "descr" "Checks nested command substitutions in here docs" +} +t_nested_cmdsubs_in_heredoc_body() { + atf_require_prog cat + atf_require_prog rm + + rm -f * 2>/dev/null || : + echo "Hello" > File + + atf_check -s exit:0 -o inline:'Hello U\nHelp me!\n' -e empty \ + ${TEST_SH} -c 'cat <<- EOF + $(cat File) U + $( V=$(cat File); echo "${V%lo}p" ) me! + EOF' + + rm -f * 2>/dev/null || : + echo V>V ; echo A>A; echo R>R + echo Value>VAR + + atf_check -s exit:0 -o inline:'$2.50\n' -e empty \ + ${TEST_SH} -c 'cat <<- EOF + $(Value='\''$2.50'\'';eval echo $(eval $(cat V)$(cat A)$(cat R)=\'\''\$$(cat $(cat V)$(cat A)$(cat R))\'\''; eval echo \$$(set -- *;echo ${3}${1}${2}))) + EOF' +} + +atf_test_case u_nested_backticks_in_heredoc +u_nested_backticks_in_heredoc_head() { + atf_set "descr" "Checks nested old style cmd subs in here docs" +} +u_nested_backticks_in_heredoc_body() { + atf_require_prog cat + atf_require_prog rm + + rm -f * 2>/dev/null || : + echo "Hello" > File + + atf_check -s exit:0 -o inline:'Hello U\nHelp me!\n' -e empty \ + ${TEST_SH} -c 'cat <<- EOF + `cat File` U + `V=\`cat File\`; echo "${V%lo}p" ` me! + EOF' + + rm -f * 2>/dev/null || : + echo V>V ; echo A>A; echo R>R + echo Value>VAR + + atf_check -s exit:0 -o inline:'$5.20\n' -e empty \ + ${TEST_SH} -c 'cat <<- EOF + `Value='\''$5.20'\'';eval echo \`eval \\\`cat V\\\`\\\`cat A\\\`\\\`cat R\\\`=\\\'\''\\\$\\\`cat \\\\\\\`cat V\\\\\\\`\\\\\\\`cat A\\\\\\\`\\\\\\\`cat R\\\\\\\`\\\`\\\'\''; eval echo \\\$\\\`set -- *;echo \\\\\${3}\\\\\${1}\\\\\${2}\\\`\`` + EOF' +} + +atf_test_case v_cmdsub_paren_tests +v_cmdsub__paren_tests_head() { + atf_set "descr" "tests with cmdsubs containing embedded ')'" +} +v_cmdsub_paren_tests_body() { + + # Tests from: + # http://www.in-ulm.de/~mascheck/various/cmd-subst/ + # (slightly modified.) + + atf_check -s exit:0 -o inline:'A.1\n' -e empty ${TEST_SH} -c \ + 'echo $( + case x in x) echo A.1;; esac + )' + + atf_check -s exit:0 -o inline:'A.2\n' -e empty ${TEST_SH} -c \ + 'echo $( + case x in x) echo A.2;; esac # comment + )' + + atf_check -s exit:0 -o inline:'A.3\n' -e empty ${TEST_SH} -c \ + 'echo $( + case x in (x) echo A.3;; esac + )' + + atf_check -s exit:0 -o inline:'A.4\n' -e empty ${TEST_SH} -c \ + 'echo $( + case x in (x) echo A.4;; esac # comment + )' + + atf_check -s exit:0 -o inline:'A.5\n' -e empty ${TEST_SH} -c \ + 'echo $( + case x in (x) echo A.5 + esac + )' + + atf_check -s exit:0 -o inline:'B: quoted )\n' -e empty ${TEST_SH} -c \ + 'echo $( + echo '\''B: quoted )'\'' + )' + + atf_check -s exit:0 -o inline:'C: comment then closing paren\n' \ + -e empty ${TEST_SH} -c \ + 'echo $( + echo C: comment then closing paren # ) + )' + + atf_check -s exit:0 -o inline:'D.1: here-doc with )\n' \ + -e empty ${TEST_SH} -c \ + 'echo $( + cat <<-\eof + D.1: here-doc with ) + eof + )' + + # D.2 is a bogus test. + + atf_check -s exit:0 -o inline:'D.3: here-doc with \()\n' \ + -e empty ${TEST_SH} -c \ + 'echo $( + cat <<-\eof + D.3: here-doc with \() + eof + )' + + atf_check -s exit:0 -e empty \ + -o inline:'E: here-doc terminated with a parenthesis ("academic")\n' \ + ${TEST_SH} -c \ + 'echo $( + cat <<-\) + E: here-doc terminated with a parenthesis ("academic") + ) + )' + + atf_check -s exit:0 -e empty \ +-o inline:'F.1: here-doc embed with unbal single, back- or doublequote '\''\n' \ + ${TEST_SH} -c \ + 'echo $( + cat <<-"eof" + F.1: here-doc embed with unbal single, back- or doublequote '\'' + eof + )' + atf_check -s exit:0 -e empty \ + -o inline:'F.2: here-doc embed with unbal single, back- or doublequote "\n' \ + ${TEST_SH} -c \ + 'echo $( + cat <<-"eof" + F.2: here-doc embed with unbal single, back- or doublequote " + eof + )' + atf_check -s exit:0 -e empty \ + -o inline:'F.3: here-doc embed with unbal single, back- or doublequote `\n' \ + ${TEST_SH} -c \ + 'echo $( + cat <<-"eof" + F.3: here-doc embed with unbal single, back- or doublequote ` + eof + )' + + atf_check -s exit:0 -e empty -o inline:'G: backslash at end of line\n' \ + ${TEST_SH} -c \ + 'echo $( + echo G: backslash at end of line # \ + )' + + atf_check -s exit:0 -e empty \ + -o inline:'H: empty command-substitution\n' \ + ${TEST_SH} -c 'echo H: empty command-substitution $( )' +} + +atf_test_case w_heredoc_outside_cmdsub +w_heredoc_outside_cmdsub_head() { + atf_set "descr" "Checks that here docs work inside cmd subs" +} +w_heredoc_outside_cmdsub_body() { + atf_require_prog cat + + atf_check -s exit:0 -o inline:'Mary had a\nlittle\nlamb\n' -e empty \ + ${TEST_SH} -c 'echo "$( cat <<- \EOF )" + Mary had a + little + lamb + EOF + ' + + atf_check -s exit:0 -e empty \ + -o inline:'Mary had 1\nlittle\nlamb\nMary had 4\nlittle\nlambs\n' \ + ${TEST_SH} -c 'for N in 1 4; do echo "$( cat <<- EOF )" + Mary had ${N} + little + lamb$( [ $N -gt 1 ] && echo s ) + EOF + done' + + + atf_check -s exit:0 -o inline:'A Calculation:\n2 * 7 = 14\n' -e empty \ + ${TEST_SH} -c 'echo "$( cat <<- EOF)" + A Calculation: + 2 * 7 = $(( 2 * 7 )) + EOF + ' +} + +atf_test_case x_heredoc_outside_backticks +x_heredoc_outside_backticks_head() { + atf_set "descr" "Checks that here docs work inside old style cmd subs" +} +x_heredoc_outside_backticks_body() { + atf_require_prog cat + + atf_check -s exit:0 -o inline:'Mary had a little lamb\n' -e empty \ + ${TEST_SH} -c 'echo ` cat <<- \EOF ` + Mary had a + little + lamb + EOF + ' + + atf_check -s exit:0 -o inline:'A Calculation:\n17 / 3 = 5\n' -e empty \ + ${TEST_SH} -c 'echo "` cat <<- EOF `" + A Calculation: + 17 / 3 = $(( 17 / 3 )) + EOF + ' +} + +atf_test_case t_nested_cmdsubs_in_heredoc +t_nested_cmdsubs_in_heredoc_head() { + atf_set "descr" "Checks nested command substitutions in here docs" +} +t_nested_cmdsubs_in_heredoc_body() { + atf_require_prog cat + atf_require_prog rm + + rm -f * 2>/dev/null || : + echo "Hello" > File + + atf_check -s exit:0 -o inline:'Hello U\nHelp me!\n' -e empty \ + ${TEST_SH} -c 'cat <<- EOF + $(cat File) U + $( V=$(cat File); echo "${V%lo}p" ) me! + EOF' + + rm -f * 2>/dev/null || : + echo V>V ; echo A>A; echo R>R + echo Value>VAR + + atf_check -s exit:0 -o inline:'$2.50\n' -e empty \ + ${TEST_SH} -c 'cat <<- EOF + $(Value='\''$2.50'\'';eval echo $(eval $(cat V)$(cat A)$(cat R)=\'\''\$$(cat $(cat V)$(cat A)$(cat R))\'\''; eval echo \$$(set -- *;echo ${3}${1}${2}))) + EOF' +} + +atf_test_case z_absurd_heredoc_cmdsub_combos +z_absurd_heredoc_cmdsub_combos_head() { + atf_set "descr" "perverse and unusual cmd substitutions & more" +} +z_absurd_heredoc_cmdsub_combos_body() { + + echo "Help!" > help + + # This version works in NetBSD (& FreeBSD)'s sh (and most others) + atf_check -s exit:0 -o inline:'Help!\nMe 2\n' -e empty ${TEST_SH} -c ' + cat <<- EOF + $( + cat <<- STOP + $( + cat `echo help` + ) + STOP + ) + $( + cat <<- END 4<<-TRASH + Me $(( 1 + 1 )) + END + This is unused noise! + TRASH + ) + EOF + ' + + # atf_expect_fail "PR bin/50993 - heredoc parsing done incorrectly" + atf_check -s exit:0 -o inline:'Help!\nMe 2\n' -e empty ${TEST_SH} -c ' + cat <<- EOF + $( + cat << STOP + $( + cat `echo help` + ) + STOP + ) + $( + cat <<- END 4<<TRASH + Me $(( 1 + 1 )) + END + This is unused noise! + TRASH + ) + EOF + ' +} + +atf_init_test_cases() { + atf_add_test_case a_basic_cmdsub + atf_add_test_case b_basic_backticks + atf_add_test_case c_nested_cmdsub + atf_add_test_case d_nested_backticks + atf_add_test_case e_perverse_mixing + atf_add_test_case f_redirect_in_cmdsub + atf_add_test_case g_redirect_in_backticks + atf_add_test_case h_vars_in_cmdsub + atf_add_test_case i_vars_in_backticks + atf_add_test_case j_cmdsub_in_varexpand + atf_add_test_case k_backticks_in_varexpand + atf_add_test_case l_arithmetic_in_cmdsub + atf_add_test_case m_arithmetic_in_backticks + atf_add_test_case n_cmdsub_in_arithmetic + atf_add_test_case o_backticks_in_arithmetic + atf_add_test_case p_cmdsub_in_heredoc + atf_add_test_case q_backticks_in_heredoc + atf_add_test_case r_heredoc_in_cmdsub + atf_add_test_case s_heredoc_in_backticks + atf_add_test_case t_nested_cmdsubs_in_heredoc + atf_add_test_case u_nested_backticks_in_heredoc + atf_add_test_case v_cmdsub_paren_tests + atf_add_test_case w_heredoc_outside_cmdsub + atf_add_test_case x_heredoc_outside_backticks + atf_add_test_case z_absurd_heredoc_cmdsub_combos +} diff --git a/contrib/netbsd-tests/bin/sh/t_compexit.sh b/contrib/netbsd-tests/bin/sh/t_compexit.sh deleted file mode 100755 index 019b740..0000000 --- a/contrib/netbsd-tests/bin/sh/t_compexit.sh +++ /dev/null @@ -1,63 +0,0 @@ -# $NetBSD: t_compexit.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ -# -# Copyright (c) 2007 The NetBSD Foundation, Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -# The standard -# http://www.opengroup.org/onlinepubs/007904975/utilities/set.html -# says: -# -# -e -# -# When this option is on, if a simple command fails for any of the -# reasons listed in Consequences of Shell Errors or returns an exit -# status value >0, and is not part of the compound list following a -# while, until, or if keyword, and is not a part of an AND or OR list, -# and is not a pipeline preceded by the ! reserved word, then the shell -# shall immediately exit. - -crud() { - set -e - for x in a - do - BAR="foo" - false && echo true - echo mumble - done -} - -atf_test_case set_e -set_e_head() { - atf_set "descr" "Tests that 'set -e' turns on error detection" \ - "and that it behaves as defined by the standard" -} -set_e_body() { - foo=`crud` - atf_check_equal 'x$foo' 'xmumble' -} - -atf_init_test_cases() { - atf_add_test_case set_e -} diff --git a/contrib/netbsd-tests/bin/sh/t_evaltested.sh b/contrib/netbsd-tests/bin/sh/t_evaltested.sh index e40f8bd..128a55c 100755 --- a/contrib/netbsd-tests/bin/sh/t_evaltested.sh +++ b/contrib/netbsd-tests/bin/sh/t_evaltested.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_evaltested.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: t_evaltested.sh,v 1.2 2016/03/27 14:50:01 christos Exp $ # # Copyright (c) 2011 The NetBSD Foundation, Inc. # All rights reserved. @@ -24,6 +24,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} atf_test_case evaltested @@ -43,7 +45,7 @@ fi echo "passed" exit 0 EOF - output="$(/bin/sh helper.sh)" + output="$($TEST_SH helper.sh)" [ $? = 0 ] && return if [ -n "$output" ] diff --git a/contrib/netbsd-tests/bin/sh/t_exit.sh b/contrib/netbsd-tests/bin/sh/t_exit.sh index 62c5869..17ed230 100755 --- a/contrib/netbsd-tests/bin/sh/t_exit.sh +++ b/contrib/netbsd-tests/bin/sh/t_exit.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_exit.sh,v 1.3 2012/04/13 06:12:32 jruoho Exp $ +# $NetBSD: t_exit.sh,v 1.6 2016/05/07 23:51:30 kre Exp $ # # Copyright (c) 2007 The NetBSD Foundation, Inc. # All rights reserved. @@ -24,74 +24,124 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} -crud() { - test yes = no - - cat <<EOF -$? -EOF -} atf_test_case background background_head() { atf_set "descr" "Tests that sh(1) sets '$?' properly when running " \ - "a command in the background (PR bin/46327)" + "a command in the background (PR bin/46327)" } background_body() { - atf_check -s exit:0 -o ignore -e ignore -x "true; true & echo $?" - atf_check -s exit:0 -o ignore -e ignore -x "false; true & echo $?" + atf_check -o match:0 -e empty ${TEST_SH} -c 'true; true & echo $?' + # atf_expect_fail "PR bin/46327" (now fixed?) + atf_check -o match:0 -e empty ${TEST_SH} -c 'false; true & echo $?' } atf_test_case function function_head() { - atf_set "descr" "Tests that \$? is correctly updated inside" \ - "a function" + atf_set "descr" "Tests that \$? is correctly updated inside " \ + "a function" } function_body() { - foo=`crud` - atf_check_equal 'x$foo' 'x1' + atf_check -s exit:0 -o match:STATUS=1-0 -e empty \ + ${TEST_SH} -c ' + crud() { + test yes = no + + cat <<-EOF + STATUS=$? + EOF + } + foo=$(crud) + echo "${foo}-$?" + ' } atf_test_case readout readout_head() { - atf_set "descr" "Tests that \$? is correctly updated in a" \ - "compound expression" + atf_set "descr" "Tests that \$? is correctly updated in a " \ + "compound expression" } readout_body() { - atf_check_equal '$( true && ! true | false; echo $? )' '0' + atf_check -s exit:0 -o match:0 -e empty \ + ${TEST_SH} -c 'true && ! true | false; echo $?' } atf_test_case trap_subshell trap_subshell_head() { - atf_set "descr" "Tests that the trap statement in a subshell" \ - "works when the subshell exits" + atf_set "descr" "Tests that the trap statement in a subshell " \ + "works when the subshell exits" } trap_subshell_body() { - atf_check -s eq:0 -o inline:'exiting\n' -x \ - '( trap "echo exiting" EXIT; /usr/bin/true )' + atf_check -s exit:0 -o inline:'exiting\n' -e empty \ + ${TEST_SH} -c '( trap "echo exiting" EXIT; /usr/bin/true )' } atf_test_case trap_zero__implicit_exit +trap_zero__implicit_exit_head() { + atf_set "descr" "Tests that the trap statement in a subshell in a " \ + "script works when the subshell simply runs out of commands" +} trap_zero__implicit_exit_body() { - # PR bin/6764: sh works but ksh does not" + # PR bin/6764: sh works but ksh does not echo '( trap "echo exiting" 0 )' >helper.sh - atf_check -s eq:0 -o match:exiting -e empty /bin/sh helper.sh - atf_check -s eq:0 -o match:exiting -e empty /bin/ksh helper.sh + atf_check -s exit:0 -o match:exiting -e empty ${TEST_SH} helper.sh + # test ksh by setting TEST_SH to /bin/ksh and run the entire set... + # atf_check -s exit:0 -o match:exiting -e empty /bin/ksh helper.sh } atf_test_case trap_zero__explicit_exit +trap_zero__explicit_exit_head() { + atf_set "descr" "Tests that the trap statement in a subshell in a " \ + "script works when the subshell executes an explicit exit" +} trap_zero__explicit_exit_body() { - echo '( trap "echo exiting" 0; exit )' >helper.sh - atf_check -s eq:0 -o match:exiting -e empty /bin/sh helper.sh - atf_check -s eq:0 -o match:exiting -e empty /bin/ksh helper.sh + echo '( trap "echo exiting" 0; exit; echo NO_NO_NO )' >helper.sh + atf_check -s exit:0 -o match:exiting -o not-match:NO_NO -e empty \ + ${TEST_SH} helper.sh + # test ksh by setting TEST_SH to /bin/ksh and run the entire set... + # atf_check -s exit:0 -o match:exiting -e empty /bin/ksh helper.sh } -atf_test_case trap_zero__explicit_return -trap_zero__explicit_return_body() { - echo '( trap "echo exiting" 0; return )' >helper.sh - atf_check -s eq:0 -o match:exiting -e empty /bin/sh helper.sh - atf_check -s eq:0 -o match:exiting -e empty /bin/ksh helper.sh +atf_test_case simple_exit +simple_exit_head() { + atf_set "descr" "Tests that various values for exit status work" +} +# Note: ATF will not allow tests of exit values > 255, even if they would work +simple_exit_body() { + for N in 0 1 2 3 4 5 6 42 99 101 125 126 127 128 129 200 254 255 + do + atf_check -s exit:$N -o empty -e empty \ + ${TEST_SH} -c "exit $N; echo FOO; echo BAR >&2" + done +} + +atf_test_case subshell_exit +subshell_exit_head() { + atf_set "descr" "Tests that subshell exit status works and \$? gets it" +} +# Note: ATF will not allow tests of exit values > 255, even if they would work +subshell_exit_body() { + for N in 0 1 2 3 4 5 6 42 99 101 125 126 127 128 129 200 254 255 + do + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -c "(exit $N); test \$? -eq $N" + done +} + +atf_test_case subshell_background +subshell_background_head() { + atf_set "descr" "Tests that sh(1) sets '$?' properly when running " \ + "a subshell in the background" +} +subshell_background_body() { + atf_check -o match:0 -e empty \ + ${TEST_SH} -c 'true; (false || true) & echo $?' + # atf_expect_fail "PR bin/46327" (now fixed?) + atf_check -o match:0 -e empty \ + ${TEST_SH} -c 'false; (false || true) & echo $?' } atf_init_test_cases() { @@ -101,5 +151,7 @@ atf_init_test_cases() { atf_add_test_case trap_subshell atf_add_test_case trap_zero__implicit_exit atf_add_test_case trap_zero__explicit_exit - atf_add_test_case trap_zero__explicit_return + atf_add_test_case simple_exit + atf_add_test_case subshell_exit + atf_add_test_case subshell_background } diff --git a/contrib/netbsd-tests/bin/sh/t_expand.sh b/contrib/netbsd-tests/bin/sh/t_expand.sh index eeaad5f..e785e1f 100755 --- a/contrib/netbsd-tests/bin/sh/t_expand.sh +++ b/contrib/netbsd-tests/bin/sh/t_expand.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_expand.sh,v 1.2 2013/10/06 21:05:50 ast Exp $ +# $NetBSD: t_expand.sh,v 1.8 2016/04/29 18:29:17 christos Exp $ # # Copyright (c) 2007, 2009 The NetBSD Foundation, Inc. # All rights reserved. @@ -24,6 +24,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} # # This file tests the functions in expand.c. @@ -50,19 +52,15 @@ dollar_at_head() { } dollar_at_body() { # This one should work everywhere. - got=`echo "" "" | sed 's,$,EOL,'` - atf_check_equal ' EOL' '$got' + atf_check -s exit:0 -o inline:' EOL\n' -e empty \ + ${TEST_SH} -c 'echo "" "" | '" sed 's,\$,EOL,'" # This code triggered the bug. - set -- "" "" - got=`echo "$@" | sed 's,$,EOL,'` - atf_check_equal ' EOL' '$got' + atf_check -s exit:0 -o inline:' EOL\n' -e empty \ + ${TEST_SH} -c 'set -- "" ""; echo "$@" | '" sed 's,\$,EOL,'" - set -- - - shift - n_arg() { echo $#; } - n_args=`n_arg "$@"` - atf_check_equal '0' '$n_args' + atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \ + 'set -- -; shift; n_arg() { echo $#; }; n_arg "$@"' } atf_test_case dollar_at_with_text @@ -71,15 +69,61 @@ dollar_at_with_text_head() { "within the quotes. PR bin/33956." } dollar_at_with_text_body() { - set -- - atf_check_equal '' "$(delim_argv "$@")" - atf_check_equal '>foobar<' "$(delim_argv "foo$@bar")" - atf_check_equal '>foo bar<' "$(delim_argv "foo $@ bar")" - set -- a b c - atf_check_equal '>a< >b< >c<' "$(delim_argv "$@")" - atf_check_equal '>fooa< >b< >cbar<' "$(delim_argv "foo$@bar")" - atf_check_equal '>foo a< >b< >c bar<' "$(delim_argv "foo $@ bar")" + cat <<'EOF' > h-f1 + +delim_argv() { + str= + while [ $# -gt 0 ]; do + if [ -z "${str}" ]; then + str=">$1<" + else + str="${str} >$1<" + fi + shift + done + echo "${str}" +} + +EOF + cat <<'EOF' > h-f2 + +delim_argv() { + str= + while [ $# -gt 0 ]; do + + str="${str}${str:+ }>$1<" + shift + + done + echo "${str}" +} + +EOF + + chmod +x h-f1 h-f2 + + for f in 1 2 + do + atf_check -s exit:0 -o inline:'\n' -e empty ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- ; delim_argv "$@"' + atf_check -s exit:0 -o inline:'>foobar<\n' -e empty \ + ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- ; delim_argv "foo$@bar"' + atf_check -s exit:0 -o inline:'>foo bar<\n' -e empty \ + ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- ; delim_argv "foo $@ bar"' + + atf_check -s exit:0 -o inline:'>a< >b< >c<\n' -e empty \ + ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- a b c; delim_argv "$@"' + atf_check -s exit:0 -o inline:'>fooa< >b< >cbar<\n' -e empty \ + ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- a b c; delim_argv "foo$@bar"' + atf_check -s exit:0 -o inline:'>foo a< >b< >c bar<\n' -e empty \ + ${TEST_SH} -c \ + ". ./h-f${f}; "'set -- a b c; delim_argv "foo $@ bar"' + done } atf_test_case strip @@ -91,8 +135,25 @@ strip_head() { strip_body() { line='#define bindir "/usr/bin" /* comment */' stripped='#define bindir "/usr/bin" ' - atf_expect_fail "PR bin/43469" - atf_check_equal '$stripped' '${line%%/\**}' + + # atf_expect_fail "PR bin/43469" -- now fixed + for exp in \ + '${line%%/\**}' \ + '${line%%"/*"*}' \ + '${line%%'"'"'/*'"'"'*}' \ + '"${line%%/\**}"' \ + '"${line%%"/*"*}"' \ + '"${line%%'"'"'/*'"'"'*}"' \ + '${line%/\**}' \ + '${line%"/*"*}' \ + '${line%'"'"'/*'"'"'*}' \ + '"${line%/\**}"' \ + '"${line%"/*"*}"' \ + '"${line%'"'"'/*'"'"'*}"' + do + atf_check -o inline:":$stripped:\n" -e empty ${TEST_SH} -c \ + "line='${line}'; echo :${exp}:" + done } atf_test_case varpattern_backslashes @@ -103,7 +164,8 @@ varpattern_backslashes_head() { varpattern_backslashes_body() { line='/foo/bar/*/baz' stripped='/foo/bar/' - atf_check_equal $stripped ${line%%\**} + atf_check -o inline:'/foo/bar/\n' -e empty ${TEST_SH} -c \ + 'line="/foo/bar/*/baz"; echo ${line%%\**}' } atf_test_case arithmetic @@ -114,9 +176,13 @@ arithmetic_head() { "this is true." } arithmetic_body() { - atf_check_equal '3' '$((1 + 2))' - atf_check_equal '2147483647' '$((0x7fffffff))' - atf_check_equal '9223372036854775807' '$(((1 << 63) - 1))' + + atf_check -o inline:'3' -e empty ${TEST_SH} -c \ + 'printf %s $((1 + 2))' + atf_check -o inline:'2147483647' -e empty ${TEST_SH} -c \ + 'printf %s $((0x7fffffff))' + atf_check -o inline:'9223372036854775807' -e empty ${TEST_SH} -c \ + 'printf %s $(((1 << 63) - 1))' } atf_test_case iteration_on_null_parameter @@ -126,10 +192,178 @@ iteration_on_null_parameter_head() { "PR bin/48202." } iteration_on_null_parameter_body() { - s1=`/bin/sh -uc 'N=; set -- ${N}; for X; do echo "[$X]"; done' 2>&1` - s2=`/bin/sh -uc 'N=; set -- ${N:-}; for X; do echo "[$X]"; done' 2>&1` - atf_check_equal '' '$s1' - atf_check_equal '[]' '$s2' + atf_check -o empty -e empty ${TEST_SH} -c \ + 'N=; set -- ${N}; for X; do echo "[$X]"; done' +} + +atf_test_case iteration_on_quoted_null_parameter +iteration_on_quoted_null_parameter_head() { + atf_set "descr" \ + 'Check iteration of "$@" in for loop when set to null;' +} +iteration_on_quoted_null_parameter_body() { + atf_check -o inline:'[]\n' -e empty ${TEST_SH} -c \ + 'N=; set -- "${N}"; for X; do echo "[$X]"; done' +} + +atf_test_case iteration_on_null_or_null_parameter +iteration_on_null_or_null_parameter_head() { + atf_set "descr" \ + 'Check expansion of null parameter as default for another null' +} +iteration_on_null_or_null_parameter_body() { + atf_check -o empty -e empty ${TEST_SH} -c \ + 'N=; E=; set -- ${N:-${E}}; for X; do echo "[$X]"; done' +} + +atf_test_case iteration_on_null_or_missing_parameter +iteration_on_null_or_missing_parameter_head() { + atf_set "descr" \ + 'Check expansion of missing parameter as default for another null' +} +iteration_on_null_or_missing_parameter_body() { + # atf_expect_fail 'PR bin/50834' + atf_check -o empty -e empty ${TEST_SH} -c \ + 'N=; set -- ${N:-}; for X; do echo "[$X]"; done' +} + +nl=' +' +reset() +{ + TEST_NUM=0 + TEST_FAILURES='' + TEST_FAIL_COUNT=0 + TEST_ID="$1" +} + +check() +{ + fail=false + TEMP_FILE=$( mktemp OUT.XXXXXX ) + TEST_NUM=$(( $TEST_NUM + 1 )) + MSG= + + # our local shell (ATF_SHELL) better do quoting correctly... + # some of the tests expect us to expand $nl internally... + CMD="$1" + + result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" )" + STATUS=$? + + if [ "${STATUS}" -ne "$3" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} expected exit code $3, got ${STATUS}" + + # don't actually fail just because of wrong exit code + # unless we either expected, or received "good" + case "$3/${STATUS}" in + (*/0|0/*) fail=true;; + esac + fi + + if [ "$3" -eq 0 ]; then + if [ -s "${TEMP_FILE}" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Messages produced on stderr unexpected..." + MSG="${MSG}${nl}$( cat "${TEMP_FILE}" )" + fail=true + fi + else + if ! [ -s "${TEMP_FILE}" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Expected messages on stderr," + MSG="${MSG} nothing produced" + fail=true + fi + fi + rm -f "${TEMP_FILE}" + + # Remove newlines (use local shell for this) + oifs="$IFS" + IFS="$nl" + result="$(echo $result)" + IFS="$oifs" + if [ "$2" != "$result" ] + then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Expected output '$2', received '$result'" + fail=true + fi + + if $fail + then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Full command: <<${CMD}>>" + fi + + $fail && test -n "$TEST_ID" && { + TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+${nl}}" + TEST_FAILURES="${TEST_FAILURES}${TEST_ID}[$TEST_NUM]:" + TEST_FAILURES="${TEST_FAILURES} Test of '$1' failed."; + TEST_FAILURES="${TEST_FAILURES}${nl}${MSG}" + TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 )) + return 0 + } + $fail && atf_fail "Test[$TEST_NUM] of '$1' failed${nl}${MSG}" + return 0 +} + +results() +{ + test -z "${TEST_ID}" && return 0 + test -z "${TEST_FAILURES}" && return 0 + + echo >&2 "==========================================" + echo >&2 "While testing '${TEST_ID}'" + echo >&2 " - - - - - - - - - - - - - - - - -" + echo >&2 "${TEST_FAILURES}" + atf_fail \ + "Test ${TEST_ID}: $TEST_FAIL_COUNT subtests (of $TEST_NUM) failed - see stderr" +} + +atf_test_case shell_params +shell_params_head() { + atf_set "descr" "Test correct operation of the numeric parameters" +} +shell_params_body() { + atf_require_prog mktemp + + reset shell_params + + check 'set -- a b c; echo "$#: $1 $2 $3"' '3: a b c' 0 + check 'set -- a b c d e f g h i j k l m; echo "$#: ${1}0 ${10} $10"' \ + '13: a0 j a0' 0 + check 'x="$0"; set -- a b; y="$0"; + [ "x${x}y" = "x${y}y" ] && echo OK || echo x="$x" y="$y"' \ + 'OK' 0 + check "${TEST_SH} -c 'echo 0=\$0 1=\$1 2=\$2' a b c" '0=a 1=b 2=c' 0 + + echo 'echo 0="$0" 1="$1" 2="$2"' > helper.sh + check "${TEST_SH} helper.sh a b c" '0=helper.sh 1=a 2=b' 0 + + check 'set -- a bb ccc dddd eeeee ffffff ggggggg hhhhhhhh \ + iiiiiiiii jjjjjjjjjj kkkkkkkkkkk + echo "${#}: ${#1} ${#2} ${#3} ${#4} ... ${#9} ${#10} ${#11}"' \ + '11: 1 2 3 4 ... 9 10 11' 0 + + check 'set -- a b c; echo "$#: ${1-A} ${2-B} ${3-C} ${4-D} ${5-E}"' \ + '3: a b c D E' 0 + check 'set -- a "" c "" e + echo "$#: ${1:-A} ${2:-B} ${3:-C} ${4:-D} ${5:-E}"' \ + '5: a B c D e' 0 + check 'set -- a "" c "" e + echo "$#: ${1:+A} ${2:+B} ${3:+C} ${4:+D} ${5:+E}"' \ + '5: A C E' 0 + check 'set -- "abab*cbb" + echo "${1} ${1#a} ${1%b} ${1##ab} ${1%%b} ${1#*\*} ${1%\**}"' \ + 'abab*cbb bab*cbb abab*cb ab*cbb abab*cb cbb abab' 0 + check 'set -- "abab?cbb" + echo "${1}:${1#*a}+${1%b*}-${1##*a}_${1%%b*}%${1#[ab]}=${1%?*}/${1%\?*}"' \ + 'abab?cbb:bab?cbb+abab?cb-b?cbb_a%bab?cbb=abab?cb/abab' 0 + check 'set -- a "" c "" e; echo "${2:=b}"' '' 1 + + results } atf_init_test_cases() { @@ -139,4 +373,8 @@ atf_init_test_cases() { atf_add_test_case varpattern_backslashes atf_add_test_case arithmetic atf_add_test_case iteration_on_null_parameter + atf_add_test_case iteration_on_quoted_null_parameter + atf_add_test_case iteration_on_null_or_null_parameter + atf_add_test_case iteration_on_null_or_missing_parameter + atf_add_test_case shell_params } diff --git a/contrib/netbsd-tests/bin/sh/t_fsplit.sh b/contrib/netbsd-tests/bin/sh/t_fsplit.sh index 2c3dbae..a37804b 100755 --- a/contrib/netbsd-tests/bin/sh/t_fsplit.sh +++ b/contrib/netbsd-tests/bin/sh/t_fsplit.sh @@ -1,6 +1,6 @@ -# $NetBSD: t_fsplit.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: t_fsplit.sh,v 1.4 2016/03/27 14:50:01 christos Exp $ # -# Copyright (c) 2007 The NetBSD Foundation, Inc. +# Copyright (c) 2007-2016 The NetBSD Foundation, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,22 +33,51 @@ # the "${x-" and "}" were absent from the input line. # # So: sh -c 'set ${x-a b c}; echo $#' should give 3. +# and: sh -c 'set -- ${x-}' echo $#' shold give 0 # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + nl=' ' check() { - result="$(eval $1)" + TEST=$((${TEST} + 1)) + + case "$#" in + (2) ;; + (*) atf_fail "Internal test error, $# args to check test ${TEST}";; + esac + + result=$( ${TEST_SH} -c "unset x; $1" ) + STATUS="$?" + # Remove newlines oifs="$IFS" IFS="$nl" result="$(echo $result)" IFS="$oifs" + + # trim the test text in case we use it in a message below + case "$1" in + ????????????????*) + set -- "$(expr "$1" : '\(............\).*')..." "$2" ;; + esac + if [ "$2" != "$result" ] then - atf_fail "expected [$2], found [$result]" + if [ "${STATUS}" = "0" ] + then + atf_fail "Test ${TEST} '$1': expected [$2], found [$result]" + else + atf_fail \ + "TEST ${TEST} '$1' failed ($STATUS): expected [$2], found [$result]" + fi + elif [ "${STATUS}" != 0 ] + then + atf_fail "TEST ${TEST} '$1' failed ($STATUS)" fi } @@ -59,6 +88,7 @@ for_head() { for_body() { unset x + TEST=0 # Since I managed to break this, leave the test in check 'for f in $x; do echo x${f}y; done' '' } @@ -68,17 +98,121 @@ default_val_head() { atf_set "descr" "Checks field splitting in variable default values" } default_val_body() { - unset x - + TEST=0 # Check that IFS is applied to text from ${x-...} unless it is inside # any set of "..." - check 'set ${x-a b c}; echo $#' 3 - check 'for i in ${x-a b c}; do echo "z${i}z"; done' 'zaz zbz zcz' - check 'for i in ${x-"a b" c}; do echo "z${i}z"; done' 'za bz zcz' - check 'for i in ${x-"a ${x-b c}" d}; do echo "z${i}z"; done' 'za b cz zdz' - check 'for i in ${x-"a ${x-"b c"}" d}; do echo "z${i}z"; done' 'za b cz zdz' - check 'for i in ${x-a ${x-"b c"} d}; do echo "z${i}z"; done' 'zaz zb cz zdz' - check 'for i in ${x-a ${x-b c} d}; do echo "z${i}z"; done' 'zaz zbz zcz zdz' + check 'set -- ${x-a b c}; echo $#' 3 + + check 'set -- ${x-"a b" c}; echo $#' 2 + check 'set -- ${x-a "b c"}; echo $#' 2 + check 'set -- ${x-"a b c"}; echo $#' 1 + + check "set -- \${x-'a b' c}; echo \$#" 2 + check "set -- \${x-a 'b c'}; echo \$#" 2 + check "set -- \${x-'a b c'}; echo \$#" 1 + + check 'set -- ${x-a\ b c}; echo $#' 2 + check 'set -- ${x-a b\ c}; echo $#' 2 + check 'set -- ${x-a\ b\ c}; echo $#' 1 + + check 'set -- ${x}; echo $#' 0 + check 'set -- ${x-}; echo $#' 0 + check 'set -- ${x-""}; echo $#' 1 + check 'set -- ""${x}; echo $#' 1 + check 'set -- ""${x-}; echo $#' 1 + check 'set -- ""${x-""}; echo $#' 1 + check 'set -- ${x}""; echo $#' 1 + check 'set -- ${x-}""; echo $#' 1 + check 'set -- ${x-""}""; echo $#' 1 + check 'set -- ""${x}""; echo $#' 1 + check 'set -- ""${x-}""; echo $#' 1 + check 'set -- ""${x-""}""; echo $#' 1 + + check 'for i in ${x-a b c}; do echo "z${i}z"; done' \ + 'zaz zbz zcz' + check 'for i in ${x-"a b" c}; do echo "z${i}z"; done' \ + 'za bz zcz' + check 'for i in ${x-"a ${x-b c}" d}; do echo "z${i}z"; done' \ + 'za b cz zdz' + check 'for i in ${x-a ${x-b c} d}; do echo "z${i}z"; done' \ + 'zaz zbz zcz zdz' + + # I am not sure these two are correct, the rules on quoting word + # in ${var-word} are peculiar, and hard to fathom... + # They are what the NetBSD shell does, and bash, not the freebsd shell + # (as of Mar 1, 2016) + + check 'for i in ${x-"a ${x-"b c"}" d}; do echo "z${i}z"; done' \ + 'za b cz zdz' + check 'for i in ${x-a ${x-"b c"} d}; do echo "z${i}z"; done' \ + 'zaz zb cz zdz' +} + +atf_test_case replacement_val +replacement_val_head() { + atf_set "descr" "Checks field splitting in variable replacement values" +} +replacement_val_body() { + TEST=0 + + # Check that IFS is applied to text from ${x+...} unless it is inside + # any set of "...", or whole expansion is quoted, or both... + + check 'x=BOGUS; set -- ${x+a b c}; echo $#' 3 + + check 'x=BOGUS; set -- ${x+"a b" c}; echo $#' 2 + check 'x=BOGUS; set -- ${x+a "b c"}; echo $#' 2 + check 'x=BOGUS; set -- ${x+"a b c"}; echo $#' 1 + + check "x=BOGUS; set -- \${x+'a b' c}; echo \$#" 2 + check "x=BOGUS; set -- \${x+a 'b c'}; echo \$#" 2 + check "x=BOGUS; set -- \${x+'a b c'}; echo \$#" 1 + + check 'x=BOGUS; set -- ${x+a\ b c}; echo $#' 2 + check 'x=BOGUS; set -- ${x+a b\ c}; echo $#' 2 + check 'x=BOGUS; set -- ${x+a\ b\ c}; echo $#' 1 + + check 'x=BOGUS; set -- ${x+}; echo $#' 0 + check 'x=BOGUS; set -- ${x+""}; echo $#' 1 + check 'x=BOGUS; set -- ""${x+}; echo $#' 1 + check 'x=BOGUS; set -- ""${x+""}; echo $#' 1 + check 'x=BOGUS; set -- ${x+}""; echo $#' 1 + check 'x=BOGUS; set -- ${x+""}""; echo $#' 1 + check 'x=BOGUS; set -- ""${x+}""; echo $#' 1 + check 'x=BOGUS; set -- ""${x+""}""; echo $#' 1 + + # verify that the value of $x does not affecty the value of ${x+...} + check 'x=BOGUS; set -- ${x+}; echo X$1' X + check 'x=BOGUS; set -- ${x+""}; echo X$1' X + check 'x=BOGUS; set -- ""${x+}; echo X$1' X + check 'x=BOGUS; set -- ""${x+""}; echo X$1' X + check 'x=BOGUS; set -- ${x+}""; echo X$1' X + check 'x=BOGUS; set -- ${x+""}""; echo X$1' X + check 'x=BOGUS; set -- ""${x+}""; echo X$1' X + check 'x=BOGUS; set -- ""${x+""}""; echo X$1' X + + check 'x=BOGUS; set -- ${x+}; echo X${1-:}X' X:X + check 'x=BOGUS; set -- ${x+""}; echo X${1-:}X' XX + check 'x=BOGUS; set -- ""${x+}; echo X${1-:}X' XX + check 'x=BOGUS; set -- ""${x+""}; echo X${1-:}X' XX + check 'x=BOGUS; set -- ${x+}""; echo X${1-:}X' XX + check 'x=BOGUS; set -- ${x+""}""; echo X${1-:}X' XX + check 'x=BOGUS; set -- ""${x+}""; echo X${1-:}X' XX + check 'x=BOGUS; set -- ""${x+""}""; echo X${1-:}X' XX + + # and validate that the replacement can be used as expected + check 'x=BOGUS; for i in ${x+a b c}; do echo "z${i}z"; done'\ + 'zaz zbz zcz' + check 'x=BOGUS; for i in ${x+"a b" c}; do echo "z${i}z"; done'\ + 'za bz zcz' + check 'x=BOGUS; for i in ${x+"a ${x+b c}" d}; do echo "z${i}z"; done'\ + 'za b cz zdz' + check 'x=BOGUS; for i in ${x+"a ${x+"b c"}" d}; do echo "z${i}z"; done'\ + 'za b cz zdz' + check 'x=BOGUS; for i in ${x+a ${x+"b c"} d}; do echo "z${i}z"; done'\ + 'zaz zb cz zdz' + check 'x=BOGUS; for i in ${x+a ${x+b c} d}; do echo "z${i}z"; done'\ + 'zaz zbz zcz zdz' } atf_test_case ifs_alpha @@ -89,13 +223,19 @@ ifs_alpha_head() { ifs_alpha_body() { unset x + TEST=0 # repeat with an alphabetic in IFS check 'IFS=q; set ${x-aqbqc}; echo $#' 3 - check 'IFS=q; for i in ${x-aqbqc}; do echo "z${i}z"; done' 'zaz zbz zcz' - check 'IFS=q; for i in ${x-"aqb"qc}; do echo "z${i}z"; done' 'zaqbz zcz' - check 'IFS=q; for i in ${x-"aq${x-bqc}"qd}; do echo "z${i}z"; done' 'zaqbqcz zdz' - check 'IFS=q; for i in ${x-"aq${x-"bqc"}"qd}; do echo "z${i}z"; done' 'zaqbqcz zdz' - check 'IFS=q; for i in ${x-aq${x-"bqc"}qd}; do echo "z${i}z"; done' 'zaz zbqcz zdz' + check 'IFS=q; for i in ${x-aqbqc}; do echo "z${i}z"; done' \ + 'zaz zbz zcz' + check 'IFS=q; for i in ${x-"aqb"qc}; do echo "z${i}z"; done' \ + 'zaqbz zcz' + check 'IFS=q; for i in ${x-"aq${x-bqc}"qd}; do echo "z${i}z"; done' \ + 'zaqbqcz zdz' + check 'IFS=q; for i in ${x-"aq${x-"bqc"}"qd}; do echo "z${i}z"; done' \ + 'zaqbqcz zdz' + check 'IFS=q; for i in ${x-aq${x-"bqc"}qd}; do echo "z${i}z"; done' \ + 'zaz zbqcz zdz' } atf_test_case quote @@ -106,6 +246,7 @@ quote_head() { quote_body() { unset x + TEST=0 # Some quote propagation checks check 'set "${x-a b c}"; echo $#' 1 check 'set "${x-"a b" c}"; echo $1' 'a b c' @@ -120,19 +261,44 @@ dollar_at_head() { dollar_at_body() { unset x + TEST=0 # Check we get "$@" right - check 'set ""; for i; do echo "z${i}z"; done' 'zz' - check 'set ""; for i in "$@"; do echo "z${i}z"; done' 'zz' - check 'set "" ""; for i; do echo "z${i}z"; done' 'zz zz' - check 'set "" ""; for i in "$@"; do echo "z${i}z"; done' 'zz zz' - check 'set "" ""; for i in $@; do echo "z${i}z"; done' '' - check 'set "a b" c; for i; do echo "z${i}z"; done' 'za bz zcz' - check 'set "a b" c; for i in "$@"; do echo "z${i}z"; done' 'za bz zcz' - check 'set "a b" c; for i in $@; do echo "z${i}z"; done' 'zaz zbz zcz' - check 'set " a b " c; for i in "$@"; do echo "z${i}z"; done' 'z a b z zcz' - check 'set --; for i in x"$@"x; do echo "z${i}z"; done' 'zxxz' - check 'set a; for i in x"$@"x; do echo "z${i}z"; done' 'zxaxz' - check 'set a b; for i in x"$@"x; do echo "z${i}z"; done' 'zxaz zbxz' + + check 'set --; for i in x"$@"x; do echo "z${i}z"; done' 'zxxz' + check 'set a; for i in x"$@"x; do echo "z${i}z"; done' 'zxaxz' + check 'set a b; for i in x"$@"x; do echo "z${i}z"; done' 'zxaz zbxz' + + check 'set --; for i; do echo "z${i}z"; done' '' + check 'set --; for i in $@; do echo "z${i}z"; done' '' + check 'set --; for i in "$@"; do echo "z${i}z"; done' '' + # atf_expect_fail "PR bin/50834" + check 'set --; for i in ""$@; do echo "z${i}z"; done' 'zz' + # atf_expect_pass + check 'set --; for i in $@""; do echo "z${i}z"; done' 'zz' + check 'set --; for i in ""$@""; do echo "z${i}z"; done' 'zz' + check 'set --; for i in """$@"; do echo "z${i}z"; done' 'zz' + check 'set --; for i in "$@"""; do echo "z${i}z"; done' 'zz' + check 'set --; for i in """$@""";do echo "z${i}z"; done' 'zz' + + check 'set ""; for i; do echo "z${i}z"; done' 'zz' + check 'set ""; for i in "$@"; do echo "z${i}z"; done' 'zz' + check 'set "" ""; for i; do echo "z${i}z"; done' 'zz zz' + check 'set "" ""; for i in "$@"; do echo "z${i}z"; done' 'zz zz' + check 'set "" ""; for i in $@; do echo "z${i}z"; done' '' + + check 'set "a b" c; for i; do echo "z${i}z"; done' \ + 'za bz zcz' + check 'set "a b" c; for i in "$@"; do echo "z${i}z"; done' \ + 'za bz zcz' + check 'set "a b" c; for i in $@; do echo "z${i}z"; done' \ + 'zaz zbz zcz' + check 'set " a b " c; for i in "$@"; do echo "z${i}z"; done' \ + 'z a b z zcz' + + check 'set a b c; for i in "$@$@"; do echo "z${i}z"; done' \ + 'zaz zbz zcaz zbz zcz' + check 'set a b c; for i in "$@""$@";do echo "z${i}z"; done' \ + 'zaz zbz zcaz zbz zcz' } atf_test_case ifs @@ -143,6 +309,7 @@ ifs_head() { ifs_body() { unset x + TEST=0 # Some IFS tests check 't="-- "; IFS=" "; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '0' check 't=" x"; IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1' @@ -165,19 +332,26 @@ var_length_head() { "a variable's length" } var_length_body() { - unset x + TEST=0 - # Check that we apply IFS to ${#var} long=12345678123456781234567812345678 long=$long$long$long$long - check 'echo ${#long}; IFS=2; echo ${#long}; set 1 ${#long};echo $#' '128 1 8 3' - check 'IFS=2; set ${x-${#long}}; IFS=" "; echo $* $#' '1 8 2' - check 'IFS=2; set ${x-"${#long}"}; IFS=" "; echo $* $#' '128 1' + export long + + # first test that the test method works... + check 'set -u; : ${long}; echo ${#long}' '128' + + # Check that we apply IFS to ${#var} + check 'echo ${#long}; IFS=2; echo ${#long}; set 1 ${#long};echo $#' \ + '128 1 8 3' + check 'IFS=2; set ${x-${#long}}; IFS=" "; echo $* $#' '1 8 2' + check 'IFS=2; set ${x-"${#long}"}; IFS=" "; echo $* $#' '128 1' } atf_init_test_cases() { atf_add_test_case for atf_add_test_case default_val + atf_add_test_case replacement_val atf_add_test_case ifs_alpha atf_add_test_case quote atf_add_test_case dollar_at diff --git a/contrib/netbsd-tests/bin/sh/t_here.sh b/contrib/netbsd-tests/bin/sh/t_here.sh index 250c686..27307f5 100755 --- a/contrib/netbsd-tests/bin/sh/t_here.sh +++ b/contrib/netbsd-tests/bin/sh/t_here.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_here.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: t_here.sh,v 1.6 2016/03/31 16:21:52 christos Exp $ # # Copyright (c) 2007 The NetBSD Foundation, Inc. # All rights reserved. @@ -24,50 +24,542 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} nl=' ' +reset() +{ + TEST_NUM=0 + TEST_FAILURES='' + TEST_FAIL_COUNT=0 + TEST_ID="$1" +} + check() { - SVIFS="$IFS" - result="$(eval $1)" - # Remove newlines + fail=false + TEMP_FILE=$( mktemp OUT.XXXXXX ) + TEST_NUM=$(( $TEST_NUM + 1 )) + + # our local shell (ATF_SHELL) better do quoting correctly... + # some of the tests expect us to expand $nl internally... + CMD="nl='${nl}'; $1" + + result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" )" + STATUS=$? + + if [ "${STATUS}" -ne "$3" ]; then + echo >&2 "[$TEST_NUM] expected exit code $3, got ${STATUS}" + + # don't actually fail just because of wrong exit code + # unless we either expected, or received "good" + case "$3/${STATUS}" in + (*/0|0/*) fail=true;; + esac + fi + + if [ "$3" -eq 0 ]; then + if [ -s "${TEMP_FILE}" ]; then + echo >&2 \ + "[$TEST_NUM] Messages produced on stderr unexpected..." + cat "${TEMP_FILE}" >&2 + fail=true + fi + else + if ! [ -s "${TEMP_FILE}" ]; then + echo >&2 \ + "[$TEST_NUM] Expected messages on stderr, nothing produced" + fail=true + fi + fi + rm -f "${TEMP_FILE}" + + # Remove newlines (use local shell for this) oifs="$IFS" IFS="$nl" result="$(echo $result)" IFS="$oifs" if [ "$2" != "$result" ] then - atf_fail "expected [$2], found [$result]" + echo >&2 "[$TEST_NUM] Expected output '$2', received '$result'" + fail=true fi - IFS="$SVIFS" + + if $fail + then + echo >&2 "[$TEST_NUM] Full command: <<${CMD}>>" + fi + + $fail && test -n "$TEST_ID" && { + TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+ +}${TEST_ID}[$TEST_NUM]: test of '$1' failed"; + TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 )) + return 0 + } + $fail && atf_fail "Test[$TEST_NUM] of '$1' failed" + return 0 } -atf_test_case all -all_head() { +results() +{ + test -z "${TEST_ID}" && return 0 + test -z "${TEST_FAILURES}" && return 0 + + echo >&2 "==========================================" + echo >&2 "While testing '${TEST_ID}'" + echo >&2 " - - - - - - - - - - - - - - - - -" + echo >&2 "${TEST_FAILURES}" + atf_fail \ + "Test ${TEST_ID}: $TEST_FAIL_COUNT subtests (of $TEST_NUM) failed - see stderr" +} + +atf_test_case do_simple +do_simple_head() { atf_set "descr" "Basic tests for here documents" } -all_body() { +do_simple_body() { y=x - IFS= - check 'x=`cat <<EOF'$nl'text'${nl}EOF$nl'`; echo $x' 'text' - check 'x=`cat <<\EOF'$nl'text'${nl}EOF$nl'`; echo $x' 'text' + reset 'simple' + IFS=' ' + check 'x=`cat <<EOF'$nl'text'${nl}EOF$nl'`; echo $x' 'text' 0 + check 'x=`cat <<\EOF'$nl'text'${nl}EOF$nl'`; echo $x' 'text' 0 + + check "y=${y};"'x=`cat <<EOF'$nl'te${y}t'${nl}EOF$nl'`; echo $x' \ + 'text' 0 + check "y=${y};"'x=`cat <<\EOF'$nl'te${y}t'${nl}EOF$nl'`; echo $x' \ + 'te${y}t' 0 + check "y=${y};"'x=`cat <<"EOF"'$nl'te${y}t'${nl}EOF$nl'`; echo $x' \ + 'te${y}t' 0 + check "y=${y};"'x=`cat <<'"'EOF'"$nl'te${y}t'${nl}EOF$nl'`; echo $x' \ + 'te${y}t' 0 + + # check that quotes in the here doc survive and cause no problems + check "cat <<EOF${nl}te'xt${nl}EOF$nl" "te'xt" 0 + check "cat <<\EOF${nl}te'xt${nl}EOF$nl" "te'xt" 0 + check "cat <<'EOF'${nl}te'xt${nl}EOF$nl" "te'xt" 0 + check "cat <<EOF${nl}te\"xt${nl}EOF$nl" 'te"xt' 0 + check "cat <<\EOF${nl}te\"xt${nl}EOF$nl" 'te"xt' 0 + check "cat <<'EOF'${nl}te\"xt${nl}EOF$nl" 'te"xt' 0 + check "cat <<'EO'F${nl}te\"xt${nl}EOF$nl" 'te"xt' 0 + + check "y=${y};"'x=`cat <<EOF'$nl'te'"'"'${y}t'${nl}EOF$nl'`; echo $x' \ + 'te'"'"'xt' 0 + check "y=${y};"'x=`cat <<EOF'$nl'te'"''"'${y}t'${nl}EOF$nl'`; echo $x' \ + 'te'"''"'xt' 0 + + # note that the blocks of empty space in the following must + # be entirely tab characters, no spaces. + + check 'x=`cat <<EOF'"$nl text${nl}EOF$nl"'`; echo "$x"' \ + ' text' 0 + check 'x=`cat <<-EOF'"$nl text${nl}EOF$nl"'`; echo $x' \ + 'text' 0 + check 'x=`cat <<-EOF'"${nl}text${nl} EOF$nl"'`; echo $x' \ + 'text' 0 + check 'x=`cat <<-\EOF'"$nl text${nl} EOF$nl"'`; echo $x' \ + 'text' 0 + check 'x=`cat <<- "EOF"'"$nl text${nl}EOF$nl"'`; echo $x' \ + 'text' 0 + check 'x=`cat <<- '"'EOF'${nl}text${nl} EOF$nl"'`; echo $x' \ + 'text' 0 + results +} + +atf_test_case end_markers +end_markers_head() { + atf_set "descr" "Tests for various end markers of here documents" +} +end_markers_body() { + + reset 'end_markers' + for end in EOF 1 \! '$$$' "string " a\\\ a\\\ \ '&' '' ' ' ' ' \ + --STRING-- . '~~~' ')' '(' '#' '()' '(\)' '(\/)' '--' '\' '{' '}' \ +VERYVERYVERYVERYLONGLONGLONGin_fact_absurdly_LONG_LONG_HERE_DOCUMENT_TERMINATING_MARKER_THAT_goes_On_forever_and_ever_and_ever... + do + # check unquoted end markers + case "${end}" in + ('' | *[' ()\$&#*~']* ) ;; # skip unquoted endmark test for these + (*) check \ + 'x=$(cat << '"${end}${nl}text${nl}${end}${nl}"'); printf %s "$x"' 'text' 0 + ;; + esac + + # and quoted end markers + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${nl}"'); printf %s "$x"' 'text' 0 + + # and see what happens if we encounter "almost" an end marker + case "${#end}" in + (0|1) ;; # too short to try truncation tests + (*) check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end%?}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end%?}" 0 + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end#?}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end#?}" 0 + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end%?}+${nl}${end}${nl}"');printf %s "$x"' \ + "text ${end%?}+" 0 + ;; + esac + + # or something that is a little longer + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end}x${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end}x" 0 + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}!${end}${nl}${end}${nl}"'); printf %s "$x"' \ + "text !${end}" 0 + + # or which does not begin at start of line + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl} ${end}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end}" 0 + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl} ${end}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end}" 0 + + # or end at end of line + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end} ${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end} " 0 + + # or something that is correct much of the way, but then... + + case "${#end}" in + (0) ;; # cannot test this one + (1) check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${end}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end}${end}" 0 + ;; + (2-7) pfx="${end%?}" + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${pfx}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end}${pfx}" 0 + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${pfx}${end}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${pfx}${end}" 0 + ;; + (*) pfx=${end%??????}; sfx=${end#??????} + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${end}${sfx}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${end}${sfx}" 0 + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${pfx}${end}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${pfx}${end}" 0 + check \ + 'x=$(cat <<'"'${end}'${nl}text${nl}${pfx}${sfx}${nl}${end}${nl}"'); printf %s "$x"' \ + "text ${pfx}${sfx}" 0 + ;; + esac + done + + # Add striptabs tests (in similar way) here one day... + + results +} + +atf_test_case incomplete +incomplete_head() { + atf_set "descr" "Basic tests for incomplete here documents" +} +incomplete_body() { + reset incomplete + + check 'cat <<EOF' '' 2 + check 'cat <<- EOF' '' 2 + check 'cat <<\EOF' '' 2 + check 'cat <<- \EOF' '' 2 + + check 'cat <<EOF'"${nl}" '' 2 + check 'cat <<- EOF'"${nl}" '' 2 + check 'cat <<'"'EOF'${nl}" '' 2 + check 'cat <<- "EOF"'"${nl}" '' 2 + + check 'cat << EOF'"${nl}${nl}" '' 2 + check 'cat <<-EOF'"${nl}${nl}" '' 2 + check 'cat << '"'EOF'${nl}${nl}" '' 2 + check 'cat <<-"EOF"'"${nl}${nl}" '' 2 + + check 'cat << EOF'"${nl}"'line 1'"${nl}" '' 2 + check 'cat <<-EOF'"${nl}"' line 1'"${nl}" '' 2 + check 'cat << EOF'"${nl}"'line 1'"${nl}"' line 2'"${nl}" '' 2 + check 'cat <<-EOF'"${nl}"' line 1'"${nl}"'line 2'"${nl}" '' 2 + + check 'cat << EOF'"${nl}line 1${nl}${nl}line3${nl}${nl}5!${nl}" '' 2 + + results +} + +atf_test_case lineends +lineends_head() { + atf_set "descr" "Tests for line endings in here documents" +} +lineends_body() { + reset lineends + + # note that "check" removes newlines from stdout before comparing. + # (they become blanks, provided there is something before & after) + + check 'cat << \echo'"${nl}"'\'"${nl}echo${nl}echo${nl}" '\' 0 + check 'cat << echo'"${nl}"'\'"${nl}echo${nl}echo${nl}" 'echo' 0 + check 'cat << echo'"${nl}"'\\'"${nl}echo${nl}echo${nl}" '\' 0 + + check 'X=3; cat << ec\ho'"${nl}"'$X\'"${nl}echo${nl}echo${nl}" \ + '$X\' 0 + check 'X=3; cat << echo'"${nl}"'$X'"${nl}echo${nl}echo${nl}" \ + '3' 0 + check 'X=3; cat << echo'"${nl}"'$X\'"${nl}echo${nl}echo${nl}" \ + '' 0 + check 'X=3; cat << echo'"${nl}"'${X}\'"${nl}echo${nl}echo${nl}" \ + '3echo' 0 + check 'X=3; cat << echo'"${nl}"'\$X\'"${nl}echo${nl}echo${nl}" \ + '$Xecho' 0 + check 'X=3; cat << echo'"${nl}"'\\$X \'"${nl}echo${nl}echo${nl}" \ + '\3 echo' 0 + + check \ + 'cat << "echo"'"${nl}"'line1\'"${nl}"'line2\'"${nl}echo${nl}echo${nl}" \ + 'line1\ line2\' 0 + check \ + 'cat << echo'"${nl}"'line1\'"${nl}"'line2\'"${nl}echo${nl}echo${nl}" \ + 'line1line2echo' 0 + + results +} + +atf_test_case multiple +multiple_head() { + atf_set "descr" "Tests for multiple here documents on one cmd line" +} +multiple_body() { + reset multiple + + check \ + "(cat ; cat <&3) <<EOF0 3<<EOF3${nl}STDIN${nl}EOF0${nl}-3-${nl}EOF3${nl}" \ + 'STDIN -3-' 0 + + check "(read line; echo \"\$line\"; cat <<EOF1; echo \"\$line\") <<EOF2 +The File +EOF1 +The Line +EOF2 +" 'The Line The File The Line' 0 + + check "(read line; echo \"\$line\"; cat <<EOF; echo \"\$line\") <<EOF +The File +EOF +The Line +EOF +" 'The Line The File The Line' 0 + + check "V=1; W=2; cat <<-1; cat <<2; cat <<- 3; cat <<'4';"' cat <<\5 + $V + $W + 3 + 4 + 5 + 1 +2 + 5 + 4*$W+\$V + 3 +$W +1 +2 +3 +4 +7+$V +$W+6 +5 +' '1 2 3 4 5 5 4*2+$V $W 1 2 3 7+$V $W+6' 0 + + results +} + +atf_test_case nested +nested_head() { + atf_set "descr" "Tests for nested here documents for one cmd" +} +nested_body() { + reset nested + + check \ +'cat << EOF1'"${nl}"'$(cat << EOF2'"${nl}LINE${nl}EOF2${nl}"')'"${nl}EOF1${nl}"\ + 'LINE' 0 + +# This next one fails ... and correctly, so we will omit it (bad test) +# Reasoning is that the correct data "$(cat << EOF2)\nLINE\nEOF2\n" is +# collected for the outer (EOF1) heredoc, when that is parsed, it looks +# like +# $(cat <<EOF2) +# LINE +# EOF2 +# which looks like a good command - except it is being parsed in "heredoc" +# syntax, which means it is enclosed in double quotes, which means that +# the newline after the ')' in the first line is not a newline token, but +# just a character. The EOF2 heredoc cannot start until after the next +# newline token, of which there are none here... LINE and EOF2 are just +# more data in the outer EOF1 heredoc for its "cat" command to read & write. +# +# The previous sub-test works because there the \n comes inside the +# $( ), and in there, the outside quoting rules are suspended, and it +# all starts again - so that \n is a newline token, and the EOF2 heredoc +# is processed. +# +# check \ +# 'cat << EOF1'"${nl}"'$(cat << EOF2 )'"${nl}LINE${nl}EOF2${nl}EOF1${nl}" \ +# 'LINE' 0 + + L='cat << EOF1'"${nl}"'LINE1$(cat << EOF2'"${nl}" + L="${L}"'LINE2$(cat << EOF3'"${nl}" + L="${L}"'LINE3$(cat << EOF4'"${nl}" + L="${L}"'LINE4$(cat << EOF5'"${nl}" + L="${L}LINE5${nl}EOF5${nl})4${nl}EOF4${nl})3${nl}" + L="${L}EOF3${nl})2${nl}EOF2${nl})1${nl}EOF1${nl}" + + # That mess is ... + # + # cat <<EOF1 + # LINE1$(cat << EOF2 + # LINE2$(cat << EOF3 + # LINE3$(cat << EOF4 + # LINE4$(cat << EOF5 + # LINE5 + # EOF5 + # )4 + # EOF4 + # )3 + # EOF3 + # )2 + # EOF2 + # )1 + # EOF1 + + check "${L}" 'LINE1LINE2LINE3LINE4LINE54321' 0 + + results +} + +atf_test_case quoting +quoting_head() { + atf_set "descr" "Tests for use of quotes inside here documents" +} +quoting_body() { + reset quoting + + check 'X=!; cat <<- E\0F + <'\''"'\'' \\$X\$X "'\''" \\> + E0F + ' '<'\''"'\'' \\$X\$X "'\''" \\>' 0 + + check 'X=!; cat <<- E0F + <'\''"'\'' \\$X\$X "'\''" \\> + E0F + ' '<'\''"'\'' \!$X "'\''" \>' 0 + + check 'cat <<- END + $( echo "'\''" ) $( echo '\''"'\'' ) $( echo \\ ) + END + ' "' \" \\" 0 + + check 'X=12345; Y="string1 line1?-line2"; Z=; unset W; cat <<-EOF + ${#X}${Z:-${Y}}${W+junk}${Y%%l*}${Y#*\?} + "$Z"'\''$W'\'' ${Y%" "*} $(( X + 54321 )) + EOF + ' '5string1 line1?-line2string1 -line2 ""'\'\'' string1 66666' 0 + + results +} + +atf_test_case side_effects +side_effects_head() { + atf_set "descr" "Tests how side effects in here documents are handled" +} +side_effects_body() { + + atf_check -s exit:0 -o inline:'2\n1\n' -e empty ${TEST_SH} -c ' + unset X + cat <<-EOF + ${X=2} + EOF + echo "${X-1}" + ' +} + +atf_test_case vicious +vicious_head() { + atf_set "descr" "Tests for obscure and obnoxious uses of here docs" +} +vicious_body() { + reset + + cat <<- \END_SCRIPT > script + cat <<ONE && cat \ + <<TWO + a + ONE + b + TWO + END_SCRIPT + + atf_check -s exit:0 -o inline:'a\nb\n' -e empty ${TEST_SH} script + + # This next one is causing discussion currently (late Feb 2016) + # amongst stds writers & implementors. Consequently we + # will not check what it produces. The eventual result + # seems unlikely to be what we currently output, which + # is: + # A:echo line 1 + # B:echo line 2)" && prefix DASH_CODE <<DASH_CODE + # B:echo line 3 + # line 4 + # line 5 + # + # The likely intended output is ... + # + # A:echo line 3 + # B:echo line 1 + # line 2 + # DASH_CODE:echo line 4)" + # DASH_CODE:echo line 5 + # + # The difference is explained by differing opinions on just + # when processing of a here doc should start + + cat <<- \END_SCRIPT > script + prefix() { sed -e "s/^/$1:/"; } + DASH_CODE() { :; } - check 'x=`cat <<EOF'$nl'te${y}t'${nl}EOF$nl'`; echo $x' 'text' - check 'x=`cat <<\EOF'$nl'te${y}t'${nl}EOF$nl'`; echo $x' 'te${y}t' - check 'x=`cat <<"EOF"'$nl'te${y}t'${nl}EOF$nl'`; echo $x' 'te${y}t' - check 'x=`cat <<'"'EOF'"$nl'te${y}t'${nl}EOF$nl'`; echo $x' 'te${y}t' + prefix A <<XXX && echo "$(prefix B <<XXX + echo line 1 + XXX + echo line 2)" && prefix DASH_CODE <<DASH_CODE + echo line 3 + XXX + echo line 4)" + echo line 5 + DASH_CODE + END_SCRIPT - check 'x=`cat <<EOF'$nl'te'"'"'xt'${nl}EOF$nl'`; echo $x' 'te'"'"'xt' - check 'x=`cat <<\EOF'$nl'te'"'"'xt'${nl}EOF$nl'`; echo $x' 'te'"'"'xt' - check 'x=`cat <<"EOF"'$nl'te'"'"'xt'${nl}EOF$nl'`; echo $x' 'te'"'"'xt' + # we will just verify that the shell can parse the + # script somehow, and doesn't fall over completely... - check 'x=`cat <<EOF'$nl'te'"'"'${y}t'${nl}EOF$nl'`; echo $x' 'te'"'"'xt' - check 'x=`cat <<EOF'$nl'te'"''"'${y}t'${nl}EOF$nl'`; echo $x' 'te'"''"'xt' + atf_check -s exit:0 -o ignore -e empty ${TEST_SH} script } atf_init_test_cases() { - atf_add_test_case all + atf_add_test_case do_simple # not worthy of a comment + atf_add_test_case end_markers # the mundane, the weird, the bizarre + atf_add_test_case incomplete # where the end marker isn't... + atf_add_test_case lineends # test weird line endings in heredocs + atf_add_test_case multiple # multiple << operators on one cmd + atf_add_test_case nested # here docs inside here docs + atf_add_test_case quoting # stuff quoted inside + atf_add_test_case side_effects # here docs that modify environment + atf_add_test_case vicious # evil test from the austin-l list... } diff --git a/contrib/netbsd-tests/bin/sh/t_option.sh b/contrib/netbsd-tests/bin/sh/t_option.sh new file mode 100755 index 0000000..eae2110 --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_option.sh @@ -0,0 +1,674 @@ +# $NetBSD: t_option.sh,v 1.3 2016/03/08 14:19:28 christos Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# The standard +# http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html +# says: +# ...[lots] + +test_option_on_off() +{ + atf_require_prog tr + + for opt + do + # t is needed, as inside $()` $- appears to lose + # the 'e' option if it happened to already be + # set. Must check if that is what should + # happen, but that is a different issue. + + test -z "${opt}" && continue + + # if we are playing with more that one option at a + # time, the code below requires that we start with no + # options set, or it will mis-diagnose the situation + CLEAR='' + test "${#opt}" -gt 1 && + CLEAR='xx="$-" && xx=$(echo "$xx" | tr -d cs) && test -n "$xx" && set +"$xx";' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + "opt=${opt}"' + x() { + echo "ERROR: Unable to $1 option $2" >&2 + exit 1 + } + s() { + set -"$1" + t="$-" + x=$(echo "$t" | tr -d "$1") + test "$t" = "$x" && x set "$1" + return 0 + } + c() { + set +"$1" + t="$-" + x=$(echo "$t" | tr -d "$1") + test "$t" != "$x" && x clear "$1" + return 0 + } + '"${CLEAR}"' + + # if we do not do this, -x tracing splatters stderr + # for some shells, -v does as well (is that correct?) + case "${opt}" in + (*[xv]*) exec 2>/dev/null;; + esac + + o="$-" + x=$(echo "$o" | tr -d "$opt") + + if [ "$o" = "$x" ]; then # option was off + s "${opt}" + c "${opt}" + else + c "${opt}" + s "${opt}" + fi + ' + done +} + +test_optional_on_off() +{ + RET=0 + OPTS= + for opt + do + test "${opt}" = n && continue + ${TEST_SH} -c "set -${opt}" 2>/dev/null && + OPTS="${OPTS} ${opt}" || RET=1 + done + + test -n "${OPTS}" && test_option_on_off ${OPTS} + + return "${RET}" +} + +atf_test_case set_a +set_a_head() { + atf_set "descr" "Tests that 'set -a' turns on all var export " \ + "and that it behaves as defined by the standard" +} +set_a_body() { + atf_require_prog env + atf_require_prog grep + + test_option_on_off a + + # without -a, new variables should not be exported (so grep "fails") + atf_check -s exit:1 -o empty -e empty ${TEST_SH} -ce \ + 'unset VAR; set +a; VAR=value; env | grep "^VAR="' + + # with -a, they should be + atf_check -s exit:0 -o match:VAR=value -e empty ${TEST_SH} -ce \ + 'unset VAR; set -a; VAR=value; env | grep "^VAR="' +} + +atf_test_case set_C +set_C_head() { + atf_set "descr" "Tests that 'set -C' turns on no clobber mode " \ + "and that it behaves as defined by the standard" +} +set_C_body() { + atf_require_prog ls + + test_option_on_off C + + # Check that the environment to use for the tests is sane ... + # we assume current dir is a new tempory directory & is empty + + test -z "$(ls)" || atf_skip "Test execution directory not clean" + test -c "/dev/null" || atf_skip "Problem with /dev/null" + + echo Dummy_Content > Junk_File + echo Precious_Content > Important_File + + # Check that we can redirect onto file when -C is not set + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + ' + D=$(ls -l Junk_File) || exit 1 + set +C + echo "Overwrite it now" > Junk_File + A=$(ls -l Junk_File) || exit 1 + test "${A}" != "${D}" + ' + + # Check that we cannot redirect onto file when -C is set + atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} -c \ + ' + D=$(ls -l Important_File) || exit 1 + set -C + echo "Fail to Overwrite it now" > Important_File + A=$(ls -l Important_File) || exit 1 + test "${A}" = "${D}" + ' + + # Check that we can append to file, even when -C is set + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + ' + D=$(ls -l Junk_File) || exit 1 + set -C + echo "Append to it now" >> Junk_File + A=$(ls -l Junk_File) || exit 1 + test "${A}" != "${D}" + ' + + # Check that we abort on attempt to redirect onto file when -Ce is set + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + ' + set -Ce + echo "Fail to Overwrite it now" > Important_File + echo "Should not reach this point" + ' + + # Last check that we can override -C for when we really need to + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + ' + D=$(ls -l Junk_File) || exit 1 + set -C + echo "Change the poor bugger again" >| Junk_File + A=$(ls -l Junk_File) || exit 1 + test "${A}" != "${D}" + ' +} + +atf_test_case set_e +set_e_head() { + atf_set "descr" "Tests that 'set -e' turns on error detection " \ + "and that a simple case behaves as defined by the standard" +} +set_e_body() { + test_option_on_off e + + # Check that -e does nothing if no commands fail + atf_check -s exit:0 -o match:I_am_OK -e empty \ + ${TEST_SH} -c \ + 'false; printf "%s" I_am; set -e; true; printf "%s\n" _OK' + + # and that it (silently, but with exit status) aborts if cmd fails + atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \ + ${TEST_SH} -c \ + 'false; printf "%s" I_am; set -e; false; printf "%s\n" _Broken' + + # same, except -e this time is on from the beginning + atf_check -s not-exit:0 -o match:I_am -o not-match:Broken -e empty \ + ${TEST_SH} -ec 'printf "%s" I_am; false; printf "%s\n" _Broken' + + # More checking of -e in other places, there is lots to deal with. +} + +atf_test_case set_f +set_f_head() { + atf_set "descr" "Tests that 'set -f' turns off pathname expansion " \ + "and that it behaves as defined by the standard" +} +set_f_body() { + atf_require_prog ls + + test_option_on_off f + + # Check that the environment to use for the tests is sane ... + # we assume current dir is a new tempory directory & is empty + + test -z "$(ls)" || atf_skip "Test execution directory not clean" + + # we will assume that atf will clean up this junk directory + # when we are done. But for testing pathname expansion + # we need files + + for f in a b c d e f aa ab ac ad ae aaa aab aac aad aba abc bbb ccc + do + echo "$f" > "$f" + done + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \ + 'X=$(echo b*); Y=$(echo b*); test "${X}" != "a*"; + test "${X}" = "${Y}"' + + # now test expansion is different when -f is set + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -ec \ + 'X=$(echo b*); Y=$(set -f; echo b*); test "${X}" != "${Y}"' +} + +atf_test_case set_n +set_n_head() { + atf_set "descr" "Tests that 'set -n' supresses command execution " \ + "and that it behaves as defined by the standard" +} +set_n_body() { + # pointless to test this, if it turns on, it stays on... + # test_option_on_off n + # so just allow the tests below to verify it can be turned on + + # nothing should be executed, hence no output... + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -enc 'echo ABANDON HOPE; echo ALL YE; echo ...' + + # this is true even when the "commands" do not exist + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -enc 'ERR; FAIL; ABANDON HOPE' + + # but if there is a syntax error, it should be detected (w or w/o -e) + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -enc 'echo JUMP; for frogs swim; echo in puddles' + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -nc 'echo ABANDON HOPE; echo "ALL YE; echo ...' + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -enc 'echo ABANDON HOPE;; echo ALL YE; echo ...' + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -nc 'do YOU ABANDON HOPE; for all eternity?' + + # now test enabling -n in the middle of a script + # note that once turned on, it cannot be turned off again. + # + # omit more complex cases, as those can send some shells + # into infinite loops, and believe it or not, that might be OK! + + atf_check -s exit:0 -o match:first -o not-match:second -e empty \ + ${TEST_SH} -c 'echo first; set -n; echo second' + atf_check -s exit:0 -o match:first -o not-match:third -e empty \ + ${TEST_SH} -c 'echo first; set -n; echo second; set +n; echo third' + atf_check -s exit:0 -o inline:'a\nb\n' -e empty \ + ${TEST_SH} -c 'for x in a b c d + do + case "$x" in + a);; b);; c) set -n;; d);; + esac + printf "%s\n" "$x" + done' + + # This last one is a bit more complex to explain, so I will not try + + # First, we need to know what signal number is used for SIGUSR1 on + # the local (testing) system (signal number is $(( $XIT - 128 )) ) + + # this will take slightly over 1 second elapsed time (the sleep 1) + # The "10" for the first sleep just needs to be something big enough + # that the rest of the commands have time to complete, even on + # very slow testing systems. 10 should be enough. Otherwise irrelevant + + # The shell will usually blather to stderr about the sleep 10 being + # killed, but it affects nothing, so just allow it to cry. + + (sleep 10 & sleep 1; kill -USR1 $!; wait $!) + XIT="$?" + + # The exit value should be an integer > 128 and < 256 (often 158) + # If it is not just skip the test + + # If we do run the test, it should take (slightly over) either 1 or 2 + # seconds to complete, depending upon the shell being tested. + + case "${XIT}" in + ( 129 | 1[3-9][0-9] | 2[0-4][0-9] | 25[0-5] ) + + # The script below should exit with the same code - no output + + # Or that is the result that seems best explanable. + # "set -n" in uses like this is not exactly well defined... + + # This script comes from a member of the austin group + # (they author changes to the posix shell spec - and more.) + # The author is also an (occasional?) NetBSD user. + atf_check -s exit:${XIT} -o empty -e empty ${TEST_SH} -c ' + trap "set -n" USR1 + { sleep 1; kill -USR1 $$; sleep 1; } & + false + wait && echo t || echo f + wait + echo foo + ' + ;; + esac +} + +atf_test_case set_u +set_u_head() { + atf_set "descr" "Tests that 'set -u' turns on unset var detection " \ + "and that it behaves as defined by the standard" +} +set_u_body() { + test_option_on_off u + + # first make sure it is OK to unset an unset variable + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset _UNSET_VARIABLE_; echo OK' + # even if -u is set + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -cue \ + 'unset _UNSET_VARIABLE_; echo OK' + + # and that without -u accessing an unset variable is harmless + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset X; echo ${X}; echo OK' + # and that the unset variable test expansion works properly + atf_check -s exit:0 -o match:OKOK -e empty ${TEST_SH} -ce \ + 'unset X; printf "%s" ${X-OK}; echo OK' + + # Next test that with -u set, the shell aborts on access to unset var + # do not use -e, want to make sure it is -u that causes abort + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo ${X}; echo ERR' + # quoting should make no difference... + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo "${X}"; echo ERR' + + # Now a bunch of accesses to unset vars, with -u, in ways that are OK + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset X; set -u; echo ${X-GOOD}; echo OK' + atf_check -s exit:0 -o match:OK -e empty ${TEST_SH} -ce \ + 'unset X; set -u; echo ${X-OK}' + atf_check -s exit:0 -o not-match:ERR -o match:OK -e empty \ + ${TEST_SH} -ce 'unset X; set -u; echo ${X+ERR}; echo OK' + + # and some more ways that are not OK + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo ${X#foo}; echo ERR' + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; set -u; echo ${X%%bar}; echo ERR' + + # lastly, just while we are checking unset vars, test aborts w/o -u + atf_check -s not-exit:0 -o not-match:ERR -e not-empty ${TEST_SH} -c \ + 'unset X; echo ${X?}; echo ERR' + atf_check -s not-exit:0 -o not-match:ERR -e match:X_NOT_SET \ + ${TEST_SH} -c 'unset X; echo ${X?X_NOT_SET}; echo ERR' +} + +atf_test_case set_v +set_v_head() { + atf_set "descr" "Tests that 'set -v' turns on input read echoing " \ + "and that it behaves as defined by the standard" +} +set_v_body() { + test_option_on_off v + + # check that -v does nothing if no later input line is read + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e empty \ + ${TEST_SH} -ec 'printf "%s" OK; set -v; echo OK; exit 0' + + # but that it does when there are multiple lines + cat <<- 'EOF' | + set -v + printf %s OK + echo OK + exit 0 + EOF + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e match:echo \ + -e not-match:set ${TEST_SH} + + # and that it can be disabled again + cat <<- 'EOF' | + set -v + printf %s OK + set +v + echo OK + exit 0 + EOF + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e not-match:echo \ + ${TEST_SH} + + # and lastly, that shell keywords do get output when "read" + cat <<- 'EOF' | + set -v + for i in 111 222 333 + do + printf %s $i + done + exit 0 + EOF + atf_check -s exit:0 \ + -o match:111222333 -o not-match:printf \ + -o not-match:for -o not-match:do -o not-match:done \ + -e match:printf -e match:111 -e not-match:111222 \ + -e match:for -e match:do -e match:done \ + ${TEST_SH} +} + +atf_test_case set_x +set_x_head() { + atf_set "descr" "Tests that 'set -x' turns on command exec logging " \ + "and that it behaves as defined by the standard" +} +set_x_body() { + test_option_on_off x + + # check that cmd output appears after -x is enabled + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e not-match:printf -e match:OK -e match:echo \ + ${TEST_SH} -ec 'printf "%s" OK; set -x; echo OK; exit 0' + + # and that it stops again afer -x is disabled + atf_check -s exit:0 \ + -o match:OKOK -o not-match:echo -o not-match:printf \ + -e match:printf -e match:OK -e not-match:echo \ + ${TEST_SH} -ec 'set -x; printf "%s" OK; set +x; echo OK; exit 0' + + # also check that PS4 is output correctly + atf_check -s exit:0 \ + -o match:OK -o not-match:echo \ + -e match:OK -e match:Run:echo \ + ${TEST_SH} -ec 'PS4=Run:; set -x; echo OK; exit 0' + + return 0 + + # This one seems controversial... I suspect it is NetBSD's sh + # that is wrong to not output "for" "while" "if" ... etc + + # and lastly, that shell keywords do not get output when "executed" + atf_check -s exit:0 \ + -o match:111222333 -o not-match:printf \ + -o not-match:for \ + -e match:printf -e match:111 -e not-match:111222 \ + -e not-match:for -e not-match:do -e not-match:done \ + ${TEST_SH} -ec \ + 'set -x; for i in 111 222 333; do printf "%s" $i; done; echo; exit 0' +} + +opt_test_setup() +{ + test -n "$1" || { echo >&2 "Internal error"; exit 1; } + + cat > "$1" << 'END_OF_FUNCTIONS' +local_opt_check() +{ + local - +} + +instr() +{ + expr "$2" : "\(.*$1\)" >/dev/null +} + +save_opts() +{ + local - + + set -e + set -u + + instr e "$-" && instr u "$-" && return 0 + echo ERR +} + +fiddle_opts() +{ + set -e + set -u + + instr e "$-" && instr u "$-" && return 0 + echo ERR +} + +local_test() +{ + set +eu + + save_opts + instr '[eu]' "$-" || printf %s "OK" + + fiddle_opts + instr e "$-" && instr u "$-" && printf %s "OK" + + set +eu +} +END_OF_FUNCTIONS +} + +atf_test_case restore_local_opts +restore_local_opts_head() { + atf_set "descr" "Tests that 'local -' saves and restores options. " \ + "Note that "local" is a local shell addition" +} +restore_local_opts_body() { + atf_require_prog cat + atf_require_prog expr + + FN="test-funcs.$$" + opt_test_setup "${FN}" || atf_skip "Cannot setup test environment" + + ${TEST_SH} -ec ". './${FN}'; local_opt_check" 2>/dev/null || + atf_skip "sh extension 'local -' not supported by ${TEST_SH}" + + atf_check -s exit:0 -o match:OKOK -o not-match:ERR -e empty \ + ${TEST_SH} -ec ". './${FN}'; local_test" +} + +atf_test_case vi_emacs_VE_toggle +vi_emacs_VE_toggle_head() { + atf_set "descr" "Tests enabling vi disables emacs (and v.v - but why?)"\ + " Note that -V and -E are local shell additions" +} +vi_emacs_VE_toggle_body() { + + test_optional_on_off V E || + atf_skip "One or both V & E opts unsupported by ${TEST_SH}" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c ' + q() { + eval "case \"$-\" in + (*${2}*) return 1;; + (*${1}*) return 0;; + esac" + return 1 + } + x() { + echo >&2 "Option set or toggle failure:" \ + " on=$1 off=$2 set=$-" + exit 1 + } + set -V; q V E || x V E + set -E; q E V || x E V + set -V; q V E || x V E + set +EV; q "" "[VE]" || x "" VE + exit 0 + ' +} + +atf_test_case xx_bogus +xx_bogus_head() { + atf_set "descr" "Tests that attempting to set a nonsense option fails." +} +xx_bogus_body() { + # Biggest problem here is picking a "nonsense option" that is + # not implemented by any shell, anywhere. Hopefully this will do. + + # 'set' is a special builtin, so a conforming shell should exit + # on an arg error, and the ERR should not be printed. + atf_check -s not-exit:0 -o empty -e not-empty \ + ${TEST_SH} -c 'set -% ; echo ERR' +} + +atf_test_case Option_switching +Option_switching_head() { + atf_set "descr" "options can be enabled and disabled" +} +Option_switching_body() { + + # Cannot test -m, setting it causes test shell to fail... + # (test shell gets SIGKILL!) Wonder why ... something related to atf + # That is, it works if just run as "sh -c 'echo $-; set -m; echo $-'" + + # Don't bother testing toggling -n, once on, it stays on... + # (and because the test fn refuses to allow us to try) + + # Cannot test -o or -c here, or the extension -s + # they can only be used, not switched + + # these are the posix options, that all shells should implement + test_option_on_off a b C e f h u v x # m + + # and these are extensions that might not exist (non-fatal to test) + # -i and -s (and -c) are posix options, but are not required to + # be accessable via the "set" command, just the command line. + # We allow for -i to work with set, as that makes some sense, + # -c and -s do not. + test_optional_on_off E i I p q V || true + + # Also test (some) option combinations ... + # only testing posix options here, because it is easier... + test_option_on_off aeu vx Ca aCefux +} + +atf_init_test_cases() { + # tests are run in order sort of names produces, so choose names wisely + + # this one tests turning on/off all the mandatory. and extra flags + atf_add_test_case Option_switching + # and this tests the NetBSD "local -" functionality in functions. + atf_add_test_case restore_local_opts + + # no tests for -m (no idea how to do that one) + # -I (no easy way to generate the EOF it ignores) + # -i (not sure how to test that one at the minute) + # -p (because we aren't going to run tests setuid) + # -V/-E (too much effort, and a real test would be huge) + # -c (because almost all the other tests test it anyway) + # -q (because, for now, I am lazy) + # -s (coming soon, hopefully) + # -o (really +o: again, hopefully soon) + # -o longname (again, just laziness, don't wait...) + # -h/-b (because NetBSD doesn't implement them) + atf_add_test_case set_a + atf_add_test_case set_C + atf_add_test_case set_e + atf_add_test_case set_f + atf_add_test_case set_n + atf_add_test_case set_u + atf_add_test_case set_v + atf_add_test_case set_x + + atf_add_test_case vi_emacs_VE_toggle + atf_add_test_case xx_bogus +} diff --git a/contrib/netbsd-tests/bin/sh/t_redir.sh b/contrib/netbsd-tests/bin/sh/t_redir.sh new file mode 100755 index 0000000..580de88 --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_redir.sh @@ -0,0 +1,903 @@ +# $NetBSD: t_redir.sh,v 1.9 2016/05/14 00:33:02 kre Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# Any failures in this first test means it is not worth bothering looking +# for causes of failures in any other tests, make this one work first. + +# Problems with this test usually mean inadequate ATF_SHELL used for testing. +# (though if all pass but the last, it might be a TEST_SH problem.) + +atf_test_case basic_test_method_test +basic_test_method_test_head() +{ + atf_set "descr" "Tests that test method works as expected" +} +basic_test_method_test_body() +{ + cat <<- 'DONE' | + DONE + atf_check -s exit:0 -o empty -e empty ${TEST_SH} + cat <<- 'DONE' | + DONE + atf_check -s exit:0 -o match:0 -e empty ${TEST_SH} -c 'wc -l' + + cat <<- 'DONE' | + echo hello + DONE + atf_check -s exit:0 -o match:hello -e empty ${TEST_SH} + cat <<- 'DONE' | + echo hello + DONE + atf_check -s exit:0 -o match:1 -e empty ${TEST_SH} -c 'wc -l' + + cat <<- 'DONE' | + echo hello\ + world + DONE + atf_check -s exit:0 -o match:helloworld -e empty ${TEST_SH} + cat <<- 'DONE' | + echo hello\ + world + DONE + atf_check -s exit:0 -o match:2 -e empty ${TEST_SH} -c 'wc -l' + + printf '%s\n%s\n%s\n' Line1 Line2 Line3 > File + atf_check -s exit:0 -o inline:'Line1\nLine2\nLine3\n' -e empty \ + ${TEST_SH} -c 'cat File' + + cat <<- 'DONE' | + set -- X "" '' Y + echo ARGS="${#}" + echo '' -$1- -$2- -$3- -$4- + cat <<EOF + X=$1 + EOF + cat <<\EOF + Y=$4 + EOF + DONE + atf_check -s exit:0 -o match:ARGS=4 -o match:'-X- -- -- -Y-' \ + -o match:X=X -o match:'Y=\$4' -e empty ${TEST_SH} +} + +atf_test_case do_input_redirections +do_input_redirections_head() +{ + atf_set "descr" "Tests that simple input redirection works" +} +do_input_redirections_body() +{ + printf '%s\n%s\n%s\nEND\n' 'First Line' 'Second Line' 'Line 3' >File + + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} -c 'cat < File' + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} -c 'cat <File' + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} -c 'cat< File' + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} -c 'cat < "File"' + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} -c '< File cat' + + ln File wc + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} -c '< wc cat' + + mv wc cat + atf_check -s exit:0 -e empty -o match:4 \ + ${TEST_SH} -c '< cat wc' + + + cat <<- 'EOF' | + for l in 1 2 3; do + read line < File + echo "$line" + done + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nFirst Line\nFirst Line\n' \ + ${TEST_SH} + + cat <<- 'EOF' | + for l in 1 2 3; do + read line + echo "$line" + done <File + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\n' \ + ${TEST_SH} + + cat <<- 'EOF' | + for l in 1 2 3; do + read line < File + echo "$line" + done <File + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nFirst Line\nFirst Line\n' \ + ${TEST_SH} + + cat <<- 'EOF' | + line= + while [ "$line" != END ]; do + read line || exit 1 + echo "$line" + done <File + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} + + cat <<- 'EOF' | + while :; do + read line || exit 0 + echo "$line" + done <File + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\n' \ + ${TEST_SH} + + cat <<- 'EOF' | + l='' + while read line < File + do + echo "$line" + l="${l}x" + [ ${#l} -ge 3 ] && break + done + echo DONE + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nFirst Line\nFirst Line\nDONE\n' \ + ${TEST_SH} + + cat <<- 'EOF' | + while read line + do + echo "$line" + done <File + echo DONE + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nEND\nDONE\n' \ + ${TEST_SH} + + cat <<- 'EOF' | + l='' + while read line + do + echo "$line" + l="${l}x" + [ ${#l} -ge 3 ] && break + done <File + echo DONE + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line\nSecond Line\nLine 3\nDONE\n' ${TEST_SH} + + cat <<- 'EOF' | + l='' + while read line1 <File + do + read line2 + echo "$line1":"$line2" + l="${l}x" + [ ${#l} -ge 2 ] && break + done <File + echo DONE + EOF + atf_check -s exit:0 -e empty \ + -o inline:'First Line:First Line\nFirst Line:Second Line\nDONE\n' \ + ${TEST_SH} +} + +atf_test_case do_output_redirections +do_output_redirections_head() +{ + atf_set "descr" "Test Output redirections" +} +do_output_redirections_body() +{ +nl=' +' + T=0 + i() { T=$(expr "$T" + 1); } + + rm -f Output 2>/dev/null || : + test -f Output && atf_fail "Unable to remove Output file" +#1 + i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '> Output' + test -f Output || atf_fail "#$T: Did not make Output file" +#2 + rm -f Output 2>/dev/null || : + i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '>> Output' + test -f Output || atf_fail "#$T: Did not make Output file" +#3 + rm -f Output 2>/dev/null || : + i; atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c '>| Output' + test -f Output || atf_fail "#$T: Did not make Output file" + +#4 + rm -f Output 2>/dev/null || : + i + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'echo Hello >Output' + test -s Output || atf_fail "#$T: Did not make non-empty Output file" + test "$(cat Output)" = "Hello" || + atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'" +#5 + i + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'echo Hello>!Output' + test -s Output || atf_fail "#$T: Did not make non-empty Output file" + test "$(cat Output)" = "Hello" || + atf_fail "#$T: Incorrect Output: Should be 'Hello' is '$(cat Output)'" +#6 + i + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c 'echo Bye >>Output' + test -s Output || atf_fail "#$T: Removed Output file" + test "$(cat Output)" = "Hello${nl}Bye" || atf_fail \ + "#$T: Incorrect Output: Should be 'Hello\\nBye' is '$(cat Output)'" +#7 + i; atf_check -s exit:0 -o inline:'line 1\nline 2\n' -e empty \ + ${TEST_SH} -c \ + 'echo line 1 > Output; echo line 2 >> Output; cat Output' + test "$(cat Output)" = "line 1${nl}line 2" || atf_fail \ + "#$T: Incorrect Output: Should be 'line 1\\nline 2' is '$(cat Output)'" +#8 + i; atf_check -s exit:0 -o inline:'line 2\n' -e empty \ + ${TEST_SH} -c 'echo line 1 > Output; echo line 2' + test "$(cat Output)" = "line 1" || atf_fail \ + "#$T: Incorrect Output: Should be 'line 1' is '$(cat Output)'" +#9 + i; atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -c '(echo line 1; echo line 2 > Out2) > Out1' + test "$(cat Out1)" = "line 1" || atf_fail \ + "#$T: Incorrect Out1: Should be 'line 1' is '$(cat Out1)'" + test "$(cat Out2)" = "line 2" || atf_fail \ + "#$T: Incorrect Out2: Should be 'line 2' is '$(cat Out2)'" +#10 + i; atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -c '{ echo line 1; echo line 2 > Out2;} > Out1' + test "$(cat Out1)" = "line 1" || atf_fail \ + "#$T: Incorrect Out1: Should be 'line 1' is '$(cat Out1)'" + test "$(cat Out2)" = "line 2" || atf_fail \ + "#$T: Incorrect Out2: Should be 'line 2' is '$(cat Out2)'" +#11 + i; rm -f Out1 Out2 2>/dev/null || : + cat <<- 'EOF' | + for arg in 'line 1' 'line 2' 'line 3' + do + echo "$arg" + echo "$arg" > Out1 + done > Out2 + EOF + atf_check -s exit:0 -o empty -e empty ${TEST_SH} + test "$(cat Out1)" = "line 3" || atf_fail \ + "#$T: Incorrect Out1: Should be 'line 3' is '$(cat Out1)'" + test "$(cat Out2)" = "line 1${nl}line 2${nl}line 3" || atf_fail \ + "#$T: Incorrect Out2: Should be 'line 1\\nline 2\\nline 3' is '$(cat Out2)'" +#12 + i; rm -f Out1 Out2 2>/dev/null || : + cat <<- 'EOF' | + for arg in 'line 1' 'line 2' 'line 3' + do + echo "$arg" + echo "$arg" >> Out1 + done > Out2 + EOF + atf_check -s exit:0 -o empty -e empty ${TEST_SH} + test "$(cat Out1)" = "line 1${nl}line 2${nl}line 3" || atf_fail \ + "#$T: Incorrect Out1: Should be 'line 1\\nline 2\\nline 3' is '$(cat Out1)'" + test "$(cat Out2)" = "line 1${nl}line 2${nl}line 3" || atf_fail \ + "#$T: Incorrect Out2: Should be 'line 1\\nline 2\\nline 3' is '$(cat Out2)'" +} + +atf_test_case fd_redirections +fd_redirections_head() +{ + atf_set "descr" "Tests redirections to/from specific descriptors" +} +fd_redirections_body() +{ + atf_require_prog /bin/echo + + cat <<- 'DONE' > helper.sh + f() { + /bin/echo nothing "$1" >& "$1" + } + for n + do + eval "f $n $n"'> file-$n' + done + DONE + cat <<- 'DONE' > reread.sh + f() { + (read -r var; echo "${var}") <&"$1" + } + for n + do + x=$( eval "f $n $n"'< file-$n' ) + test "${x}" = "nothing $n" || echo "$n" + done + DONE + + validate() + { + for n + do + test -e "file-$n" || atf_fail "file-$n not created" + C=$(cat file-"$n") + test "$C" = "nothing $n" || + atf_fail "file-$n contains '$C' not 'nothing $n'" + done + } + + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} helper.sh 1 2 3 4 5 6 7 8 9 + validate 1 2 3 4 5 6 7 8 9 + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} reread.sh 3 4 5 6 7 8 9 + + L=$(ulimit -n) + if [ "$L" -ge 30 ] + then + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} helper.sh 10 15 19 20 25 29 + validate 10 15 19 20 25 29 + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} reread.sh 10 15 19 20 25 29 + fi + if [ "$L" -ge 100 ] + then + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} helper.sh 32 33 49 50 51 63 64 65 77 88 99 + validate 32 33 49 50 51 63 64 65 77 88 99 + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} reread.sh 32 33 49 50 51 63 64 65 77 88 99 + fi + if [ "$L" -ge 500 ] + then + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} helper.sh 100 101 199 200 222 333 444 499 + validate 100 101 199 200 222 333 444 499 + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} reread.sh 100 101 199 200 222 333 444 499 + fi + if [ "$L" -gt 1005 ] + then + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} helper.sh 1000 1001 1002 1003 1004 1005 + validate 1000 1001 1002 1003 1004 1005 + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} reread.sh 1000 1001 1002 1003 1004 1005 + fi +} + +atf_test_case local_redirections +local_redirections_head() +{ + atf_set "descr" \ + "Tests that exec can reassign file descriptors in the shell itself" +} +local_redirections_body() +{ + cat <<- 'DONE' > helper.sh + for f + do + eval "exec $f"'> file-$f' + done + + for f + do + printf '%s\n' "Hello $f" >&"$f" + done + + for f + do + eval "exec $f"'>&-' + done + + for f + do + eval "exec $f"'< file-$f' + done + + for f + do + exec <& "$f" + read -r var || echo >&2 "No data in file-$f" + read -r x && echo >&2 "Too much data in file-${f}: $x" + test "${var}" = "Hello $f" || + echo >&2 "file-$f contains '${var}' not 'Hello $f'" + done + DONE + + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} helper.sh 3 4 5 6 7 8 9 + + L=$(ulimit -n) + if [ "$L" -ge 30 ] + then + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} helper.sh 10 11 13 15 16 19 20 28 29 + fi + if [ "$L" -ge 100 ] + then + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} helper.sh 30 31 32 63 64 65 77 88 99 + fi + if [ "$L" -ge 500 ] + then + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} helper.sh 100 101 111 199 200 201 222 333 499 + fi + if [ "$L" -ge 1005 ] + then + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} helper.sh 1000 1001 1002 1003 1004 1005 + fi +} + +atf_test_case named_fd_redirections +named_fd_redirections_head() +{ + atf_set "descr" "Tests redirections to /dev/stdout (etc)" + +} +named_fd_redirections_body() +{ + if test -c /dev/stdout + then + atf_check -s exit:0 -o inline:'OK\n' -e empty \ + ${TEST_SH} -c 'echo OK >/dev/stdout' + atf_check -s exit:0 -o inline:'OK\n' -e empty \ + ${TEST_SH} -c '/bin/echo OK >/dev/stdout' + fi + + if test -c /dev/stdin + then + atf_require_prog cat + + echo GOOD | atf_check -s exit:0 -o inline:'GOOD\n' -e empty \ + ${TEST_SH} -c 'read var </dev/stdin; echo $var' + echo GOOD | atf_check -s exit:0 -o inline:'GOOD\n' -e empty \ + ${TEST_SH} -c 'cat </dev/stdin' + fi + + if test -c /dev/stderr + then + atf_check -s exit:0 -e inline:'OK\n' -o empty \ + ${TEST_SH} -c 'echo OK 2>/dev/stderr >&2' + atf_check -s exit:0 -e inline:'OK\n' -o empty \ + ${TEST_SH} -c '/bin/echo OK 2>/dev/stderr >&2' + fi + + if test -c /dev/fd/8 && test -c /dev/fd/9 + then + atf_check -s exit:0 -o inline:'EIGHT\n' -e empty \ + ${TEST_SH} -c 'printf "%s\n" EIGHT 8>&1 >/dev/fd/8 | + cat 9<&0 </dev/fd/9' + fi + + return 0 +} + +atf_test_case redir_in_case +redir_in_case_head() +{ + atf_set "descr" "Tests that sh(1) allows just redirections " \ + "in case statements. (PR bin/48631)" +} +redir_in_case_body() +{ + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -c 'case x in (whatever) >foo;; esac' + + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -c 'case x in (whatever) >foo 2>&1;; esac' + + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -c 'case x in (whatever) >foo 2>&1 </dev/null;; esac' + + atf_check -s exit:0 -o empty -e empty \ + ${TEST_SH} -c 'case x in (whatever) >${somewhere};; esac' +} + +atf_test_case incorrect_redirections +incorrect_redirections_head() +{ + atf_set "descr" "Tests that sh(1) correctly ignores non-redirections" +} +incorrect_redirections_body() { + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'echo foo>' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'read foo<' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c 'echo foo<>' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo x > '"$nl" + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'read x < '"$nl" + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo x <> '"$nl" + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo x >< anything' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo x >>< anything' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo x >|< anything' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'echo x > ; read x < /dev/null || echo bad' + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + 'read x < & echo y > /dev/null; wait && echo bad' + + rm -f Output 2>/dev/null || : + atf_check -s exit:0 -e empty -o inline:'A Line > Output\n' \ + ${TEST_SH} -c 'echo A Line \> Output' + test -f Output && atf_file "File 'Output' appeared and should not have" + + rm -f Output 2>/dev/null || : + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} -c 'echo A Line \>> Output' + test -f Output || atf_file "File 'Output' not created when it should" + test "$(cat Output)" = 'A Line >' || atf_fail \ + "Output file contains '$(cat Output)' instead of '"'A Line >'\' + + rm -f Output \> 2>/dev/null || : + atf_check -s exit:0 -e empty -o empty \ + ${TEST_SH} -c 'echo A Line >\> Output' + test -f Output && atf_file "File 'Output' appeared and should not have" + test -f '>' || atf_file "File '>' not created when it should" + test "$(cat '>')" = 'A Line Output' || atf_fail \ + "Output file ('>') contains '$(cat '>')' instead of 'A Line Output'" +} + +# Many more tests in t_here, so here we have just rudimentary checks +atf_test_case redir_here_doc +redir_here_doc_head() +{ + atf_set "descr" "Tests that sh(1) correctly processes 'here' doc " \ + "input redirections" +} +redir_here_doc_body() +{ + # nb: the printf is not executed, it is data + cat <<- 'DONE' | + cat <<EOF + printf '%s\n' 'hello\n' + EOF + DONE + atf_check -s exit:0 -o match:printf -o match:'hello\\n' \ + -e empty ${TEST_SH} +} + +atf_test_case subshell_redirections +subshell_redirections_head() +{ + atf_set "descr" "Tests redirection interactions between shell and " \ + "its sub-shell(s)" +} +subshell_redirections_body() +{ + atf_require_prog cat + + LIM=$(ulimit -n) + + cat <<- 'DONE' | + exec 6>output-file + + ( printf "hello\n" >&6 ) + + exec 8<output-file + + ( read hello <&8 ; test hello = "$hello" || echo >&2 Hello ) + + ( printf "bye-bye\n" >&6 ) + + ( exec 8<&- ) + read bye <&8 || echo >&2 "Closed?" + echo Bye="$bye" + DONE + atf_check -s exit:0 -o match:Bye=bye-bye -e empty \ + ${TEST_SH} + + cat <<- 'DONE' | + for arg in one-4 two-24 three-14 + do + fd=${arg#*-} + file=${arg%-*} + eval "exec ${fd}>${file}" + done + + for arg in one-5 two-7 three-19 + do + fd=${arg#*-} + file=${arg%-*} + eval "exec ${fd}<${file}" + done + + ( + echo line-1 >&4 + echo line-2 >&24 + echo line-3 >&14 + echo go + ) | ( + read go + read x <&5 + read y <&7 + read z <&19 + + printf "%s\n" "${x}" "${y}" "${z}" + ) + DONE + atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \ + -e empty ${TEST_SH} + + cat <<- 'DONE' | + for arg in one-4-5 two-6-7 three-8-9 four-11-10 five-3-12 + do + ofd=${arg##*-} + file=${arg%-*} + ifd=${file#*-} + file=${file%-*} + eval "exec ${ofd}>${file}" + eval "exec ${ifd}<${file}" + done + + ( ( ( echo line-1 >& 13 ) 13>&12 ) 12>&5 ) >stdout 2>errout + ( ( ( echo line-2 >& 4) 13>&12 ) 4>&7 ) >>stdout 2>>errout + ( ( ( echo line-3 >& 6) 8>&1 6>&11 >&12) 11>&9 >&7 ) >>stdout + + ( ( ( cat <&13 >&12 ) 13<&8 12>&10 ) 10>&1 8<&6 ) 6<&4 + ( ( ( cat <&4 ) <&4 6<&8 8<&11 ) + <&4 4<&6 6<&8 8<&11 ) <&4 4<&6 6<&8 8<&11 11<&3 + ( ( ( cat <&7 >&1 ) 7<&6 >&10 ) 10>&2 6<&8 ) 2>&1 + DONE + atf_check -s exit:0 -o inline:'line-1\nline-2\nline-3\n' \ + -e empty ${TEST_SH} +} + +atf_test_case ulimit_redirection_interaction +ulimit_redirection_interaction_head() +{ + atf_set "descr" "Tests interactions between redirect and ulimit -n " +} +ulimit_redirection_interaction_body() +{ + atf_require_prog ls + + cat <<- 'DONE' > helper.sh + oLIM=$(ulimit -n) + HRD=$(ulimit -H -n) + test "${oLIM}" -lt "${HRD}" && ulimit -n "${HRD}" + LIM=$(ulimit -n) + + FDs= + LFD=-1 + while [ ${LIM} -gt 16 ] + do + FD=$(( ${LIM} - 1 )) + if [ "${FD}" -eq "${LFD}" ]; then + echo >&2 "Infinite loop... (busted $(( )) ??)" + exit 1 + fi + LFD="${FD}" + + eval "exec ${FD}"'> /dev/null' + FDs="${FD}${FDs:+ }${FDs}" + + ( + FD=$(( ${LIM} + 1 )) + eval "exec ${FD}"'> /dev/null' + echo "Reached unreachable command" + ) 2>/dev/null && echo >&2 "Opened beyond limit!" + + (eval 'ls 2>&1 3>&1 4>&1 5>&1 '"${FD}"'>&1') >&"${FD}" + + LIM=$(( ${LIM} / 2 )) + ulimit -S -n "${LIM}" + done + + # Even though ulimit has been reduced, open fds should work + for FD in ${FDs} + do + echo ${FD} in ${FDs} >&"${FD}" || exit 1 + done + + ulimit -S -n "${oLIM}" + + # maybe more later... + + DONE + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} helper.sh +} + +atf_test_case validate_fn_redirects +validate_fn_redirects_head() +{ + # These test cases inspired by PR bin/48875 and the sh + # changes that were required to fix it. + + atf_set "descr" "Tests various redirections applied to functions " \ + "See PR bin/48875" +} +validate_fn_redirects_body() +{ + cat <<- 'DONE' > f-def + f() { + printf '%s\n' In-Func + } + DONE + + atf_check -s exit:0 -o inline:'In-Func\nsuccess1\n' -e empty \ + ${TEST_SH} -c ". ./f-def; f ; printf '%s\n' success1" + atf_check -s exit:0 -o inline:'success2\n' -e empty \ + ${TEST_SH} -c ". ./f-def; f >/dev/null; printf '%s\n' success2" + atf_check -s exit:0 -o inline:'success3\n' -e empty \ + ${TEST_SH} -c ". ./f-def; f >&- ; printf '%s\n' success3" + atf_check -s exit:0 -o inline:'In-Func\nsuccess4\n' -e empty \ + ${TEST_SH} -c ". ./f-def; f & wait; printf '%s\n' success4" + atf_check -s exit:0 -o inline:'success5\n' -e empty \ + ${TEST_SH} -c ". ./f-def; f >&- & wait; printf '%s\n' success5" + atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess6\n' -e empty \ + ${TEST_SH} -c ". ./f-def; f;f; printf '%s\n' success6" + atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess7\n' -e empty \ + ${TEST_SH} -c ". ./f-def; { f;f;}; printf '%s\n' success7" + atf_check -s exit:0 -o inline:'In-Func\nIn-Func\nsuccess8\n' -e empty \ + ${TEST_SH} -c ". ./f-def; { f;f;}& wait; printf '%s\n' success8" + atf_check -s exit:0 -o inline:'In-Func\nsuccess9\n' -e empty \ + ${TEST_SH} -c \ + ". ./f-def; { f>/dev/null;f;}& wait; printf '%s\n' success9" + atf_check -s exit:0 -o inline:'In-Func\nsuccess10\n' -e empty \ + ${TEST_SH} -c \ + ". ./f-def; { f;f>/dev/null;}& wait; printf '%s\n' success10" + + # This one tests the issue etcupdate had with the original 48875 fix + atf_check -s exit:0 -o inline:'Func a\nFunc b\nFunc c\n' -e empty \ + ${TEST_SH} -c ' + f() { + echo Func "$1" + } + exec 3<&0 4>&1 + ( echo x-a; echo y-b; echo z-c ) | + while read A + do + B=${A#?-} + f "$B" <&3 >&4 + done >&2' + + # And this tests a similar condition with that same fix + cat <<- 'DONE' >Script + f() { + printf '%s' " hello $1" + } + exec 3>&1 + echo $( for i in a b c + do printf '%s' @$i; f $i >&3; done >foo + ) + printf '%s\n' foo=$(cat foo) + DONE + atf_check -s exit:0 -e empty \ + -o inline:' hello a hello b hello c\nfoo=@a@b@c\n' \ + ${TEST_SH} Script + + # Tests with sh reading stdin, which is not quite the same internal + # mechanism. + echo ". ./f-def || echo >&2 FAIL + f + printf '%s\n' stdin1 + "| atf_check -s exit:0 -o inline:'In-Func\nstdin1\n' -e empty ${TEST_SH} + + echo ' + . ./f-def || echo >&2 FAIL + f >&- + printf "%s\n" stdin2 + ' | atf_check -s exit:0 -o inline:'stdin2\n' -e empty ${TEST_SH} + + cat <<- 'DONE' > fgh.def + f() { + echo -n f >&3 + sleep 4 + echo -n F >&3 + } + g() { + echo -n g >&3 + sleep 2 + echo -n G >&3 + } + h() { + echo -n h >&3 + } + DONE + + atf_check -s exit:0 -o inline:'fFgGh' -e empty \ + ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL + exec 3>&1 + f; g; h' + + atf_check -s exit:0 -o inline:'fghGF' -e empty \ + ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL + exec 3>&1 + f & sleep 1; g & sleep 1; h; wait' + + atf_check -s exit:0 -o inline:'fFgGhX Y\n' -e empty \ + ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL + exec 3>&1 + echo X $( f ; g ; h ) Y' + + # This one is the real test for PR bin/48875. If the + # cmdsub does not complete before f g (and h) exit, + # then the 'F' & 'G' will precede 'X Y' in the output. + # If the cmdsub finishes while f & g are still running, + # then the X Y will appear before the F and G. + # The trailing "sleep 3" is just so we catch all the + # output (otherwise atf_check will be finished while + # f & g are still sleeping). + + atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty \ + ${TEST_SH} -c '. ./fgh.def || echo >&2 FAIL + exec 3>&1 + echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y + sleep 3 + exec 4>&1 || echo FD_FAIL + ' + + # Do the test again to verify it also all works reading stdin + # (which is a slightly different path through the shell) + echo ' + . ./fgh.def || echo >&2 FAIL + exec 3>&1 + echo X $( f >&- & sleep 1; g >&- & sleep 1 ; h ) Y + sleep 3 + exec 4>&1 || echo FD_FAIL + ' | atf_check -s exit:0 -o inline:'fghX Y\nGF' -e empty ${TEST_SH} +} + +atf_init_test_cases() { + atf_add_test_case basic_test_method_test + atf_add_test_case do_input_redirections + atf_add_test_case do_output_redirections + atf_add_test_case fd_redirections + atf_add_test_case local_redirections + atf_add_test_case incorrect_redirections + atf_add_test_case named_fd_redirections + atf_add_test_case redir_here_doc + atf_add_test_case redir_in_case + atf_add_test_case subshell_redirections + atf_add_test_case ulimit_redirection_interaction + atf_add_test_case validate_fn_redirects +} diff --git a/contrib/netbsd-tests/bin/sh/t_redircloexec.sh b/contrib/netbsd-tests/bin/sh/t_redircloexec.sh new file mode 100755 index 0000000..7659606 --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_redircloexec.sh @@ -0,0 +1,178 @@ +# $NetBSD: t_redircloexec.sh,v 1.3 2016/05/15 15:44:43 kre Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# This code is derived from software contributed to The NetBSD Foundation +# by Christos Zoulas. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +mkhelper() { + name=$1 + fd=$2 + shift 2 + + echo "$@" > ./"${name}1" + echo "echo ${name}2" ">&${fd}" > ./"${name}2" +} + +runhelper() { + ${TEST_SH} "./${1}1" +} + +cleanhelper() { + # not really needed, atf cleans up... + rm -f ./"${1}1" ./"${1}2" out +} + +atf_test_case exec_redir_closed +exec_redir_closed_head() { + atf_set "descr" "Tests that redirections created by exec are closed on exec" +} +exec_redir_closed_body() { + + mkhelper exec 6 \ + "exec 6> out; echo exec1 >&6; ${TEST_SH} exec2; exec 6>&-" + + atf_check -s exit:0 -o empty -e not-empty ${TEST_SH} ./exec1 + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -e ./exec1 + + mkhelper exec 9 \ + "exec 9> out; echo exec1 >&9; ${TEST_SH} exec2" + + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} ./exec1 + + mkhelper exec 8 \ + "exec 8> out; printf OK; echo exec1 >&8;" \ + "printf OK; ${TEST_SH} exec2; printf ERR" + + atf_check -s not-exit:0 -o match:OKOK -o not-match:ERR -e not-empty \ + ${TEST_SH} -e ./exec1 + + mkhelper exec 7 \ + "exec 7> out; printf OK; echo exec1 >&7;" \ + "printf OK; ${TEST_SH} exec2 || printf ERR" + + atf_check -s exit:0 -o match:OKOKERR -e not-empty \ + ${TEST_SH} ./exec1 + + cleanhelper exec +} + +atf_test_case exec_redir_open +exec_redir_open_head() { + atf_set "descr" "Tests that redirections created by exec can remain open" +} +exec_redir_open_body() { + + mkhelper exec 6 \ + "exec 6> out 6>&6; echo exec1 >&6; ${TEST_SH} exec2; exec 6>&-" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} ./exec1 + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -e ./exec1 + + mkhelper exec 9 \ + "exec 9> out ; echo exec1 >&9; ${TEST_SH} exec2 9>&9" + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} ./exec1 + + mkhelper exec 8 \ + "exec 8> out; printf OK; exec 8>&8; echo exec1 >&8;" \ + "printf OK; ${TEST_SH} exec2; printf OK" + + atf_check -s exit:0 -o match:OKOKOK -e empty \ + ${TEST_SH} -e ./exec1 + + mkhelper exec 7 \ + "exec 7> out; printf OK; echo exec1 >&7;" \ + "printf OK; ${TEST_SH} 7>&7 exec2; printf OK" + + atf_check -s exit:0 -o match:OKOKOK -e empty \ + ${TEST_SH} -e ./exec1 + + cleanhelper exec +} + +atf_test_case loop_redir_open +loop_redir_open_head() { + atf_set "descr" "Tests that redirections in loops don't close on exec" +} +loop_redir_open_body() { + mkhelper for 3 "for x in x; do ${TEST_SH} ./for2; done 3>out" + atf_check -s exit:0 \ + -o empty \ + -e empty \ + ${TEST_SH} ./for1 + cleanhelper for +} + +atf_test_case compound_redir_open +compound_redir_open_head() { + atf_set "descr" "Tests that redirections in compound statements don't close on exec" +} +compound_redir_open_body() { + mkhelper comp 3 "{ ${TEST_SH} ./comp2; } 3>out" + atf_check -s exit:0 \ + -o empty \ + -e empty \ + ${TEST_SH} ./comp1 + cleanhelper comp +} + +atf_test_case simple_redir_open +simple_redir_open_head() { + atf_set "descr" "Tests that redirections in simple commands don't close on exec" +} +simple_redir_open_body() { + mkhelper simp 4 "${TEST_SH} ./simp2 4>out" + atf_check -s exit:0 \ + -o empty \ + -e empty \ + ${TEST_SH} ./simp1 + cleanhelper simp +} + +atf_test_case subshell_redir_open +subshell_redir_open_head() { + atf_set "descr" "Tests that redirections on subshells don't close on exec" +} +subshell_redir_open_body() { + mkhelper comp 5 "( ${TEST_SH} ./comp2; ${TEST_SH} ./comp2 ) 5>out" + atf_check -s exit:0 \ + -o empty \ + -e empty \ + ${TEST_SH} ./comp1 + cleanhelper comp +} + +atf_init_test_cases() { + atf_add_test_case exec_redir_closed + atf_add_test_case exec_redir_open + atf_add_test_case loop_redir_open + atf_add_test_case compound_redir_open + atf_add_test_case simple_redir_open + atf_add_test_case subshell_redir_open +} diff --git a/contrib/netbsd-tests/bin/sh/t_set_e.sh b/contrib/netbsd-tests/bin/sh/t_set_e.sh index 8dfe6e4..b56ab22 100755 --- a/contrib/netbsd-tests/bin/sh/t_set_e.sh +++ b/contrib/netbsd-tests/bin/sh/t_set_e.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_set_e.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: t_set_e.sh,v 1.4 2016/03/31 16:22:27 christos Exp $ # # Copyright (c) 2007 The NetBSD Foundation, Inc. # All rights reserved. @@ -30,7 +30,7 @@ # http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html # the implementation of "sh" to test -: ${TEST_SH:="sh"} +: ${TEST_SH:="/bin/sh"} failwith() { @@ -63,7 +63,7 @@ dcheck() # is thus important to test. (PR bin/29861) echeck() { - check1 'eval '"'($1)'" "$2" "eval '($1)'" + check1 'eval '"'( $1 )'" "$2" "eval '($1)'" } atf_test_case all @@ -81,8 +81,8 @@ all_body() { # first, check basic functioning. # The ERR shouldn't print; the result of the () should be 1. # Henceforth we'll assume that we don't need to check $?. - dcheck '(set -e; false; echo ERR$?); echo -n OK$?' 'OK1' - echeck '(set -e; false; echo ERR$?); echo -n OK$?' 'OK1' + dcheck '(set -e; false; echo ERR$?); echo OK$?' 'OK1' + echeck '(set -e; false; echo ERR$?); echo OK$?' 'OK1' # these cases should be equivalent to the preceding. dcheck '(set -e; /nonexistent; echo ERR); echo OK' 'OK' @@ -205,6 +205,9 @@ all_body() { # According to dsl@ in PR bin/32282, () is not defined as a # subshell, only as a grouping operator [and a scope, I guess] + + # (This is incorrect. () is definitely a sub-shell) + # so the nested false ought to cause the whole shell to exit, # not just the subshell. dholland@ would like to see C&V, # because that seems like a bad idea. (Among other things, it @@ -215,8 +218,10 @@ all_body() { # # XXX: the second set has been disabled in the name of making # all tests "pass". + # + # As they should be, they are utter nonsense. - # 1. error if the whole shell exits (current behavior) + # 1. error if the whole shell exits (current correct behavior) dcheck 'echo OK; (set -e; false); echo OK' 'OK OK' echeck 'echo OK; (set -e; false); echo OK' 'OK OK' # 2. error if the whole shell does not exit (dsl's suggested behavior) @@ -232,29 +237,32 @@ all_body() { # backquote expansion (PR bin/17514) - # correct + # (in-)correct #dcheck '(set -e; echo ERR `false`; echo ERR); echo OK' 'OK' #dcheck '(set -e; echo ERR $(false); echo ERR); echo OK' 'OK' #dcheck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'OK' #dcheck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'OK' - # wrong current behavior + # Not-wrong current behavior + # the exit status of ommand substitution is ignored in most cases + # None of these should be causing the shell to exit. dcheck '(set -e; echo ERR `false`; echo ERR); echo OK' 'ERR ERR OK' dcheck '(set -e; echo ERR $(false); echo ERR); echo OK' 'ERR ERR OK' dcheck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'ERR ERR OK' dcheck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'ERR ERR OK' + # This is testing one case (the case?) where the exit status is used dcheck '(set -e; x=`false`; echo ERR); echo OK' 'OK' dcheck '(set -e; x=$(false); echo ERR); echo OK' 'OK' dcheck '(set -e; x=`exit 3`; echo ERR); echo OK' 'OK' dcheck '(set -e; x=$(exit 3); echo ERR); echo OK' 'OK' - # correct + # correct (really just commented out incorrect nonsense) #echeck '(set -e; echo ERR `false`; echo ERR); echo OK' 'OK' #echeck '(set -e; echo ERR $(false); echo ERR); echo OK' 'OK' #echeck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'OK' #echeck '(set -e; echo ERR $(exit 3); echo ERR); echo OK' 'OK' - # wrong current behavior + # not-wrong current behavior (as above) echeck '(set -e; echo ERR `false`; echo ERR); echo OK' 'ERR ERR OK' echeck '(set -e; echo ERR $(false); echo ERR); echo OK' 'ERR ERR OK' echeck '(set -e; echo ERR `exit 3`; echo ERR); echo OK' 'ERR ERR OK' @@ -267,11 +275,19 @@ all_body() { # shift (PR bin/37493) # correct + # Actually, both ways are correct, both are permitted #dcheck '(set -e; shift || true; echo OK); echo OK' 'OK OK' #echeck '(set -e; shift || true; echo OK); echo OK' 'OK OK' - # wrong current behavior - dcheck '(set -e; shift || true; echo OK); echo OK' 'OK' - echeck '(set -e; shift || true; echo OK); echo OK' 'OK' + # (not-) wrong current behavior + #dcheck '(set -e; shift || true; echo OK); echo OK' 'OK' + #echeck '(set -e; shift || true; echo OK); echo OK' 'OK' + + # what is wrong is this test assuming one behaviour or the other + # (and incidentally this has nothing whatever to do with "-e", + # the test should really be moved elsewhere...) + # But for now, leave it here, and correct it: + dcheck '(set -e; shift && echo OK); echo OK' 'OK' + echeck '(set -e; shift && echo OK); echo OK' 'OK' # Done. diff --git a/contrib/netbsd-tests/bin/sh/t_shift.sh b/contrib/netbsd-tests/bin/sh/t_shift.sh new file mode 100755 index 0000000..f65c6c2 --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_shift.sh @@ -0,0 +1,181 @@ +# $NetBSD: t_shift.sh,v 1.2 2016/05/17 09:05:14 kre Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +atf_test_case basic_shift_test +basic_shift_test_head() { + atf_set "descr" "Test correct operation of valid shifts" +} +basic_shift_test_body() { + + for a in \ + "one-arg::0:one-arg" \ + "one-arg:1:0:one-arg" \ + "one-arg:0:1 one-arg" \ + "a b c::2 b c:a" \ + "a b c:1:2 b c:a" \ + "a b c:2:1 c:a:b" \ + "a b c:3:0:a:b:c" \ + "a b c:0:3 a b c" \ + "a b c d e f g h i j k l m n o p:1:15 b c d e f g h i j k l m n o p"\ + "a b c d e f g h i j k l m n o p:9:7 j k l m n o p:a:b:c:g:h:i" \ + "a b c d e f g h i j k l m n o p:13:3 n o p:a:b:c:d:k:l:m" \ + "a b c d e f g h i j k l m n o p:16:0:a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p" + do + oIFS="${IFS}" + IFS=:; set -- $a + IFS="${oIFS}" + + init="$1"; n="$2"; res="$3"; shift 3 + + not= + for b + do + not="${not} -o not-match:$b" + done + + atf_check -s exit:0 -o "match:${res}" ${not} -e empty \ + ${TEST_SH} -c "set -- ${init}; shift $n;"' echo "$# $*"' + done + + atf_check -s exit:0 -o match:complete -o not-match:ERR -e empty \ + ${TEST_SH} -c \ + 'set -- a b c d e;while [ $# -gt 0 ];do shift||echo ERR;done;echo complete' +} + +atf_test_case excessive_shift +excessive_shift_head() { + atf_set "descr" "Test acceptable operation of shift too many" +} +# In: +# +# http://pubs.opengroup.org/onlinepubs/9699919799 +# /utilities/V3_chap02.html#tag_18_26_01 +# +# (that URL should be one line, with the /util... immediately after ...9799) +# +# POSIX says of shift (in the "EXIT STATUS" paragraph): +# +# If the n operand is invalid or is greater than "$#", this may be considered +# a syntax error and a non-interactive shell may exit; if the shell does not +# exit in this case, a non-zero exit status shall be returned. +# Otherwise, zero shall be returned. +# +# NetBSD's sh treats it as an error and exits (if non-interactive, as here), +# other shells do not. +# +# Either behaviour is acceptable - so the test allows for both +# (and checks that if the shell does not exit, "shift" returns status != 0) + +excessive_shift_body() { + for a in \ + "one-arg:2" \ + "one-arg:4" \ + "one-arg:13" \ + "one two:3" \ + "one two:7" \ + "one two three four five:6" \ + "I II III IV V VI VII VIII IX X XI XII XIII XIV XV:16" \ + "I II III IV V VI VII VIII IX X XI XII XIII XIV XV:17" \ + "I II III IV V VI VII VIII IX X XI XII XIII XIV XV:30" \ + "I II III IV V VI VII VIII IX X XI XII XIII XIV XV:9999" + do + oIFS="${IFS}" + IFS=:; set -- $a + IFS="${oIFS}" + + atf_check -s not-exit:0 -o match:OK -o not-match:ERR \ + -e ignore ${TEST_SH} -c \ + "set -- $1 ;"'echo OK:$#-'"$2;shift $2 && echo ERR" + done +} + +atf_test_case function_shift +function_shift_head() { + atf_set "descr" "Test that shift in a function does not affect outside" +} +function_shift_body() { + : # later... +} + +atf_test_case non_numeric_shift +non_numeric_shift_head() { + atf_set "descr" "Test that non-numeric args to shift are detected" +} + +# from the DESCRIPTION section at the URL mentioned with the excessive_shift +# test: +# +# The value n shall be an unsigned decimal integer ... +# +# That is not hex (octal will be treated as if it were decimal, a leading 0 +# will simply be ignored - we test for this by giving an "octal" value that +# would be OK if parsed as octal, but not if parsed (correctly) as decimal) +# +# Obviously total trash like roman numerals or alphabetic strings are out. +# +# Also no signed values (no + or -) and not a string that looks kind of like +# a number, but only if you're generous +# +# But as the EXIT STATUS section quoted above says, with an invalid 'n' +# the shell has the option of exiting, or returning status != 0, so +# again this test allows both. + +non_numeric_shift_body() { + + # there are 9 args set, 010 is 8 if parsed octal, 10 decimal + for a in a I 0x12 010 5V -1 ' ' '' +1 ' 1' + do + atf_check -s not-exit:0 -o empty -e ignore ${TEST_SH} -c \ + "set -- a b c d e f g h i; shift '$a' && echo ERROR" + done +} + +atf_test_case too_many_args +too_many_args_head() { + # See PR bin/50896 + atf_set "descr" "Test that sh detects invalid extraneous args to shift" +} +# This is a syntax error, a non-interactive shell (us) must exit $? != 0 +too_many_args_body() { + # This tests the bug in PR bin/50896 is fixed + + for a in "1 1" "1 0" "1 2 3" "1 foo" "1 --" "-- 1" + do + atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \ + " set -- a b c d; shift ${a} ; echo FAILED " + done +} + +atf_init_test_cases() { + atf_add_test_case basic_shift_test + atf_add_test_case excessive_shift + atf_add_test_case function_shift + atf_add_test_case non_numeric_shift + atf_add_test_case too_many_args +} diff --git a/contrib/netbsd-tests/bin/sh/t_ulimit.sh b/contrib/netbsd-tests/bin/sh/t_ulimit.sh index 3e7c0a6..094a2ee 100755 --- a/contrib/netbsd-tests/bin/sh/t_ulimit.sh +++ b/contrib/netbsd-tests/bin/sh/t_ulimit.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_ulimit.sh,v 1.1 2012/06/11 18:32:59 njoly Exp $ +# $NetBSD: t_ulimit.sh,v 1.3 2016/03/27 14:50:01 christos Exp $ # # Copyright (c) 2012 The NetBSD Foundation, Inc. # All rights reserved. @@ -24,6 +24,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} # ulimit builtin test. @@ -31,13 +33,22 @@ atf_test_case limits limits_head() { atf_set "descr" "Checks for limits flags" } + +get_ulimits() { + local limits=$(${TEST_SH} -c 'ulimit -a' | + sed -e 's/.*\(-[A-Za-z0-9]\)[^A-Za-z0-9].*/\1/' | sort -u) + if [ -z "$limits" ]; then + # grr ksh + limits="-a -b -c -d -f -l -m -n -p -r -s -t -v" + fi + echo "$limits" +} + limits_body() { - atf_check -s eq:0 -o ignore -e empty \ - /bin/sh -c "ulimit -a" - for l in $(ulimit -a | sed 's,^.*(,,;s, .*$,,'); + atf_check -s eq:0 -o ignore -e empty ${TEST_SH} -c "ulimit -a" + for l in $(get_ulimits) do - atf_check -s eq:0 -o ignore -e empty \ - /bin/sh -c "ulimit $l" + atf_check -s eq:0 -o ignore -e empty ${TEST_SH} -c "ulimit $l" done } diff --git a/contrib/netbsd-tests/bin/sh/t_varquote.sh b/contrib/netbsd-tests/bin/sh/t_varquote.sh index 1768777..3811d85 100755 --- a/contrib/netbsd-tests/bin/sh/t_varquote.sh +++ b/contrib/netbsd-tests/bin/sh/t_varquote.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_varquote.sh,v 1.2 2012/03/25 18:50:19 christos Exp $ +# $NetBSD: t_varquote.sh,v 1.5 2016/03/27 14:50:01 christos Exp $ # # Copyright (c) 2007 The NetBSD Foundation, Inc. # All rights reserved. @@ -24,6 +24,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} # Variable quoting test. @@ -39,30 +41,70 @@ all_head() { atf_set "descr" "Basic checks for variable quoting" } all_body() { - foo='${a:-foo}' - check "$foo" '${a:-foo}' - foo="${a:-foo}" - check "$foo" "foo" + cat <<-'EOF' > script.sh + T=0 + check() { + T=$((${T} + 1)) - foo=${a:-"'{}'"} - check "$foo" "'{}'" + if [ "$1" != "$2" ] + then + printf '%s\n' "T${T}: expected [$2], found [$1]" + exit 1 + fi + } - foo=${a:-${b:-"'{}'"}} - check "$foo" "'{}'" + #1 + foo='${a:-foo}' + check "$foo" '${a:-foo}' + #2 + foo="${a:-foo}" + check "$foo" "foo" + #3 + foo=${a:-"'{}'"} + check "$foo" "'{}'" + #4 + foo=${a:-${b:-"'{}'"}} + check "$foo" "'{}'" + #5 + # ${ } The ' are inside ".." so are literal (not quotes). + foo="${a-'}'}" + check "$foo" "''}" + #6 + # The rules for quoting in ${var-word} expressions are somewhat + # weird, in the following there is not one quoted string being + # assigned to foo (with internally quoted sub-strings), rather + # it is a mixed quoted/unquoted string, with parts that are + # quoted, separated by 2 unquoted sections... + # qqqqqqqqqq uuuuuuuuuu qq uuuu qqqq + foo="${a:-${b:-"${c:-${d:-"x}"}}y}"}}z}" + # " z*" + # ${a:- } + # ${b:- } + # " y*" + # ${c:- } + # ${d:- } + # "x*" + check "$foo" "x}y}z}" + #7 + # And believe it or not, this is the one that gives + # most problems, with 3 different observed outputs... + # qqqqq qq q is one interpretation + # qqqqq QQQQ q is another (most common) + # (the third is syntax error...) + foo="${a:-"'{}'"}" + check "$foo" "'{}'" - foo="${a:-"'{}'"}" - check "$foo" "'{}'" + EOF - foo="${a:-${b:-"${c:-${d:-"x}"}}y}"}}z}" - # " z*" - # ${a:- } - # ${b:- } - # " y*" - # ${c:- } - # ${d:- } - # "x*" - check "$foo" "x}y}z}" + OUT=$( ${TEST_SH} script.sh 2>&1 ) + if [ $? -ne 0 ] + then + atf_fail "${OUT}" + elif [ -n "${OUT}" ] + then + atf_fail "script.sh unexpectedly said: ${OUT}" + fi } atf_test_case nested_quotes_multiword @@ -72,10 +114,21 @@ nested_quotes_multiword_head() { } nested_quotes_multiword_body() { atf_check -s eq:0 -o match:"first-word second-word" -e empty \ - /bin/sh -c 'echo "${foo:="first-word"} second-word"' + ${TEST_SH} -c 'echo "${foo:="first-word"} second-word"' +} + +atf_test_case default_assignment_with_arith +default_assignment_with_arith_head() { + atf_set "descr" "Tests default variable assignment with arithmetic" \ + "string works (PR bin/50827)" +} +default_assignment_with_arith_body() { + atf_check -s eq:0 -o empty -e empty ${TEST_SH} -c ': "${x=$((1))}"' + atf_check -s eq:0 -o match:1 -e empty ${TEST_SH} -c 'echo "${x=$((1))}"' } atf_init_test_cases() { atf_add_test_case all atf_add_test_case nested_quotes_multiword + atf_add_test_case default_assignment_with_arith } diff --git a/contrib/netbsd-tests/bin/sh/t_varval.sh b/contrib/netbsd-tests/bin/sh/t_varval.sh new file mode 100755 index 0000000..94e306b --- /dev/null +++ b/contrib/netbsd-tests/bin/sh/t_varval.sh @@ -0,0 +1,251 @@ +# $NetBSD: t_varval.sh,v 1.1 2016/03/16 15:49:19 christos Exp $ +# +# Copyright (c) 2016 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +# Test all kinds of weird values in various ways to use shell $... expansions + +oneline() +{ + q="'" + test $# -eq 4 && q="" + + v=$( printf '\\%3.3o' $(( $2 & 0xFF )) ) + printf "%s" "$1" + if [ $2 != 39 ]; then + printf "%sprefix${v}suffix%s" "$q" "$q" + elif [ $# -ne 4 ]; then + printf %s prefix\"\'\"suffix + else + printf %s prefix\'suffix + fi + printf "%s\n" "$3" +} + +mkdata() { + quote= pfx= + while [ $# -gt 0 ] + do + case "$1" in + --) shift; break;; + -q) quote=no; shift; continue;; + esac + + pfx="${pfx}${pfx:+ }${1}" + shift + done + + sfx= + while [ $# -gt 0 ] + do + sfx="${sfx}${sfx:+ }${1}" + shift + done + + i=1 # '\0' is not expected to work, anywhere... + while [ $i -lt 256 ] + do + oneline "${pfx}" "$i" "${sfx}" $quote + i=$(( $i + 1 )) + done +} + +atf_test_case aaa +aaa_head() { + atf_set "descr" "Check that this test has a hope of working. " \ + "Just give up on these tests if the aaa test fails". +} +aaa_body() { + oneline "echo " 9 '' | + atf_check -s exit:0 -o inline:'prefix\tsuffix\n' -e empty \ + ${TEST_SH} + + oneline "VAR=" 65 '; echo "${#VAR}:${VAR}"' | + atf_check -s exit:0 -o inline:'13:prefixAsuffix\n' -e empty \ + ${TEST_SH} + + oneline "VAR=" 1 '; echo "${#VAR}:${VAR}"' | + atf_check -s exit:0 -o inline:'13:prefixsuffix\n' -e empty \ + ${TEST_SH} + + oneline "VAR=" 10 '; echo "${#VAR}:${VAR}"' | + atf_check -s exit:0 -o inline:'13:prefix\nsuffix\n' -e empty \ + ${TEST_SH} + + rm -f prefix* 2>/dev/null || : + oneline "echo hello >" 45 "" | + atf_check -s exit:0 -o empty -e empty ${TEST_SH} + test -f "prefix-suffix" || + atf_fail "failed to create prefix-suffix (45)" + test -s "prefix-suffix" || + atf_fail "no data in prefix-suffix (45)" + test "$(cat prefix-suffix)" = "hello" || + atf_fail "incorrect data in prefix-suffix (45)" + + return 0 +} + +atf_test_case assignment +assignment_head() { + atf_set "descr" "Check that all chars can be assigned to vars" +} +assignment_body() { + atf_require_prog grep + atf_require_prog rm + + rm -f results || : + mkdata "VAR=" -- '; echo ${#VAR}' | + atf_check -s exit:0 -o save:results -e empty ${TEST_SH} + test -z $( grep -v "^13$" results ) || + atf_fail "Incorrect lengths: $(grep -nv '^13$' results)" + + return 0 +} + +atf_test_case cmdline +cmdline_head() { + atf_set "descr" "Check vars containing all chars can be used" +} +cmdline_body() { + atf_require_prog rm + atf_require_prog wc + + rm -f results || : + mkdata "VAR=" -- '; echo "${VAR}"' | + atf_check -s exit:0 -o save:results -e empty ${TEST_SH} + + # 256 because one output line contains a \n ... + test $( wc -l < results ) -eq 256 || + atf_fail "incorrect line count in results" + test $(wc -c < results) -eq $(( 255 * 14 )) || + atf_fail "incorrect character count in results" + + return 0 +} + +atf_test_case redirect +redirect_head() { + atf_set "descr" "Check vars containing all chars can be used" +} +redirect_body() { + atf_require_prog ls + atf_require_prog wc + atf_require_prog rm + atf_require_prog mkdir + atf_require_prog rmdir + + nl=' +' + + rm -f prefix* suffix || : + + mkdir prefix # one of the files will be prefix/suffix + mkdata "VAR=" -- '; echo "${VAR}" > "${VAR}"' | + atf_check -s exit:0 -o empty -e empty ${TEST_SH} + + test -f "prefix/suffix" || + atf_fail "Failed to create file in subdirectory" + test $( wc -l < "prefix/suffix" ) -eq 1 || + atf_fail "Not exactly one line in prefix/suffix file" + + atf_check -s exit:0 -o empty -e empty rm "prefix/suffix" + atf_check -s exit:0 -o empty -e empty rmdir "prefix" + + test -f "prefix${nl}suffix" || + atf_fail "Failed to create file with newline in its name" + test $( wc -l < "prefix${nl}suffix" ) -eq 2 || + atf_fail "NewLine file did not contain embedded newline" + + atf_check -s exit:0 -o empty -e empty rm "prefix${nl}suffix" + + # Now there should be 253 files left... + test $( ls | wc -l ) -eq 253 || + atf_fail \ + "Did not create all expected files: wanted: 253, found ($( ls | wc -l ))" + + # and each of them should have a name that is 13 chars long (+ \n) + test $( ls | wc -c ) -eq $(( 253 * 14 )) || + atf_fail "File names do not appear to be as expected" + + return 0 +} + +atf_test_case read +read_head() { + atf_set "descr" "Check vars containing all chars can be used" +} +read_body() { + atf_require_prog ls + atf_require_prog wc + atf_require_prog rm + atf_require_prog mkdir + atf_require_prog rmdir + + nl=' +' + + rm -f prefix* suffix || : + + mkdir prefix # one of the files will be prefix/suffix + mkdata -q | + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c ' + while read -r VAR + do + # skip the mess made by embedded newline + case "${VAR}" in + (prefix | suffix) continue;; + esac + echo "${VAR}" > "${VAR}" + done' + + test -f "prefix/suffix" || + atf_fail "Failed to create file in subdirectory" + test $( wc -l < "prefix/suffix" ) -eq 1 || + atf_fail "Not exactly one line in prefix/suffix file" + + atf_check -s exit:0 -o empty -e empty rm "prefix/suffix" + atf_check -s exit:0 -o empty -e empty rmdir "prefix" + + # Now there should be 253 files left... + test $( ls | wc -l ) -eq 253 || + atf_fail \ + "Did not create all expected files: wanted: 253, found ($( ls | wc -l ))" + + # and each of them should have a name that is 13 chars long (+ \n) + test $( ls | wc -c ) -eq $(( 253 * 14 )) || + atf_fail "File names do not appear to be as expected" + + return 0 +} + +atf_init_test_cases() { + atf_add_test_case aaa + atf_add_test_case assignment + atf_add_test_case cmdline + atf_add_test_case redirect + atf_add_test_case read +} diff --git a/contrib/netbsd-tests/bin/sh/t_wait.sh b/contrib/netbsd-tests/bin/sh/t_wait.sh index 99b47df..eaad7e0 100755 --- a/contrib/netbsd-tests/bin/sh/t_wait.sh +++ b/contrib/netbsd-tests/bin/sh/t_wait.sh @@ -1,4 +1,4 @@ -# $NetBSD: t_wait.sh,v 1.1 2012/03/17 16:33:11 jruoho Exp $ +# $NetBSD: t_wait.sh,v 1.8 2016/03/31 16:22:54 christos Exp $ # # Copyright (c) 2008, 2009, 2010 The NetBSD Foundation, Inc. # All rights reserved. @@ -24,36 +24,172 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +# the implementation of "sh" to test +: ${TEST_SH:="/bin/sh"} + +atf_test_case basic_wait +basic_wait_head() { + atf_set "descr" "Tests simple uses of wait" +} +basic_wait_body() { + atf_require_prog sleep + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '(echo nothing >/dev/null) & wait' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + '(exit 3) & wait $!; S=$?; test $S -eq 3 || { + echo "status: $S"; exit 1; }' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'sleep 3 & sleep 2 & sleep 1 & wait' + + atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \ + 'sleep 3 & (exit 2) & sleep 1 & wait' +} atf_test_case individual individual_head() { - atf_set "descr" "Tests that waiting for individual jobs works" + atf_set "descr" "Tests that waiting for individual processes works" } individual_body() { + atf_require_prog sleep + + cat >individualhelper.sh <<\EOF +sleep 3 & P1=$! +sleep 1 & P2=$! + +wait ${P1} +S=$? +if [ $S -ne 0 ]; then + echo "Waiting for first process failed: $S" + exit 1 +fi + +wait ${P2} +S=$? +if [ $? -ne 0 ]; then + echo "Waiting for second process failed" + exit 1 +fi + +exit 0 +EOF + output=$(${TEST_SH} individualhelper.sh 2>&1) + [ $? -eq 0 ] || atf_fail "${output}" +} + +atf_test_case jobs +jobs_head() { + atf_set "descr" "Tests that waiting for individual jobs works" +} +jobs_body() { # atf-sh confuses wait for some reason; work it around by creating # a helper script that executes /bin/sh directly. - cat >helper.sh <<EOF + + if ! ${TEST_SH} -c 'sleep 1 & wait %1' 2>/dev/null + then + atf_skip "No job control support in this shell" + fi + + cat >individualhelper.sh <<\EOF sleep 3 & sleep 1 & wait %1 -if [ \$? -ne 0 ]; then - echo "Waiting of first job failed" +if [ $? -ne 0 ]; then + echo "Waiting for first job failed" + exit 1 +fi + +wait %2 +if [ $? -ne 0 ]; then + echo "Waiting for second job failed" + exit 1 +fi + +exit 0 +EOF + output=$(${TEST_SH} individualhelper.sh 2>&1) + [ $? -eq 0 ] || atf_fail "${output}" + + cat >individualhelper.sh <<\EOF +{ sleep 3; exit 3; } & +{ sleep 1; exit 7; } & + +wait %1 +S=$? +if [ $S -ne 3 ]; then + echo "Waiting for first job failed - status: $S != 3 (expected)" exit 1 fi wait %2 -if [ \$? -ne 0 ]; then - echo "Waiting of second job failed" +S=$? +if [ $S -ne 7 ]; then + echo "Waiting for second job failed - status: $S != 7 (expected)" exit 1 fi exit 0 EOF - output=$(/bin/sh helper.sh) + + output=$(${TEST_SH} individualhelper.sh 2>&1) [ $? -eq 0 ] || atf_fail "${output}" } +atf_test_case kill +kill_head() { + atf_set "descr" "Tests that killing the shell while in wait calls trap" +} +kill_body() { + atf_require_prog sleep + atf_require_prog kill + + s=killhelper.sh + z=killhelper.$$ + pid= + + # waiting for a specific process that is not a child + # should return exit status of 127 according to the spec + # This test is here before the next, to avoid that one + # entering an infinite loop should the shell have a bug here. + + atf_check -s exit:127 -o empty -e ignore ${TEST_SH} -c 'wait 1' + + cat > "${s}" <<'EOF' + +trap "echo SIGHUP" 1 +(sleep 5; exit 3) & +sl=$! +wait +S=$? +echo $S +LS=9999 +while [ $S -ne 0 ] && [ $S != 127 ]; do + wait $sl; S=$?; echo $S + test $S = $LS && { echo "wait repeats..."; exit 2; } + LS=$S + done +EOF + + ${TEST_SH} $s > $z & + pid=$! + sleep 1 + + kill -HUP "${pid}" + wait + + output="$(cat $z | tr '\n' ' ')" + + if [ "$output" != "SIGHUP 129 3 127 " ]; then + atf_fail "${output} != 'SIGHUP 129 3 127 '" + fi +} + atf_init_test_cases() { + atf_add_test_case basic_wait atf_add_test_case individual + atf_add_test_case jobs + atf_add_test_case kill } |