Home | History | Annotate | Line # | Download | only in sh
t_builtins.sh revision 1.5
      1  1.5  kre # $NetBSD: t_builtins.sh,v 1.5 2019/01/09 10:51:23 kre Exp $
      2  1.1  kre #
      3  1.2  kre # Copyright (c) 2018 The NetBSD Foundation, Inc.
      4  1.1  kre # All rights reserved.
      5  1.1  kre #
      6  1.1  kre # Redistribution and use in source and binary forms, with or without
      7  1.1  kre # modification, are permitted provided that the following conditions
      8  1.1  kre # are met:
      9  1.1  kre # 1. Redistributions of source code must retain the above copyright
     10  1.1  kre #    notice, this list of conditions and the following disclaimer.
     11  1.1  kre # 2. Redistributions in binary form must reproduce the above copyright
     12  1.1  kre #    notice, this list of conditions and the following disclaimer in the
     13  1.1  kre #    documentation and/or other materials provided with the distribution.
     14  1.1  kre #
     15  1.1  kre # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     16  1.1  kre # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     17  1.1  kre # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  1.1  kre # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     19  1.1  kre # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     20  1.1  kre # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     21  1.1  kre # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     22  1.1  kre # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     23  1.1  kre # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     24  1.1  kre # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     25  1.1  kre # POSSIBILITY OF SUCH DAMAGE.
     26  1.1  kre #
     27  1.1  kre # the implementation of "sh" to test
     28  1.1  kre : ${TEST_SH:="/bin/sh"}
     29  1.1  kre 
     30  1.1  kre #
     31  1.1  kre # This file tests the various sh builtin utilities.
     32  1.1  kre #
     33  1.1  kre # Those utilities that are really external programs, which are builtin in
     34  1.1  kre # for (mostly) performance (printf, kill, test, ...), are tested elsewhere.
     35  1.1  kre # We do test the builtin "echo" here as (in NetBSD) it is different than
     36  1.1  kre # the external one.
     37  1.1  kre #
     38  1.1  kre # The (mostly special) builtins which appear to be more syntax than command
     39  1.1  kre # are tested in other test programs, rather than here (break, continue...)
     40  1.1  kre #
     41  1.1  kre # And finally, those which are fundamental to the operation of the shell,
     42  1.1  kre # like wait, set, shift, ... are also tested in other test programs where
     43  1.1  kre # all their operations can be more thoroughly verified.
     44  1.1  kre #
     45  1.1  kre # This leaves those which need to be built in (cd, umask, ...) but whose
     46  1.1  kre # purpose is mostly to alter the environment in which the shell operates
     47  1.1  kre # of that of the commands it runs.   These tests act in co-operation with
     48  1.1  kre # other tests exist here (where thy do) by not duplicating tests run
     49  1.1  kre # elsewhere (ulimit is one example) but just adding to those.
     50  1.1  kre # One day these might be unified.
     51  1.1  kre #
     52  1.1  kre # We do test both standard use of the builtins (where they are standard)
     53  1.1  kre # and NetBSD sh extensions (when run on a shell with no support, such tests
     54  1.1  kre # should be skipped.)
     55  1.1  kre #
     56  1.1  kre 
     57  1.1  kre # Utility function able to test whether most of the builtins exist in
     58  1.1  kre # the shell being tested.
     59  1.1  kre have_builtin()
     60  1.1  kre {
     61  1.1  kre 	${TEST_SH} -c "( $3 $1 $4 ) >/dev/null 2>&1" 	&&
     62  1.1  kre 	LC_ALL=C ${TEST_SH} -c \
     63  1.1  kre 	    'case "$( (type '"$1"') 2>&1)" in
     64  1.1  kre 		(*built*)	exit 0 ;;
     65  1.3  kre 		(*reserved*)	exit 0 ;;   # zsh!! (reserved words are builtin)
     66  1.1  kre 	     esac
     67  1.1  kre 	     exit 1'					||
     68  1.1  kre 	{
     69  1.1  kre 		test -z "$2" && atf_skip "${TEST_SH} has no '$1$5' built-in"
     70  1.1  kre 		return 1;
     71  1.1  kre 	}
     72  1.1  kre 
     73  1.1  kre 	return 0
     74  1.1  kre }
     75  1.1  kre 
     76  1.5  kre # And another to test if the shell being tested is the NetBSD shell,
     77  1.5  kre # as we use these tests both to test standards conformance (correctness)
     78  1.5  kre # which should be possible for all shells, and to test NetBSD
     79  1.5  kre # extensions (which we mostly do by testing if the extension exists)
     80  1.5  kre # and NetBSD sh behaviour for what is unspecified by the standard
     81  1.5  kre # (so we will be notified via test failure should that unspecified
     82  1.5  kre # behaviour alter) for which we have to discover if that shell is the
     83  1.5  kre # one being tested.
     84  1.5  kre 
     85  1.5  kre is_netbsd_sh()
     86  1.5  kre {
     87  1.5  kre 	unset NETBSD_SHELL 2>/dev/null
     88  1.5  kre 	test -n "$( ${TEST_SH} -c 'printf %s "${NETBSD_SHELL}"')"
     89  1.5  kre }
     90  1.5  kre 
     91  1.1  kre ### Helper functions
     92  1.1  kre 
     93  1.1  kre nl='
     94  1.1  kre '
     95  1.1  kre reset()
     96  1.1  kre {
     97  1.1  kre 	TEST_NUM=0
     98  1.1  kre 	TEST_FAILURES=''
     99  1.1  kre 	TEST_FAIL_COUNT=0
    100  1.1  kre 	TEST_ID="$1"
    101  1.1  kre 
    102  1.1  kre 	# These are used in check()
    103  1.1  kre 	atf_require_prog tr
    104  1.1  kre 	atf_require_prog printf
    105  1.1  kre 	atf_require_prog mktemp
    106  1.1  kre }
    107  1.1  kre 
    108  1.1  kre # Test run & validate.
    109  1.1  kre #
    110  1.1  kre #	$1 is the command to run (via sh -c)
    111  1.1  kre #	$2 is the expected output
    112  1.1  kre #	$3 is the expected exit status from sh
    113  1.1  kre #	$4 is optional extra data for the error msg (if there is one)
    114  1.1  kre #
    115  1.1  kre # Stderr is exxpected to be empty, unless the expected exit code ($3) is != 0
    116  1.1  kre # in which case some message there is expected (and nothing is a failure).
    117  1.1  kre # When non-zero exit is expected, we note a different (non-zero) value
    118  1.1  kre # observed, but do not fail the test because of that.
    119  1.1  kre 
    120  1.1  kre check()
    121  1.1  kre {
    122  1.1  kre 	fail=false
    123  1.1  kre 	TEMP_FILE=$( mktemp OUT.XXXXXX )
    124  1.1  kre 	TEST_NUM=$(( $TEST_NUM + 1 ))
    125  1.1  kre 	MSG=
    126  1.1  kre 
    127  1.1  kre 	# our local shell (ATF_SHELL) better do quoting correctly...
    128  1.1  kre 	# some of the tests expect us to expand $nl internally...
    129  1.1  kre 	CMD="$1"
    130  1.1  kre 
    131  1.1  kre 	# determine what the test generates, preserving trailing \n's
    132  1.1  kre 	result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" && printf X )"
    133  1.1  kre 	STATUS=$?
    134  1.1  kre 	result="${result%X}"
    135  1.1  kre 
    136  1.1  kre 
    137  1.1  kre 	if [ "${STATUS}" -ne "$3" ]; then
    138  1.1  kre 		MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
    139  1.1  kre 		MSG="${MSG} expected exit code $3, got ${STATUS}"
    140  1.1  kre 
    141  1.1  kre 		# don't actually fail just because of wrong exit code
    142  1.1  kre 		# unless we either expected, or received "good"
    143  1.1  kre 		# or something else is detected as incorrect as well.
    144  1.1  kre 		case "$3/${STATUS}" in
    145  1.1  kre 		(*/0|0/*) fail=true;;
    146  1.1  kre 		esac
    147  1.1  kre 	fi
    148  1.1  kre 
    149  1.1  kre 	if [ "$3" -eq 0 ]; then
    150  1.1  kre 		if [ -s "${TEMP_FILE}" ]; then
    151  1.1  kre 			MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
    152  1.1  kre 			MSG="${MSG} Messages produced on stderr unexpected..."
    153  1.1  kre 			MSG="${MSG}${nl}$( cat "${TEMP_FILE}" )"
    154  1.1  kre 			fail=true
    155  1.1  kre 		fi
    156  1.1  kre 	else
    157  1.1  kre 		if ! [ -s "${TEMP_FILE}" ]; then
    158  1.1  kre 			MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
    159  1.1  kre 			MSG="${MSG} Expected messages on stderr,"
    160  1.1  kre 			MSG="${MSG} nothing produced"
    161  1.1  kre 			fail=true
    162  1.1  kre 		fi
    163  1.1  kre 	fi
    164  1.1  kre 	rm -f "${TEMP_FILE}"
    165  1.1  kre 
    166  1.1  kre 	if [ "$2" != "${result}" ]
    167  1.1  kre 	then
    168  1.1  kre 		MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
    169  1.1  kre 		MSG="${MSG} Expected: <<$2>>, received: <<$result>>"
    170  1.1  kre 		fail=true
    171  1.1  kre 	fi
    172  1.1  kre 
    173  1.1  kre 	if $fail
    174  1.1  kre 	then
    175  1.1  kre 		if [ -n "$4" ]; then
    176  1.1  kre 			MSG="${MSG}${MSG:+${nl}}[$TEST_NUM] Note: ${4}"
    177  1.1  kre 		fi
    178  1.1  kre 		MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]"
    179  1.1  kre  		MSG="${MSG} Full command: <<${CMD}>>"
    180  1.1  kre 	fi
    181  1.1  kre 
    182  1.1  kre 	$fail && test -n "$TEST_ID" && {
    183  1.1  kre 		TEST_FAILURES="${TEST_FAILURES}${TEST_FAILURES:+${nl}}"
    184  1.1  kre 		TEST_FAILURES="${TEST_FAILURES}${TEST_ID}[$TEST_NUM]:"
    185  1.1  kre 		TEST_FAILURES="${TEST_FAILURES} Test of <<$1>> failed.";
    186  1.1  kre 		TEST_FAILURES="${TEST_FAILURES}${nl}${MSG}"
    187  1.1  kre 		TEST_FAIL_COUNT=$(( $TEST_FAIL_COUNT + 1 ))
    188  1.1  kre 		return 0
    189  1.1  kre 	}
    190  1.1  kre 	$fail && atf_fail "Test[$TEST_NUM] failed: $(
    191  1.1  kre 	    # ATF does not like newlines in messages, so change them...
    192  1.1  kre 		    printf '%s' "${MSG}" | tr '\n' ';'
    193  1.1  kre 	    )"
    194  1.1  kre 	return 0
    195  1.1  kre }
    196  1.1  kre 
    197  1.1  kre results()
    198  1.1  kre {
    199  1.1  kre 	test -n "$1" && atf_expect_fail "$1"
    200  1.1  kre 
    201  1.1  kre 	test -z "${TEST_ID}" && return 0
    202  1.1  kre 	test -z "${TEST_FAILURES}" && return 0
    203  1.1  kre 
    204  1.1  kre 	echo >&2 "=========================================="
    205  1.1  kre 	echo >&2 "While testing '${TEST_ID}'"
    206  1.1  kre 	echo >&2 " - - - - - - - - - - - - - - - - -"
    207  1.1  kre 	echo >&2 "${TEST_FAILURES}"
    208  1.1  kre 
    209  1.1  kre 	atf_fail \
    210  1.1  kre  "Test ${TEST_ID}: $TEST_FAIL_COUNT (of $TEST_NUM) subtests failed - see stderr"
    211  1.1  kre }
    212  1.1  kre 
    213  1.1  kre ####### End helpers
    214  1.1  kre 
    215  1.1  kre atf_test_case colon
    216  1.1  kre colon_head() {
    217  1.1  kre 	atf_set "descr" "Tests the shell special builtin ':' command"
    218  1.1  kre }
    219  1.1  kre colon_body() {
    220  1.1  kre 	have_builtin : || return 0
    221  1.1  kre 
    222  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c ":"
    223  1.1  kre 
    224  1.1  kre 	# ':' is a special builtin, so we should exit on redirect error
    225  1.1  kre 	# and variable assignments should persist (stupid, but it is the rule)
    226  1.1  kre 
    227  1.1  kre 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    228  1.1  kre 		": >/foo/bar; printf %s No-exit-BUG"
    229  1.1  kre 	atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \
    230  1.1  kre 		'X=BUG; X=OK : ; printf %s "${X}"'
    231  1.1  kre }
    232  1.1  kre 
    233  1.1  kre atf_test_case echo
    234  1.1  kre echo_head() {
    235  1.1  kre 	atf_set "descr" "Tests the shell builtin version of echo"
    236  1.1  kre }
    237  1.1  kre echo_body() {
    238  1.1  kre 	have_builtin echo || return 0
    239  1.1  kre 
    240  1.5  kre 	if ! is_netbsd_sh; then
    241  1.1  kre 		atf_skip \
    242  1.1  kre 	   "${TEST_SH%% *} is not the NetBSD shell, this test is for it alone"
    243  1.1  kre 		return 0
    244  1.1  kre 	fi
    245  1.1  kre 
    246  1.1  kre 	reset echo
    247  1.1  kre 
    248  1.1  kre 	check 'echo "hello world"' "hello world${nl}" 0
    249  1.1  kre 	check 'echo hello world' "hello world${nl}" 0
    250  1.1  kre 	check 'echo -n "hello world"' "hello world" 0
    251  1.1  kre 	check 'IFS=:; echo hello world' "hello world${nl}" 0
    252  1.1  kre 	check 'IFS=; echo hello world' "hello world${nl}" 0
    253  1.1  kre 
    254  1.1  kre 	check 'echo -e "hello world"' "hello world${nl}" 0
    255  1.1  kre 	check 'echo -e hello world' "hello world${nl}" 0
    256  1.1  kre 	check 'IFS=:; echo -e hello world' "hello world${nl}" 0
    257  1.1  kre 
    258  1.1  kre 	# only one of the options is used
    259  1.1  kre 	check 'echo -e -n "hello world"' "-n hello world${nl}" 0
    260  1.1  kre 	check 'echo -n -e "hello world"' "-e hello world" 0
    261  1.1  kre 	# and only when it is alone
    262  1.1  kre 	check 'echo -en "hello world"' "-en hello world${nl}" 0
    263  1.1  kre 	check 'echo -ne "hello world"' "-ne hello world${nl}" 0
    264  1.1  kre 
    265  1.1  kre 	# echo is specifically required to *not* support --
    266  1.1  kre 	check 'echo -- "hello world"' "-- hello world${nl}" 0
    267  1.1  kre 
    268  1.1  kre 	# similarly any other unknown option is simply part of the output
    269  1.1  kre 	for OPT in a b c v E N Q V 0 1 2 @ , \? \[ \] \( \; . \* -help -version
    270  1.1  kre 	do
    271  1.1  kre 		check "echo '-${OPT}' foo" "-${OPT} foo${nl}" 0
    272  1.1  kre 	done
    273  1.1  kre 
    274  1.1  kre 	# Now test the \\ expansions, with and without -e
    275  1.1  kre 
    276  1.1  kre 	# We rely upon printf %b (tested elsewhere, not only a sh test)
    277  1.1  kre 	# to verify the output when the \\ is supposed to be expanded.
    278  1.1  kre 
    279  1.1  kre 	for E in '' -e
    280  1.1  kre 	do
    281  1.1  kre 		for B in a b c e f n r t v \\ 04 010 012 0177
    282  1.1  kre 		do
    283  1.1  kre 			S="test string with \\${B} in it"
    284  1.1  kre 			if [ -z "${E}" ]; then
    285  1.1  kre 				R="${S}${nl}"
    286  1.1  kre 			else
    287  1.1  kre 				R="$(printf '%b\nX' "${S}")"
    288  1.1  kre 				R=${R%X}
    289  1.1  kre 			fi
    290  1.1  kre 			check "echo $E '${S}'" "${R}" 0
    291  1.1  kre 		done
    292  1.1  kre 	done
    293  1.1  kre 
    294  1.1  kre 	results
    295  1.1  kre }
    296  1.1  kre 
    297  1.1  kre atf_test_case eval
    298  1.1  kre eval_head() {
    299  1.1  kre 	atf_set "descr" "Tests the shell special builtin 'eval'"
    300  1.1  kre }
    301  1.1  kre eval_body() {
    302  1.1  kre 	have_builtin eval || return 0
    303  1.1  kre 
    304  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval "exit 0"'
    305  1.1  kre 	atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c 'eval "exit 1"'
    306  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval exit 0'
    307  1.5  kre 	atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c 'eval exit 1'
    308  1.5  kre 
    309  1.5  kre 	atf_check -s exit:0 -e empty -o inline:0 ${TEST_SH} -c \
    310  1.5  kre 		'eval true; printf %d $?'
    311  1.5  kre 	atf_check -s exit:0 -e empty -o inline:1 ${TEST_SH} -c \
    312  1.5  kre 		'eval false; printf %d $?'
    313  1.1  kre 
    314  1.1  kre 	atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \
    315  1.1  kre 		'X=a Y=b Z=c; for V in X Y Z; do eval "printf %s \$$V"; done'
    316  1.1  kre 	atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \
    317  1.1  kre 		'X=a Y=b Z=c; for V in X Y Z; do eval printf %s \$$V; done'
    318  1.1  kre 	atf_check -s exit:0 -e empty -o inline:XYZ ${TEST_SH} -c \
    319  1.1  kre 		'for V in X Y Z; do eval "${V}=${V}"; done; printf %s "$X$Y$Z"'
    320  1.5  kre 
    321  1.5  kre 	# make sure eval'd strings affect the shell environment
    322  1.5  kre 
    323  1.5  kre 	atf_check -s exit:0 -e empty -o inline:/b/ ${TEST_SH} -c \
    324  1.5  kre 		'X=a; eval "X=b"; printf /%s/ "${X-unset}"'
    325  1.5  kre 	atf_check -s exit:0 -e empty -o inline:/b/ ${TEST_SH} -c \
    326  1.5  kre 		'X=a; Y=X; Z=b; eval "$Y=$Z"; printf /%s/ "${X-unset}"'
    327  1.5  kre 	atf_check -s exit:0 -e empty -o inline:/unset/ ${TEST_SH} -c \
    328  1.5  kre 		'X=a; eval "unset X"; printf /%s/ "${X-unset}"'
    329  1.5  kre 	atf_check -s exit:0 -e empty -o inline:// ${TEST_SH} -c \
    330  1.5  kre 		'unset X; eval "X="; printf /%s/ "${X-unset}"'
    331  1.5  kre 	atf_check -s exit:0 -e empty -o inline:'2 y Z ' ${TEST_SH} -c \
    332  1.5  kre 		'set -- X y Z; eval shift; printf "%s " "$#" "$@"'
    333  1.5  kre 
    334  1.5  kre 	# ensure an error in an eval'd string causes the shell to exit
    335  1.5  kre 	# unless 'eval' is preceded by 'command' (in which case the
    336  1.5  kre 	# string is not eval'd but execution continues)
    337  1.5  kre 
    338  1.5  kre 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    339  1.5  kre 		'eval "if done"; printf %s status=$?'
    340  1.5  kre 
    341  1.5  kre 	atf_check -s exit:0 -e not-empty -o 'match:status=[1-9]' \
    342  1.5  kre 	    ${TEST_SH} -c \
    343  1.5  kre 		'command eval "if done"; printf %s status=$?'
    344  1.5  kre 
    345  1.5  kre 	atf_check -s not-exit:0 -e not-empty \
    346  1.5  kre 	    -o 'match:status=[1-9]' -o 'not-match:[XY]' ${TEST_SH} -c \
    347  1.5  kre 		 'command eval "printf X; if done; printf Y"
    348  1.5  kre 		  S=$?; printf %s status=$S; exit $S'
    349  1.5  kre 
    350  1.5  kre 	# whether 'X' is output here is (or should be) unspecified.
    351  1.5  kre 	atf_check -s not-exit:0 -e not-empty \
    352  1.5  kre 	    -o 'match:status=[1-9]' -o 'not-match:Y' ${TEST_SH} -c \
    353  1.5  kre 		 'command eval "printf X
    354  1.5  kre 		 		if done
    355  1.5  kre 				printf Y"
    356  1.5  kre 		  S=$?; printf %s status=$S; exit $S'
    357  1.5  kre 
    358  1.5  kre 	if is_netbsd_sh
    359  1.5  kre 	then
    360  1.5  kre 		# but on NetBSD we expect that X to appear...
    361  1.5  kre 		atf_check -s not-exit:0 -e not-empty  -o 'match:X' \
    362  1.5  kre 		    -o 'match:status=[1-9]' -o 'not-match:Y' ${TEST_SH} -c \
    363  1.5  kre 			 'command eval "printf X
    364  1.5  kre 					if done
    365  1.5  kre 					printf Y"
    366  1.5  kre 			  S=$?; printf %s status=$S; exit $S'
    367  1.5  kre 	fi
    368  1.1  kre }
    369  1.1  kre 
    370  1.1  kre atf_test_case exec
    371  1.1  kre exec_head() {
    372  1.1  kre 	atf_set "descr" "Tests the shell special builtin 'exec'"
    373  1.1  kre }
    374  1.1  kre exec_body() {
    375  1.1  kre 	have_builtin exec || return 0
    376  1.1  kre 
    377  1.1  kre 	atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \
    378  1.1  kre 		'exec printf OK; printf BROKEN; exit 3'
    379  1.1  kre 	atf_check -s exit:3 -e empty -o inline:OKOK ${TEST_SH} -c \
    380  1.1  kre 		'(exec printf OK); printf OK; exit 3'
    381  1.1  kre }
    382  1.1  kre 
    383  1.1  kre atf_test_case export
    384  1.1  kre export_head() {
    385  1.1  kre 	atf_set "descr" "Tests the shell builtin 'export'"
    386  1.1  kre }
    387  1.1  kre export_body() {
    388  1.1  kre 	have_builtin export || return 0
    389  1.1  kre 
    390  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR'
    391  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR=abc'
    392  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export V A R'
    393  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
    394  1.1  kre 		'export V A=1 R=2'
    395  1.1  kre 
    396  1.1  kre 	atf_require_prog printenv
    397  1.1  kre 
    398  1.1  kre 	atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \
    399  1.1  kre 		'unset VAR || exit 7; export VAR; printenv VAR'
    400  1.1  kre 	atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \
    401  1.1  kre 		'unset VAR || exit 7; export VAR=; printenv VAR'
    402  1.1  kre 	atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \
    403  1.1  kre 		'unset VAR || exit 7; VAR=; export VAR; printenv VAR'
    404  1.1  kre 	atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \
    405  1.1  kre 		'unset VAR || exit 7; export VAR; VAR=; printenv VAR'
    406  1.1  kre 	atf_check -s exit:0 -e empty -o inline:XYZ\\n ${TEST_SH} -c \
    407  1.1  kre 		'unset VAR || exit 7; export VAR=XYZ; printenv VAR'
    408  1.1  kre 	atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \
    409  1.1  kre 		'VAR=ABC; export VAR; printenv VAR'
    410  1.1  kre 	atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \
    411  1.1  kre 		'unset VAR || exit 7; export VAR; VAR=ABC; printenv VAR'
    412  1.1  kre 	atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
    413  1.1  kre 		'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR'
    414  1.1  kre 	atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
    415  1.1  kre 		'unset VAR || exit 7; export VAR;
    416  1.1  kre 		 VAR=ABC; printenv VAR; VAR=XYZ; printenv VAR'
    417  1.1  kre 
    418  1.3  kre 	# don't check VAR=value, some shells provide meaningless quoting...
    419  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \
    420  1.3  kre 		${TEST_SH} -c \
    421  1.3  kre 			'VAR=foobar ; export VAR ; export -p'
    422  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \
    423  1.3  kre 		${TEST_SH} -c \
    424  1.3  kre 			'export VAR=foobar ; export -p'
    425  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR\$ ${TEST_SH} -c \
    426  1.3  kre 			'unset VAR ; export VAR ; export -p'
    427  1.3  kre 	atf_check -s exit:0 -e empty -o not-match:VAR ${TEST_SH} -c \
    428  1.3  kre 			'export VAR ; unset VAR ; export -p'
    429  1.3  kre 	atf_check -s exit:0 -e empty -o not-match:VAR -o not-match:foobar \
    430  1.3  kre 		${TEST_SH} -c \
    431  1.3  kre 			'VAR=foobar; export VAR ; unset VAR ; export -p'
    432  1.3  kre 
    433  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR= -o match:FOUND=foobar \
    434  1.3  kre 		${TEST_SH} -c \
    435  1.3  kre 			'export VAR=foobar; V=$(export -p);
    436  1.3  kre 			 unset VAR; eval "$V"; export -p;
    437  1.3  kre 			 printf %s\\n FOUND=${VAR-unset}'
    438  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR -o match:FOUND=unset \
    439  1.3  kre 		${TEST_SH} -c \
    440  1.3  kre 			'export VAR; V=$(export -p);
    441  1.3  kre 			 unset VAR; eval "$V"; export -p;
    442  1.3  kre 			 printf %s\\n FOUND=${VAR-unset}'
    443  1.3  kre 
    444  1.1  kre 	atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
    445  1.1  kre 		'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
    446  1.1  kre 		unset VAR; printenv VAR; VAR=PQR; printenv VAR'
    447  1.1  kre 	atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=unset\\nMNO\\n \
    448  1.1  kre 	    ${TEST_SH} -c \
    449  1.1  kre 		'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
    450  1.1  kre 		 unset VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR;
    451  1.1  kre 		 VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR'
    452  1.1  kre }
    453  1.1  kre 
    454  1.1  kre atf_test_case export_nbsd
    455  1.1  kre export_nbsd_head() {
    456  1.1  kre 	atf_set "descr" "Tests NetBSD extensions to the shell builtin 'export'"
    457  1.1  kre }
    458  1.1  kre export_nbsd_body() {
    459  1.1  kre 	have_builtin "export" "" "" "-n foo" ' -n' || return 0
    460  1.1  kre 
    461  1.1  kre 	atf_require_prog printenv
    462  1.1  kre 
    463  1.1  kre 	atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \
    464  1.1  kre 		'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
    465  1.1  kre 		export -n VAR; printenv VAR; VAR=PQR; printenv VAR'
    466  1.1  kre 
    467  1.1  kre 	atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=XYZ\\nMNO\\n \
    468  1.1  kre 	    ${TEST_SH} -c \
    469  1.1  kre 		'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR;
    470  1.1  kre 		 export -n VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR;
    471  1.1  kre 		 VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR'
    472  1.1  kre 
    473  1.1  kre 	have_builtin "export" "" "" -x ' -x' || return 0
    474  1.1  kre 
    475  1.1  kre 	atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \
    476  1.1  kre 		'export VAR=exported; export -x VAR; printenv VAR'
    477  1.1  kre 	atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \
    478  1.1  kre 		'export VAR=exported; export -x VAR; VAR=local; printenv VAR'
    479  1.1  kre 	atf_check -s exit:0 -e empty -o inline:once\\nx\\n ${TEST_SH} -c \
    480  1.1  kre 		'export VAR=exported
    481  1.1  kre 		 export -x VAR
    482  1.1  kre 		 VAR=once printenv VAR
    483  1.1  kre 		 printenv VAR || printf %s\\n x'
    484  1.1  kre 
    485  1.1  kre 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    486  1.1  kre 		'export VAR=exported; export -x VAR; export VAR=FOO'
    487  1.4  kre 
    488  1.4  kre 	have_builtin export '' 'export VAR;' '-q VAR' ' -q'  || return 0
    489  1.4  kre 
    490  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    491  1.4  kre 		'unset VAR; VAR=set; export -q VAR'
    492  1.4  kre 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    493  1.4  kre 		'export VAR=set; export -q VAR'
    494  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    495  1.4  kre 		'VAR=set; RW=set; export -q VAR RW'
    496  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    497  1.4  kre 		'VAR=set; export RO=set; export -q VAR RO'
    498  1.4  kre 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    499  1.4  kre 		'export VAR=set RO=set; export -q VAR RO'
    500  1.4  kre 
    501  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    502  1.4  kre 		'unset VAR; export -q VAR'
    503  1.4  kre 	# next one is the same as the have_builtin test, so "cannot" fail...
    504  1.4  kre 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    505  1.4  kre 		'unset VAR; export VAR; export -q VAR'
    506  1.4  kre 
    507  1.4  kre 	# if we have -q we should also have -p var...
    508  1.4  kre 	# What's more, we are testing NetBSD sh, so we know output format.
    509  1.4  kre 
    510  1.4  kre 	atf_check -s exit:0 -e empty -o match:VAR=foobar \
    511  1.4  kre 		${TEST_SH} -c \
    512  1.4  kre 			'VAR=foobar ; export VAR ; export -p VAR'
    513  1.4  kre 	atf_check -s exit:0 -e empty -o inline:1 \
    514  1.4  kre 		${TEST_SH} -c \
    515  1.4  kre 			'VAR=foobar ; export VAR ;
    516  1.4  kre 			printf %d $(export -p VAR | wc -l)'
    517  1.4  kre 	atf_check -s exit:0 -e empty \
    518  1.4  kre 		-o inline:'export VAR=foobar\nexport OTHER\n' \
    519  1.4  kre 		${TEST_SH} -c \
    520  1.4  kre 			'VAR=foobar; export VAR OTHER; export -p VAR OTHER'
    521  1.4  kre 	atf_check -s exit:0 -e empty \
    522  1.4  kre 		-o inline:'export A=aaa\nexport B\nexport D='"''"'\n' \
    523  1.4  kre 		${TEST_SH} -c \
    524  1.4  kre 			'A=aaa D= C=foo; unset B; export A B D;
    525  1.4  kre 			 export -p A B C D'
    526  1.1  kre }
    527  1.1  kre 
    528  1.1  kre atf_test_case getopts
    529  1.1  kre getopts_head() {
    530  1.1  kre 	atf_set "descr" "Tests the shell builtin 'getopts'"
    531  1.1  kre }
    532  1.1  kre getopts_body() {
    533  1.1  kre 	have_builtin getopts "" "f() {" "a x; }; f -a" || return 0
    534  1.1  kre }
    535  1.1  kre 
    536  1.1  kre atf_test_case jobs
    537  1.1  kre jobs_head() {
    538  1.1  kre 	atf_set "descr" "Tests the shell builting 'jobs' command"
    539  1.1  kre }
    540  1.1  kre jobs_body() {
    541  1.1  kre 	have_builtin jobs || return 0
    542  1.1  kre 
    543  1.1  kre 	atf_require_prog sleep
    544  1.1  kre 
    545  1.1  kre 	# note that POSIX requires that we reference $! otherwise
    546  1.1  kre 	# the shell is not required to remember the process...
    547  1.1  kre 
    548  1.1  kre 	atf_check -s exit:0 -e empty -o match:sleep -o match:Running \
    549  1.1  kre 		${TEST_SH} -c 'sleep 1 & P=$!; jobs; wait'
    550  1.1  kre 	atf_check -s exit:0 -e empty -o match:sleep -o match:Done \
    551  1.1  kre 		${TEST_SH} -c 'sleep 1 & P=$!; sleep 2; jobs; wait'
    552  1.1  kre }
    553  1.1  kre 
    554  1.1  kre atf_test_case read
    555  1.1  kre read_head() {
    556  1.1  kre 	atf_set "descr" "Tests the shell builtin read command"
    557  1.1  kre }
    558  1.1  kre read_body() {
    559  1.1  kre 	have_builtin read "" "echo x|" "var" || return 0
    560  1.1  kre }
    561  1.1  kre 
    562  1.1  kre atf_test_case readonly
    563  1.1  kre readonly_head() {
    564  1.1  kre 	atf_set "descr" "Tests the shell builtin 'readonly'"
    565  1.1  kre }
    566  1.1  kre readonly_body() {
    567  1.1  kre 	have_builtin readonly || return 0
    568  1.1  kre 
    569  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR'
    570  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR=abc'
    571  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A R'
    572  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A=1 R=2'
    573  1.1  kre 
    574  1.1  kre 	atf_check -s exit:0 -e empty -o inline:unset ${TEST_SH} -c \
    575  1.1  kre 		'unset VAR; readonly VAR; printf %s ${VAR-unset}'
    576  1.1  kre 	atf_check -s exit:0 -e empty -o inline:set ${TEST_SH} -c \
    577  1.1  kre 		'unset VAR; readonly VAR=set; printf %s ${VAR-unset}'
    578  1.3  kre 	atf_check -s exit:0 -e empty -o inline:set ${TEST_SH} -c \
    579  1.3  kre 		'VAR=initial; readonly VAR=set; printf %s ${VAR-unset}'
    580  1.1  kre 	atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \
    581  1.1  kre 		'readonly VAR=initial; VAR=new; printf %s "${VAR}"'
    582  1.1  kre 
    583  1.3  kre 	# don't check VAR=value, some shells provide meaningless quoting...
    584  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \
    585  1.3  kre 		${TEST_SH} -c \
    586  1.3  kre 			'VAR=foobar ; readonly VAR ; readonly -p'
    587  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \
    588  1.3  kre 		${TEST_SH} -c \
    589  1.3  kre 			'readonly VAR=foobar ; readonly -p'
    590  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR= -o match:foobar \
    591  1.3  kre 		-o not-match:badvalue ${TEST_SH} -c \
    592  1.3  kre 			'VAR=badvalue; readonly VAR=foobar ; readonly -p'
    593  1.3  kre 	atf_check -s exit:0 -e empty -o match:VAR\$ ${TEST_SH} -c \
    594  1.3  kre 			'unset VAR ; readonly VAR ; readonly -p'
    595  1.3  kre 
    596  1.3  kre 	# checking that readonly -p works (to reset stuff) is a pain...
    597  1.3  kre 	# particularly since not all shells say "readonly..." by default
    598  1.3  kre 	atf_check -s exit:0 -e empty -o match:MYVAR= -o match:FOUND=foobar \
    599  1.3  kre 		${TEST_SH} -c \
    600  1.3  kre 			'V=$(readonly MYVAR=foobar; readonly -p | grep " MYVAR")
    601  1.3  kre 			 unset MYVAR; eval "$V"; readonly -p;
    602  1.3  kre 			 printf %s\\n FOUND=${MYVAR-unset}'
    603  1.3  kre 	atf_check -s exit:0 -e empty -o match:MYVAR\$ -o match:FOUND=unset \
    604  1.3  kre 		${TEST_SH} -c \
    605  1.3  kre 			'V=$(readonly MYVAR; readonly -p | grep " MYVAR")
    606  1.3  kre 			 unset MYVAR; eval "$V"; readonly -p;
    607  1.3  kre 			 printf %s\\n "FOUND=${MYVAR-unset}"'
    608  1.3  kre 	atf_check -s exit:0 -e empty -o match:MYVAR= -o match:FOUND=empty \
    609  1.3  kre 		${TEST_SH} -c \
    610  1.3  kre 			'V=$(readonly MYVAR=; readonly -p | grep " MYVAR")
    611  1.3  kre 			 unset VAR; eval "$V"; readonly -p;
    612  1.3  kre 			 printf %s\\n "FOUND=${MYVAR-unset&}${MYVAR:-empty}"'
    613  1.3  kre 
    614  1.1  kre 	# don't test stderr, some shells inist on generating a message for an
    615  1.1  kre 	# unset of a readonly var (rather than simply having unset make $?=1)
    616  1.1  kre 
    617  1.1  kre 	atf_check -s not-exit:0 -e empty -o empty ${TEST_SH} -c \
    618  1.1  kre 		'unset VAR; readonly VAR=set;
    619  1.1  kre 		 unset VAR 2>/dev/null && printf %s ${VAR:-XX}'
    620  1.1  kre 	atf_check -s not-exit:0 -e ignore -o empty ${TEST_SH} -c \
    621  1.1  kre 		'unset VAR; readonly VAR=set; unset VAR && printf %s ${VAR:-XX}'
    622  1.1  kre 	atf_check -s exit:0 -e ignore -o inline:set ${TEST_SH} -c \
    623  1.1  kre 		'unset VAR; readonly VAR=set; unset VAR; printf %s ${VAR-unset}'
    624  1.1  kre }
    625  1.1  kre 
    626  1.4  kre atf_test_case readonly_nbsd
    627  1.4  kre readonly_nbsd_head() {
    628  1.4  kre 	atf_set "descr" "Tests NetBSD extensions to 'readonly'"
    629  1.4  kre }
    630  1.4  kre readonly_nbsd_body() {
    631  1.4  kre 	have_builtin readonly '' 'readonly VAR;' '-q VAR' ' -q'  || return 0
    632  1.4  kre 
    633  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    634  1.4  kre 		'VAR=set; readonly -q VAR'
    635  1.4  kre 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    636  1.4  kre 		'readonly VAR=set; readonly -q VAR'
    637  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    638  1.4  kre 		'VAR=set; RW=set; readonly -q VAR RW'
    639  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    640  1.4  kre 		'VAR=set; readonly RO=set; readonly -q VAR RO'
    641  1.4  kre 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    642  1.4  kre 		'readonly VAR=set RO=set; readonly -q VAR RO'
    643  1.4  kre 
    644  1.4  kre 	atf_check -s exit:1 -o empty -e empty ${TEST_SH} -c \
    645  1.4  kre 		'unset VAR; readonly -q VAR'
    646  1.4  kre 	# next one is the same as the have_builtin test, so "cannot" fail...
    647  1.4  kre 	atf_check -s exit:0 -o empty -e empty ${TEST_SH} -c \
    648  1.4  kre 		'unset VAR; readonly VAR; readonly -q VAR'
    649  1.4  kre 
    650  1.4  kre 	# if we have -q we should also have -p var...
    651  1.4  kre 	# What's more, we are testing NetBSD sh, so we know output format.
    652  1.4  kre 
    653  1.4  kre 	atf_check -s exit:0 -e empty -o match:VAR=foobar \
    654  1.4  kre 		${TEST_SH} -c \
    655  1.4  kre 			'VAR=foobar ; readonly VAR ; readonly -p VAR'
    656  1.4  kre 	atf_check -s exit:0 -e empty -o inline:1 \
    657  1.4  kre 		${TEST_SH} -c \
    658  1.4  kre 			'VAR=foobar ; readonly VAR ;
    659  1.4  kre 			printf %d $(readonly -p VAR | wc -l)'
    660  1.4  kre 	atf_check -s exit:0 -e empty \
    661  1.4  kre 		-o inline:'readonly VAR=foobar\nreadonly OTHER\n' \
    662  1.4  kre 		${TEST_SH} -c \
    663  1.4  kre 			'VAR=foobar; readonly VAR OTHER; readonly -p VAR OTHER'
    664  1.4  kre 	atf_check -s exit:0 -e empty \
    665  1.4  kre 		-o inline:'readonly A=aaa\nreadonly B\nreadonly D='"''"'\n' \
    666  1.4  kre 		${TEST_SH} -c \
    667  1.4  kre 			'A=aaa D= C=foo; unset B; readonly A B D;
    668  1.4  kre 			 readonly -p A B C D'
    669  1.4  kre }
    670  1.4  kre 
    671  1.1  kre atf_test_case cd_pwd
    672  1.1  kre cd_pwd_head() {
    673  1.1  kre 	atf_set "descr" "Tests the shell builtins 'cd' & 'pwd'"
    674  1.1  kre }
    675  1.1  kre cd_pwd_body() {
    676  1.1  kre 	have_builtin cd "" "HOME=/;" || return 0
    677  1.1  kre 	have_builtin pwd || return 0
    678  1.1  kre }
    679  1.1  kre 
    680  1.1  kre atf_test_case true_false
    681  1.1  kre true_false_head() {
    682  1.1  kre 	atf_set "descr" "Tests the 'true' and 'false' shell builtin commands"
    683  1.1  kre }
    684  1.1  kre true_false_body() {
    685  1.1  kre 	have_builtin true || return 0
    686  1.1  kre 
    687  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c true
    688  1.1  kre 
    689  1.1  kre 	# true is not a special builtin, so errors do not cause exit
    690  1.1  kre 	# but we should still get an error from the broken redirect
    691  1.1  kre 	# and the exit status of true should be false...
    692  1.1  kre 
    693  1.1  kre 	atf_check -s exit:0 -e not-empty -o inline:OK ${TEST_SH} -c \
    694  1.1  kre 		"true >/foo/bar && printf %s NOT-; printf %s OK"
    695  1.1  kre 
    696  1.1  kre 	# and var-assigns should not affect the current sh env
    697  1.1  kre 
    698  1.1  kre 	atf_check -s exit:0 -e empty -o inline:IS-OK ${TEST_SH} -c \
    699  1.1  kre 		'X=OK; X=BROKEN true && printf %s IS-; printf %s "${X}"'
    700  1.1  kre 
    701  1.1  kre 	have_builtin false "" ! || return 0
    702  1.1  kre 
    703  1.1  kre 	atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c false
    704  1.1  kre }
    705  1.1  kre 
    706  1.1  kre atf_test_case type
    707  1.1  kre type_head() {
    708  1.1  kre 	atf_set "descr" "Tests the sh builtin 'type' command"
    709  1.1  kre }
    710  1.1  kre type_body() {
    711  1.1  kre 	have_builtin type "" "" type || return 0
    712  1.1  kre }
    713  1.1  kre 
    714  1.1  kre # This currently has its own t_ulimit - either merge that here,
    715  1.1  kre # or delete this one and keep that...  ulimit -n is also tested in
    716  1.1  kre # the t_redir tests, as that affects the shell's use of file descriptors
    717  1.1  kre atf_test_case ulimit
    718  1.1  kre ulimit_head() {
    719  1.1  kre 	atf_set "descr" "Tests the sh builtin 'ulimit'"
    720  1.1  kre }
    721  1.1  kre ulimit_body() {
    722  1.1  kre 	have_builtin ulimit || return 0
    723  1.1  kre }
    724  1.1  kre 
    725  1.1  kre atf_test_case umask
    726  1.1  kre umask_head() {
    727  1.1  kre 	atf_set "descr" "Tests the sh builtin 'umask'"
    728  1.1  kre }
    729  1.1  kre umask_body() {
    730  1.1  kre 	have_builtin umask || return 0
    731  1.1  kre 
    732  1.1  kre 	atf_require_prog touch
    733  1.1  kre 	atf_require_prog stat
    734  1.1  kre 	atf_require_prog rm
    735  1.1  kre 	atf_require_prog chmod
    736  1.1  kre 
    737  1.1  kre 	reset umask
    738  1.1  kre 
    739  1.1  kre 	# 8 octal digits
    740  1.1  kre 	for M in 0 1 2 3 4 5 6 7
    741  1.1  kre 	do
    742  1.1  kre 	    # Test numbers start: 1 25 49 73 97 121 145 169
    743  1.1  kre 
    744  1.1  kre 	    # 8 combinations of each to test (64 inner loops)
    745  1.1  kre 	    # 3 tests in each loop, hence 192 subtests in all
    746  1.1  kre 
    747  1.1  kre 		# Test numbers from loop above, plus (below) and the next 2
    748  1.1  kre 		#+     1        4        7         10	     13
    749  1.1  kre 	    for T in "0${M}" "00${M}" "0${M}0" "0${M}00" "0${M}${M}" \
    750  1.1  kre 		"0${M}${M}0" "0${M}${M}${M}"  "0${M}0${M}"
    751  1.1  kre 		#+    16          19		  22
    752  1.1  kre 	    do
    753  1.1  kre 		# umask turns bits off, calculate which bits will be on...
    754  1.1  kre 
    755  1.1  kre 		D=$(( 0777 & ~ T ))		# for directories
    756  1.1  kre 		F=$(( $D & ~ 0111 ))		# and files with no 'x' bits
    757  1.1  kre 
    758  1.1  kre 		# Note: $(( )) always produces decimal, so we test that format
    759  1.1  kre 		# (see '%d' in printf of stat result)
    760  1.1  kre 
    761  1.1  kre 		# need chmod or we might have no perm to rmdir TD
    762  1.1  kre 		{ chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || :
    763  1.1  kre 
    764  1.1  kre 		# check that the umask applies to files created by the shell
    765  1.1  kre 		check \
    766  1.1  kre 		  "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \
    767  1.1  kre 				  "$F" 0 "$F is $(printf %#o $F)" # 1 4 7 10 ...
    768  1.1  kre 
    769  1.1  kre 		# and to files created by commands that the shell runs
    770  1.1  kre 		check \
    771  1.1  kre 		  "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \
    772  1.1  kre 				  "$F" 0 "$F is $(printf %#o $F)" # 2 5 8 11 ...
    773  1.1  kre 
    774  1.1  kre 		# and to directories created b ... (directories keep 'x')
    775  1.1  kre 		check \
    776  1.1  kre 		  "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \
    777  1.1  kre 				  "$D" 0 "$D is $(printf %#o $D)" # 3 6 9 12 ...
    778  1.1  kre 	    done
    779  1.1  kre 	done
    780  1.1  kre 
    781  1.1  kre 	# Now add a few more tests with less regular u/g/m masks
    782  1.1  kre 	# In here, include tests where umask value has no leading '0'
    783  1.1  kre 
    784  1.1  kre 	# 10 loops, the same 3 tests in each loop, 30 more subtests
    785  1.1  kre 	# from 193 .. 222
    786  1.1  kre 
    787  1.1  kre 	#        193 196 199  202 205 208 211  214  217 220
    788  1.1  kre 	for T in 013 047 722 0772 027 123 421 0124 0513 067
    789  1.1  kre 	do
    790  1.1  kre 		D=$(( 0777 & ~ 0$T ))
    791  1.1  kre 		F=$(( $D & ~ 0111 ))
    792  1.1  kre 
    793  1.1  kre 		{ chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || :
    794  1.1  kre 
    795  1.1  kre 		check \
    796  1.1  kre 		  "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \
    797  1.1  kre 				  "$F" 0 "$F is $(printf %#o $F)"	# +0
    798  1.1  kre 
    799  1.1  kre 		check \
    800  1.1  kre 		  "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \
    801  1.1  kre 				  "$F" 0 "$F is $(printf %#o $F)"	# +1
    802  1.1  kre 
    803  1.1  kre 		check \
    804  1.1  kre 		  "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \
    805  1.1  kre 				  "$D" 0 "$D is $(printf %#o $D)"	# +2
    806  1.1  kre 	done
    807  1.1  kre 
    808  1.1  kre 	results
    809  1.1  kre }
    810  1.1  kre 
    811  1.1  kre atf_test_case unset
    812  1.1  kre unset_head() {
    813  1.1  kre 	atf_set "descr" "Tests the sh builtin 'unset'"
    814  1.1  kre }
    815  1.1  kre unset_body() {
    816  1.1  kre 	have_builtin unset || return 0
    817  1.1  kre }
    818  1.1  kre 
    819  1.1  kre atf_test_case hash
    820  1.1  kre hash_head() {
    821  1.1  kre 	atf_set "descr" "Tests the sh builtin 'hash' (ash extension)"
    822  1.1  kre }
    823  1.1  kre hash_body() {
    824  1.1  kre 	have_builtin hash || return 0
    825  1.1  kre }
    826  1.1  kre 
    827  1.1  kre atf_test_case jobid
    828  1.1  kre jobid_head() {
    829  1.1  kre 	atf_set "descr" "Tests sh builtin 'jobid' (NetBSD extension)"
    830  1.1  kre }
    831  1.1  kre jobid_body() {
    832  1.1  kre 
    833  1.1  kre 	# have_builtin jobid || return 0	No simple jobid command test
    834  1.1  kre 	$TEST_SH -c '(exit 0)& jobid $!' >/dev/null 2>&1  || {
    835  1.1  kre 		atf_skip "${TEST_SH} has no 'jobid' built-in"
    836  1.1  kre 		return 0
    837  1.1  kre 	}
    838  1.1  kre }
    839  1.1  kre 
    840  1.1  kre atf_test_case let
    841  1.1  kre let_head() {
    842  1.1  kre 	atf_set "descr" "Tests the sh builtin 'let' (common extension from ksh)"
    843  1.1  kre }
    844  1.1  kre let_body() {
    845  1.1  kre 	have_builtin let "" "" 1 || return 0
    846  1.1  kre }
    847  1.1  kre 
    848  1.1  kre atf_test_case local
    849  1.1  kre local_head() {
    850  1.1  kre 	atf_set "descr" "Tests the shell builtin 'local' (common extension)"
    851  1.1  kre }
    852  1.1  kre local_body() {
    853  1.1  kre 	have_builtin local "" "f () {" "X; }; f" || return 0
    854  1.1  kre }
    855  1.1  kre 
    856  1.1  kre atf_test_case setvar
    857  1.1  kre setvar_head() {
    858  1.1  kre 	atf_set "descr" "Tests the shell builtin 'setvar' (BSD extension)"
    859  1.1  kre }
    860  1.1  kre setvar_body() {
    861  1.1  kre 	have_builtin setvar || return 0
    862  1.1  kre 
    863  1.1  kre 	atf_check -s exit:0 -e empty -o inline:foo ${TEST_SH} -c \
    864  1.1  kre 		'unset PQ && setvar PQ foo; printf %s "${PQ-not set}"'
    865  1.1  kre 	atf_check -s exit:0 -e empty -o inline:abcd ${TEST_SH} -c \
    866  1.1  kre 		'for x in a b c d; do setvar "$x" "$x"; done;
    867  1.1  kre 		 printf %s "$a$b$c$d"'
    868  1.1  kre 	atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \
    869  1.1  kre 		'a=1; b=2; c=3; d=4
    870  1.1  kre 		 for x in a b c d; do setvar "$x" ""; done;
    871  1.1  kre 		 printf %s "$a$b$c$d"'
    872  1.1  kre }
    873  1.1  kre 
    874  1.1  kre atf_test_case fdflags
    875  1.1  kre fdflags_head() {
    876  1.1  kre 	atf_set "descr" \
    877  1.1  kre 	   "Tests basic operation of sh builtin 'fdflags' (NetBSD extension)"
    878  1.1  kre }
    879  1.1  kre fdflags_body() {
    880  1.1  kre 	have_builtin fdflags || return 0
    881  1.1  kre }
    882  1.1  kre 
    883  1.1  kre atf_test_case fdflags__s
    884  1.1  kre fdflags__s_head() {
    885  1.1  kre 	atf_set "descr" "Checks setting/clearing flags on file descriptors"
    886  1.1  kre }
    887  1.1  kre fdflags__s_body() {
    888  1.1  kre 	have_builtin fdflags || return 0
    889  1.1  kre }
    890  1.1  kre 
    891  1.1  kre atf_test_case fdflags__v
    892  1.1  kre fdflags__v_head() {
    893  1.1  kre 	atf_set "descr" "Checks verbose operation of fdflags"
    894  1.1  kre }
    895  1.1  kre fdflags__v_body() {
    896  1.1  kre 	have_builtin fdflags || return 0
    897  1.1  kre }
    898  1.1  kre 
    899  1.1  kre atf_test_case fdflags__v_s
    900  1.1  kre fdflags__v_s_head() {
    901  1.1  kre 	atf_set "descr" "tests verbose operation of fdflags -s"
    902  1.1  kre }
    903  1.1  kre fdflags__v_s_body() {
    904  1.1  kre 	have_builtin fdflags || return 0
    905  1.1  kre }
    906  1.1  kre 
    907  1.1  kre atf_test_case fdflags_multiple_fd
    908  1.1  kre fdflags_multiple_fd_head() {
    909  1.1  kre 	atf_set "descr" "Checks operation of fdflags with more than one fd"
    910  1.1  kre }
    911  1.1  kre fdflags_multiple_fd_body() {
    912  1.1  kre 	have_builtin fdflags || return 0
    913  1.1  kre }
    914  1.1  kre 
    915  1.1  kre atf_test_case fdflags_one_flag_at_a_time
    916  1.1  kre fdflags_one_flag_at_a_time_head() {
    917  1.1  kre 	atf_set "descr" "Tests all possible fdflags flags, and combinations"
    918  1.1  kre }
    919  1.1  kre fdflags_one_flag_at_a_time_body() {
    920  1.1  kre 	have_builtin fdflags || return 0
    921  1.1  kre }
    922  1.1  kre 
    923  1.1  kre atf_test_case fdflags_save_restore
    924  1.1  kre fdflags_save_restore_head() {
    925  1.1  kre 	atf_set "descr" 'Verify that fd flags can be saved and restored'
    926  1.1  kre }
    927  1.1  kre fdflags_save_restore_body() {
    928  1.1  kre 	have_builtin fdflags || return 0
    929  1.1  kre }
    930  1.1  kre 
    931  1.1  kre atf_test_case fdflags_names_abbreviated
    932  1.1  kre fdflags_names_abbreviated_head() {
    933  1.1  kre 	atf_set "descr" 'Tests using abbreviated names for fdflags'
    934  1.1  kre }
    935  1.1  kre fdflags_names_abbreviated_body() {
    936  1.1  kre 	have_builtin fdflags || return 0
    937  1.1  kre }
    938  1.1  kre 
    939  1.1  kre atf_test_case fdflags_xx_errors
    940  1.1  kre fdflags_xx_errors_head() {
    941  1.1  kre 	atf_set "descr" 'Check various erroneous fdflags uses'
    942  1.1  kre }
    943  1.1  kre fdflags_xx_errors_body() {
    944  1.1  kre 	have_builtin fdflags || return 0
    945  1.1  kre }
    946  1.1  kre 
    947  1.1  kre 
    948  1.1  kre atf_init_test_cases() {
    949  1.1  kre 
    950  1.1  kre 	# "standard" builtin commands in sh
    951  1.1  kre 
    952  1.1  kre 	# no tests of the "very special" (almost syntax) builtins
    953  1.1  kre 	#  (break/continue/return) - they're tested enough elsewhere
    954  1.1  kre 
    955  1.1  kre 	atf_add_test_case cd_pwd
    956  1.1  kre 	atf_add_test_case colon
    957  1.1  kre 	atf_add_test_case echo
    958  1.1  kre 	atf_add_test_case eval
    959  1.1  kre 	atf_add_test_case exec
    960  1.1  kre 	atf_add_test_case export
    961  1.1  kre 	atf_add_test_case getopts
    962  1.1  kre 	atf_add_test_case jobs
    963  1.1  kre 	atf_add_test_case read
    964  1.1  kre 	atf_add_test_case readonly
    965  1.1  kre 	atf_add_test_case true_false
    966  1.1  kre 	atf_add_test_case type
    967  1.1  kre 	atf_add_test_case ulimit
    968  1.1  kre 	atf_add_test_case umask
    969  1.1  kre 	atf_add_test_case unset
    970  1.1  kre 
    971  1.1  kre 	# exit/wait/set/shift/trap/alias/unalias/. should have their own tests
    972  1.1  kre 	# fc/times/fg/bg/%    are too messy to contemplate for now
    973  1.1  kre 	# command ??  (probably should have some tests)
    974  1.1  kre 
    975  1.1  kre 	# Note that builtin versions of, printf, kill, ... are tested separately
    976  1.1  kre 	# (these are all "optional" builtins)
    977  1.1  kre 	# (echo is tested here because NetBSD sh builtin echo and /bin/echo
    978  1.1  kre 	#  are different)
    979  1.1  kre 
    980  1.1  kre 	atf_add_test_case export_nbsd
    981  1.1  kre 	atf_add_test_case hash
    982  1.1  kre 	atf_add_test_case jobid
    983  1.1  kre 	atf_add_test_case let
    984  1.1  kre 	atf_add_test_case local
    985  1.4  kre 	atf_add_test_case readonly_nbsd
    986  1.1  kre 	atf_add_test_case setvar
    987  1.1  kre 	# inputrc should probably be tested in libedit tests (somehow)
    988  1.1  kre 
    989  1.1  kre 	# fdflags has a bunch of test cases
    990  1.1  kre 
    991  1.1  kre 	# Always run one test, so we get at least "skipped" result
    992  1.1  kre 	atf_add_test_case fdflags
    993  1.1  kre 
    994  1.1  kre 	# but no need to say "skipped" lots more times...
    995  1.1  kre 	have_builtin fdflags available && {
    996  1.1  kre 		atf_add_test_case fdflags__s
    997  1.1  kre 		atf_add_test_case fdflags__v
    998  1.1  kre 		atf_add_test_case fdflags__v_s
    999  1.1  kre 		atf_add_test_case fdflags_multiple_fd
   1000  1.1  kre 		atf_add_test_case fdflags_names_abbreviated
   1001  1.1  kre 		atf_add_test_case fdflags_one_flag_at_a_time
   1002  1.1  kre 		atf_add_test_case fdflags_save_restore
   1003  1.1  kre 		atf_add_test_case fdflags_xx_errors
   1004  1.1  kre 	}
   1005  1.1  kre 	return 0
   1006  1.1  kre }
   1007