Module Name: src Committed By: kre Date: Wed Dec 5 02:45:06 UTC 2018
Modified Files: src/distrib/sets/lists/tests: mi src/tests/bin/sh: Makefile Added Files: src/tests/bin/sh: t_builtins.sh Log Message: Add a new test program to test the "intermediate" shell built in utilities. That is, not the low level ones that look like syntax, but aren't: break/continue/return; not those which are really just external programs that are built in for efficiency (printf, test, and kill - though kill really needs to be built in) - those should all have separate test programs (there is a test here for the built-in echo, as that is an entirely different thing to /bin/echo); and also not those for which there are other tests because of the nature of the built-in (like exit, wait, shift, ...). Lastly not "times" either as that just seems to be too hard to test rationally. There is a test (well, framework) for ulimit and there's also t_ulimit.sh one of those should go, but I am not yet sure which is the best way to reconcile things. Note: many (in fact) most of the test cases added here are either entirely empty (no tests at all, beyond testing that the built-in is in fact a shell built-in) or only very rudimentary tests - assistance in fleshing those out would be welcome (the boilerplate is all here, all that is needed is some actual tests to run...) To generate a diff of this commit: cvs rdiff -u -r1.795 -r1.796 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.13 -r1.14 src/tests/bin/sh/Makefile cvs rdiff -u -r0 -r1.1 src/tests/bin/sh/t_builtins.sh Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/tests/mi diff -u src/distrib/sets/lists/tests/mi:1.795 src/distrib/sets/lists/tests/mi:1.796 --- src/distrib/sets/lists/tests/mi:1.795 Sun Sep 23 13:36:04 2018 +++ src/distrib/sets/lists/tests/mi Wed Dec 5 02:45:05 2018 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.795 2018/09/23 13:36:04 christos Exp $ +# $NetBSD: mi,v 1.796 2018/12/05 02:45:05 kre Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -1270,6 +1270,7 @@ ./usr/tests/bin/sh/dotcmd/while_return_until tests-bin-tests compattestfile,atf ./usr/tests/bin/sh/dotcmd/while_return_while tests-bin-tests compattestfile,atf ./usr/tests/bin/sh/t_arith tests-bin-tests compattestfile,atf +./usr/tests/bin/sh/t_builtins tests-bin-tests compattestfile,atf ./usr/tests/bin/sh/t_cmdsub tests-bin-tests compattestfile,atf ./usr/tests/bin/sh/t_compexit tests-obsolete obsolete ./usr/tests/bin/sh/t_evaltested tests-bin-tests compattestfile,atf Index: src/tests/bin/sh/Makefile diff -u src/tests/bin/sh/Makefile:1.13 src/tests/bin/sh/Makefile:1.14 --- src/tests/bin/sh/Makefile:1.13 Tue Jul 10 06:49:29 2018 +++ src/tests/bin/sh/Makefile Wed Dec 5 02:45:06 2018 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.13 2018/07/10 06:49:29 kre Exp $ +# $NetBSD: Makefile,v 1.14 2018/12/05 02:45:06 kre Exp $ # .include <bsd.own.mk> @@ -8,6 +8,7 @@ TESTSDIR = ${TESTSBASE}/bin/sh TESTS_SUBDIRS += dotcmd TESTS_SH+= t_arith +TESTS_SH+= t_builtins TESTS_SH+= t_cmdsub TESTS_SH+= t_evaltested TESTS_SH+= t_exit Added files: Index: src/tests/bin/sh/t_builtins.sh diff -u /dev/null src/tests/bin/sh/t_builtins.sh:1.1 --- /dev/null Wed Dec 5 02:45:06 2018 +++ src/tests/bin/sh/t_builtins.sh Wed Dec 5 02:45:06 2018 @@ -0,0 +1,793 @@ +# $NetBSD: t_builtins.sh,v 1.1 2018/12/05 02:45:06 kre Exp $ +# +# Copyright (c) 2017 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 the various sh builtin utilities. +# +# Those utilities that are really external programs, which are builtin in +# for (mostly) performance (printf, kill, test, ...), are tested elsewhere. +# We do test the builtin "echo" here as (in NetBSD) it is different than +# the external one. +# +# The (mostly special) builtins which appear to be more syntax than command +# are tested in other test programs, rather than here (break, continue...) +# +# And finally, those which are fundamental to the operation of the shell, +# like wait, set, shift, ... are also tested in other test programs where +# all their operations can be more thoroughly verified. +# +# This leaves those which need to be built in (cd, umask, ...) but whose +# purpose is mostly to alter the environment in which the shell operates +# of that of the commands it runs. These tests act in co-operation with +# other tests exist here (where thy do) by not duplicating tests run +# elsewhere (ulimit is one example) but just adding to those. +# One day these might be unified. +# +# We do test both standard use of the builtins (where they are standard) +# and NetBSD sh extensions (when run on a shell with no support, such tests +# should be skipped.) +# + +# Utility function able to test whether most of the builtins exist in +# the shell being tested. +have_builtin() +{ + ${TEST_SH} -c "( $3 $1 $4 ) >/dev/null 2>&1" && + LC_ALL=C ${TEST_SH} -c \ + 'case "$( (type '"$1"') 2>&1)" in + (*built*) exit 0 ;; + esac + exit 1' || + { + test -z "$2" && atf_skip "${TEST_SH} has no '$1$5' built-in" + return 1; + } + + return 0 +} + +### Helper functions + +nl=' +' +reset() +{ + TEST_NUM=0 + TEST_FAILURES='' + TEST_FAIL_COUNT=0 + TEST_ID="$1" + + # These are used in check() + atf_require_prog tr + atf_require_prog printf + atf_require_prog mktemp +} + +# Test run & validate. +# +# $1 is the command to run (via sh -c) +# $2 is the expected output +# $3 is the expected exit status from sh +# $4 is optional extra data for the error msg (if there is one) +# +# Stderr is exxpected to be empty, unless the expected exit code ($3) is != 0 +# in which case some message there is expected (and nothing is a failure). +# When non-zero exit is expected, we note a different (non-zero) value +# observed, but do not fail the test because of that. + +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" + + # determine what the test generates, preserving trailing \n's + result="$( ${TEST_SH} -c "${CMD}" 2>"${TEMP_FILE}" && printf X )" + STATUS=$? + result="${result%X}" + + + 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" + # or something else is detected as incorrect as well. + 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}" + + if [ "$2" != "${result}" ] + then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM]" + MSG="${MSG} Expected: <<$2>>, received: <<$result>>" + fail=true + fi + + if $fail + then + if [ -n "$4" ]; then + MSG="${MSG}${MSG:+${nl}}[$TEST_NUM] Note: ${4}" + fi + 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] failed: $( + # ATF does not like newlines in messages, so change them... + printf '%s' "${MSG}" | tr '\n' ';' + )" + return 0 +} + +results() +{ + test -n "$1" && atf_expect_fail "$1" + + 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 (of $TEST_NUM) subtests failed - see stderr" +} + +####### End helpers + +atf_test_case colon +colon_head() { + atf_set "descr" "Tests the shell special builtin ':' command" +} +colon_body() { + have_builtin : || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c ":" + + # ':' is a special builtin, so we should exit on redirect error + # and variable assignments should persist (stupid, but it is the rule) + + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + ": >/foo/bar; printf %s No-exit-BUG" + atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \ + 'X=BUG; X=OK : ; printf %s "${X}"' +} + +atf_test_case echo +echo_head() { + atf_set "descr" "Tests the shell builtin version of echo" +} +echo_body() { + have_builtin echo || return 0 + + unset NETBSD_SHELL 2>/dev/null + if test -z "$( ${TEST_SH} -c 'printf %s "${NETBSD_SHELL}"')"; then + atf_skip \ + "${TEST_SH%% *} is not the NetBSD shell, this test is for it alone" + return 0 + fi + + reset echo + + check 'echo "hello world"' "hello world${nl}" 0 + check 'echo hello world' "hello world${nl}" 0 + check 'echo -n "hello world"' "hello world" 0 + check 'IFS=:; echo hello world' "hello world${nl}" 0 + check 'IFS=; echo hello world' "hello world${nl}" 0 + + check 'echo -e "hello world"' "hello world${nl}" 0 + check 'echo -e hello world' "hello world${nl}" 0 + check 'IFS=:; echo -e hello world' "hello world${nl}" 0 + + # only one of the options is used + check 'echo -e -n "hello world"' "-n hello world${nl}" 0 + check 'echo -n -e "hello world"' "-e hello world" 0 + # and only when it is alone + check 'echo -en "hello world"' "-en hello world${nl}" 0 + check 'echo -ne "hello world"' "-ne hello world${nl}" 0 + + # echo is specifically required to *not* support -- + check 'echo -- "hello world"' "-- hello world${nl}" 0 + + # similarly any other unknown option is simply part of the output + for OPT in a b c v E N Q V 0 1 2 @ , \? \[ \] \( \; . \* -help -version + do + check "echo '-${OPT}' foo" "-${OPT} foo${nl}" 0 + done + + # Now test the \\ expansions, with and without -e + + # We rely upon printf %b (tested elsewhere, not only a sh test) + # to verify the output when the \\ is supposed to be expanded. + + for E in '' -e + do + for B in a b c e f n r t v \\ 04 010 012 0177 + do + S="test string with \\${B} in it" + if [ -z "${E}" ]; then + R="${S}${nl}" + else + R="$(printf '%b\nX' "${S}")" + R=${R%X} + fi + check "echo $E '${S}'" "${R}" 0 + done + done + + results +} + +atf_test_case eval +eval_head() { + atf_set "descr" "Tests the shell special builtin 'eval'" +} +eval_body() { + have_builtin eval || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval "exit 0"' + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c 'eval "exit 1"' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'eval exit 0' + + atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \ + 'X=a Y=b Z=c; for V in X Y Z; do eval "printf %s \$$V"; done' + atf_check -s exit:0 -e empty -o inline:abc ${TEST_SH} -c \ + 'X=a Y=b Z=c; for V in X Y Z; do eval printf %s \$$V; done' + atf_check -s exit:0 -e empty -o inline:XYZ ${TEST_SH} -c \ + 'for V in X Y Z; do eval "${V}=${V}"; done; printf %s "$X$Y$Z"' +} + +atf_test_case exec +exec_head() { + atf_set "descr" "Tests the shell special builtin 'exec'" +} +exec_body() { + have_builtin exec || return 0 + + atf_check -s exit:0 -e empty -o inline:OK ${TEST_SH} -c \ + 'exec printf OK; printf BROKEN; exit 3' + atf_check -s exit:3 -e empty -o inline:OKOK ${TEST_SH} -c \ + '(exec printf OK); printf OK; exit 3' +} + +atf_test_case export +export_head() { + atf_set "descr" "Tests the shell builtin 'export'" +} +export_body() { + have_builtin export || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export VAR=abc' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'export V A R' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + 'export V A=1 R=2' + + atf_require_prog printenv + + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR=; printenv VAR' + atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; VAR=; export VAR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; VAR=; printenv VAR' + atf_check -s exit:0 -e empty -o inline:XYZ\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR=XYZ; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; VAR=ABC; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'unset VAR || exit 7; export VAR; + VAR=ABC; printenv VAR; VAR=XYZ; printenv VAR' + + atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + unset VAR; printenv VAR; VAR=PQR; printenv VAR' + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=unset\\nMNO\\n \ + ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + unset VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR; + VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR' +} + +atf_test_case export_nbsd +export_nbsd_head() { + atf_set "descr" "Tests NetBSD extensions to the shell builtin 'export'" +} +export_nbsd_body() { + have_builtin "export" "" "" "-n foo" ' -n' || return 0 + + atf_require_prog printenv + + atf_check -s exit:1 -e empty -o inline:ABC\\nXYZ\\n ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + export -n VAR; printenv VAR; VAR=PQR; printenv VAR' + + atf_check -s exit:0 -e empty -o inline:ABC\\nXYZ\\nVAR=XYZ\\nMNO\\n \ + ${TEST_SH} -c \ + 'VAR=ABC; export VAR; printenv VAR; VAR=XYZ; printenv VAR; + export -n VAR; printf %s\\n "VAR=${VAR-unset}"; printenv VAR; + VAR=PQR; printenv VAR; VAR=MNO; export VAR; printenv VAR' + + have_builtin "export" "" "" -x ' -x' || return 0 + + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \ + 'export VAR=exported; export -x VAR; printenv VAR' + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c \ + 'export VAR=exported; export -x VAR; VAR=local; printenv VAR' + atf_check -s exit:0 -e empty -o inline:once\\nx\\n ${TEST_SH} -c \ + 'export VAR=exported + export -x VAR + VAR=once printenv VAR + printenv VAR || printf %s\\n x' + + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + 'export VAR=exported; export -x VAR; export VAR=FOO' +} + +atf_test_case getopts +getopts_head() { + atf_set "descr" "Tests the shell builtin 'getopts'" +} +getopts_body() { + have_builtin getopts "" "f() {" "a x; }; f -a" || return 0 +} + +atf_test_case jobs +jobs_head() { + atf_set "descr" "Tests the shell builting 'jobs' command" +} +jobs_body() { + have_builtin jobs || return 0 + + atf_require_prog sleep + + # note that POSIX requires that we reference $! otherwise + # the shell is not required to remember the process... + + atf_check -s exit:0 -e empty -o match:sleep -o match:Running \ + ${TEST_SH} -c 'sleep 1 & P=$!; jobs; wait' + atf_check -s exit:0 -e empty -o match:sleep -o match:Done \ + ${TEST_SH} -c 'sleep 1 & P=$!; sleep 2; jobs; wait' +} + +atf_test_case read +read_head() { + atf_set "descr" "Tests the shell builtin read command" +} +read_body() { + have_builtin read "" "echo x|" "var" || return 0 +} + +atf_test_case readonly +readonly_head() { + atf_set "descr" "Tests the shell builtin 'readonly'" +} +readonly_body() { + have_builtin readonly || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly VAR=abc' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A R' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c 'readonly V A=1 R=2' + + atf_check -s exit:0 -e empty -o inline:unset ${TEST_SH} -c \ + 'unset VAR; readonly VAR; printf %s ${VAR-unset}' + atf_check -s exit:0 -e empty -o inline:set ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; printf %s ${VAR-unset}' + atf_check -s not-exit:0 -e not-empty -o empty ${TEST_SH} -c \ + 'readonly VAR=initial; VAR=new; printf %s "${VAR}"' + + # don't test stderr, some shells inist on generating a message for an + # unset of a readonly var (rather than simply having unset make $?=1) + + atf_check -s not-exit:0 -e empty -o empty ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; + unset VAR 2>/dev/null && printf %s ${VAR:-XX}' + atf_check -s not-exit:0 -e ignore -o empty ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; unset VAR && printf %s ${VAR:-XX}' + atf_check -s exit:0 -e ignore -o inline:set ${TEST_SH} -c \ + 'unset VAR; readonly VAR=set; unset VAR; printf %s ${VAR-unset}' +} + +atf_test_case cd_pwd +cd_pwd_head() { + atf_set "descr" "Tests the shell builtins 'cd' & 'pwd'" +} +cd_pwd_body() { + have_builtin cd "" "HOME=/;" || return 0 + have_builtin pwd || return 0 +} + +atf_test_case true_false +true_false_head() { + atf_set "descr" "Tests the 'true' and 'false' shell builtin commands" +} +true_false_body() { + have_builtin true || return 0 + + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c true + + # true is not a special builtin, so errors do not cause exit + # but we should still get an error from the broken redirect + # and the exit status of true should be false... + + atf_check -s exit:0 -e not-empty -o inline:OK ${TEST_SH} -c \ + "true >/foo/bar && printf %s NOT-; printf %s OK" + + # and var-assigns should not affect the current sh env + + atf_check -s exit:0 -e empty -o inline:IS-OK ${TEST_SH} -c \ + 'X=OK; X=BROKEN true && printf %s IS-; printf %s "${X}"' + + have_builtin false "" ! || return 0 + + atf_check -s exit:1 -e empty -o empty ${TEST_SH} -c false +} + +atf_test_case type +type_head() { + atf_set "descr" "Tests the sh builtin 'type' command" +} +type_body() { + have_builtin type "" "" type || return 0 +} + +# This currently has its own t_ulimit - either merge that here, +# or delete this one and keep that... ulimit -n is also tested in +# the t_redir tests, as that affects the shell's use of file descriptors +atf_test_case ulimit +ulimit_head() { + atf_set "descr" "Tests the sh builtin 'ulimit'" +} +ulimit_body() { + have_builtin ulimit || return 0 +} + +atf_test_case umask +umask_head() { + atf_set "descr" "Tests the sh builtin 'umask'" +} +umask_body() { + have_builtin umask || return 0 + + atf_require_prog touch + atf_require_prog stat + atf_require_prog rm + atf_require_prog chmod + + reset umask + + # 8 octal digits + for M in 0 1 2 3 4 5 6 7 + do + # Test numbers start: 1 25 49 73 97 121 145 169 + + # 8 combinations of each to test (64 inner loops) + # 3 tests in each loop, hence 192 subtests in all + + # Test numbers from loop above, plus (below) and the next 2 + #+ 1 4 7 10 13 + for T in "0${M}" "00${M}" "0${M}0" "0${M}00" "0${M}${M}" \ + "0${M}${M}0" "0${M}${M}${M}" "0${M}0${M}" + #+ 16 19 22 + do + # umask turns bits off, calculate which bits will be on... + + D=$(( 0777 & ~ T )) # for directories + F=$(( $D & ~ 0111 )) # and files with no 'x' bits + + # Note: $(( )) always produces decimal, so we test that format + # (see '%d' in printf of stat result) + + # need chmod or we might have no perm to rmdir TD + { chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || : + + # check that the umask applies to files created by the shell + check \ + "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \ + "$F" 0 "$F is $(printf %#o $F)" # 1 4 7 10 ... + + # and to files created by commands that the shell runs + check \ + "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \ + "$F" 0 "$F is $(printf %#o $F)" # 2 5 8 11 ... + + # and to directories created b ... (directories keep 'x') + check \ + "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \ + "$D" 0 "$D is $(printf %#o $D)" # 3 6 9 12 ... + done + done + + # Now add a few more tests with less regular u/g/m masks + # In here, include tests where umask value has no leading '0' + + # 10 loops, the same 3 tests in each loop, 30 more subtests + # from 193 .. 222 + + # 193 196 199 202 205 208 211 214 217 220 + for T in 013 047 722 0772 027 123 421 0124 0513 067 + do + D=$(( 0777 & ~ 0$T )) + F=$(( $D & ~ 0111 )) + + { chmod +rwx TF TFT TD; rm -fr TF TFT TD; } 2>/dev/null || : + + check \ + "umask $T; > TF; printf %d \$(stat -nf %#Lp TF)" \ + "$F" 0 "$F is $(printf %#o $F)" # +0 + + check \ + "umask $T; touch TFT; printf %d \$(stat -nf %#Lp TFT)" \ + "$F" 0 "$F is $(printf %#o $F)" # +1 + + check \ + "umask $T; mkdir TD; printf %d \$(stat -nf %#Lp TD)" \ + "$D" 0 "$D is $(printf %#o $D)" # +2 + done + + results +} + +atf_test_case unset +unset_head() { + atf_set "descr" "Tests the sh builtin 'unset'" +} +unset_body() { + have_builtin unset || return 0 +} + +atf_test_case hash +hash_head() { + atf_set "descr" "Tests the sh builtin 'hash' (ash extension)" +} +hash_body() { + have_builtin hash || return 0 +} + +atf_test_case jobid +jobid_head() { + atf_set "descr" "Tests sh builtin 'jobid' (NetBSD extension)" +} +jobid_body() { + + # have_builtin jobid || return 0 No simple jobid command test + $TEST_SH -c '(exit 0)& jobid $!' >/dev/null 2>&1 || { + atf_skip "${TEST_SH} has no 'jobid' built-in" + return 0 + } +} + +atf_test_case let +let_head() { + atf_set "descr" "Tests the sh builtin 'let' (common extension from ksh)" +} +let_body() { + have_builtin let "" "" 1 || return 0 +} + +atf_test_case local +local_head() { + atf_set "descr" "Tests the shell builtin 'local' (common extension)" +} +local_body() { + have_builtin local "" "f () {" "X; }; f" || return 0 +} + +atf_test_case setvar +setvar_head() { + atf_set "descr" "Tests the shell builtin 'setvar' (BSD extension)" +} +setvar_body() { + have_builtin setvar || return 0 + + atf_check -s exit:0 -e empty -o inline:foo ${TEST_SH} -c \ + 'unset PQ && setvar PQ foo; printf %s "${PQ-not set}"' + atf_check -s exit:0 -e empty -o inline:abcd ${TEST_SH} -c \ + 'for x in a b c d; do setvar "$x" "$x"; done; + printf %s "$a$b$c$d"' + atf_check -s exit:0 -e empty -o empty ${TEST_SH} -c \ + 'a=1; b=2; c=3; d=4 + for x in a b c d; do setvar "$x" ""; done; + printf %s "$a$b$c$d"' +} + +atf_test_case fdflags +fdflags_head() { + atf_set "descr" \ + "Tests basic operation of sh builtin 'fdflags' (NetBSD extension)" +} +fdflags_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags__s +fdflags__s_head() { + atf_set "descr" "Checks setting/clearing flags on file descriptors" +} +fdflags__s_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags__v +fdflags__v_head() { + atf_set "descr" "Checks verbose operation of fdflags" +} +fdflags__v_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags__v_s +fdflags__v_s_head() { + atf_set "descr" "tests verbose operation of fdflags -s" +} +fdflags__v_s_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_multiple_fd +fdflags_multiple_fd_head() { + atf_set "descr" "Checks operation of fdflags with more than one fd" +} +fdflags_multiple_fd_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_one_flag_at_a_time +fdflags_one_flag_at_a_time_head() { + atf_set "descr" "Tests all possible fdflags flags, and combinations" +} +fdflags_one_flag_at_a_time_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_save_restore +fdflags_save_restore_head() { + atf_set "descr" 'Verify that fd flags can be saved and restored' +} +fdflags_save_restore_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_names_abbreviated +fdflags_names_abbreviated_head() { + atf_set "descr" 'Tests using abbreviated names for fdflags' +} +fdflags_names_abbreviated_body() { + have_builtin fdflags || return 0 +} + +atf_test_case fdflags_xx_errors +fdflags_xx_errors_head() { + atf_set "descr" 'Check various erroneous fdflags uses' +} +fdflags_xx_errors_body() { + have_builtin fdflags || return 0 +} + + +atf_init_test_cases() { + + # "standard" builtin commands in sh + + # no tests of the "very special" (almost syntax) builtins + # (break/continue/return) - they're tested enough elsewhere + + atf_add_test_case cd_pwd + atf_add_test_case colon + atf_add_test_case echo + atf_add_test_case eval + atf_add_test_case exec + atf_add_test_case export + atf_add_test_case getopts + atf_add_test_case jobs + atf_add_test_case read + atf_add_test_case readonly + atf_add_test_case true_false + atf_add_test_case type + atf_add_test_case ulimit + atf_add_test_case umask + atf_add_test_case unset + + # exit/wait/set/shift/trap/alias/unalias/. should have their own tests + # fc/times/fg/bg/% are too messy to contemplate for now + # command ?? (probably should have some tests) + + # Note that builtin versions of, printf, kill, ... are tested separately + # (these are all "optional" builtins) + # (echo is tested here because NetBSD sh builtin echo and /bin/echo + # are different) + + atf_add_test_case export_nbsd + atf_add_test_case hash + atf_add_test_case jobid + atf_add_test_case let + atf_add_test_case local + atf_add_test_case setvar + # inputrc should probably be tested in libedit tests (somehow) + + # fdflags has a bunch of test cases + + # Always run one test, so we get at least "skipped" result + atf_add_test_case fdflags + + # but no need to say "skipped" lots more times... + have_builtin fdflags available && { + atf_add_test_case fdflags__s + atf_add_test_case fdflags__v + atf_add_test_case fdflags__v_s + atf_add_test_case fdflags_multiple_fd + atf_add_test_case fdflags_names_abbreviated + atf_add_test_case fdflags_one_flag_at_a_time + atf_add_test_case fdflags_save_restore + atf_add_test_case fdflags_xx_errors + } + return 0 +}