Module Name: src Committed By: kre Date: Wed Sep 5 21:05:40 UTC 2018
Modified Files: src/distrib/sets/lists/tests: mi src/etc/mtree: NetBSD.dist.tests src/tests/usr.bin: Makefile Added Files: src/tests/usr.bin/printf: Makefile printf.sh t_builtin.sh t_command.sh Log Message: Add ATF tests for printf(1) Two new test programs, one for the version of printf in /bin/sh and one for the command /usr/bin/printf (t_builtin and t_command) Each test program has 28 test cases (the same in each) of which currently 27 pass, and 1 is skipped. See the test scripts themselves for more information. To generate a diff of this commit: cvs rdiff -u -r1.793 -r1.794 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.152 -r1.153 src/etc/mtree/NetBSD.dist.tests cvs rdiff -u -r1.25 -r1.26 src/tests/usr.bin/Makefile cvs rdiff -u -r0 -r1.1 src/tests/usr.bin/printf/Makefile \ src/tests/usr.bin/printf/printf.sh src/tests/usr.bin/printf/t_builtin.sh \ src/tests/usr.bin/printf/t_command.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.793 src/distrib/sets/lists/tests/mi:1.794 --- src/distrib/sets/lists/tests/mi:1.793 Tue Aug 21 11:07:40 2018 +++ src/distrib/sets/lists/tests/mi Wed Sep 5 21:05:40 2018 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.793 2018/08/21 11:07:40 christos Exp $ +# $NetBSD: mi,v 1.794 2018/09/05 21:05:40 kre Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -3945,6 +3945,11 @@ ./usr/tests/usr.bin/pr/d_basic.in tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/pr/d_basic.out tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/pr/t_basic tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/printf tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/printf/Atffile tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/printf/Kyuafile tests-usr.bin-tests compattestfile,atf,kyua +./usr/tests/usr.bin/printf/t_builtin tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/printf/t_command tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/rump_server tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/rump_server/Atffile tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/rump_server/Kyuafile tests-usr.bin-tests compattestfile,atf,kyua Index: src/etc/mtree/NetBSD.dist.tests diff -u src/etc/mtree/NetBSD.dist.tests:1.152 src/etc/mtree/NetBSD.dist.tests:1.153 --- src/etc/mtree/NetBSD.dist.tests:1.152 Fri Aug 3 04:24:41 2018 +++ src/etc/mtree/NetBSD.dist.tests Wed Sep 5 21:05:40 2018 @@ -1,4 +1,4 @@ -# $NetBSD: NetBSD.dist.tests,v 1.152 2018/08/03 04:24:41 kamil Exp $ +# $NetBSD: NetBSD.dist.tests,v 1.153 2018/09/05 21:05:40 kre Exp $ ./usr/libdata/debug/usr/tests ./usr/libdata/debug/usr/tests/atf @@ -410,6 +410,7 @@ ./usr/tests/usr.bin/nbperf ./usr/tests/usr.bin/pkill ./usr/tests/usr.bin/pr +./usr/tests/usr.bin/printf ./usr/tests/usr.bin/rump_server ./usr/tests/usr.bin/sdiff ./usr/tests/usr.bin/sed Index: src/tests/usr.bin/Makefile diff -u src/tests/usr.bin/Makefile:1.25 src/tests/usr.bin/Makefile:1.26 --- src/tests/usr.bin/Makefile:1.25 Sun May 14 00:07:07 2017 +++ src/tests/usr.bin/Makefile Wed Sep 5 21:05:40 2018 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.25 2017/05/14 00:07:07 kamil Exp $ +# $NetBSD: Makefile,v 1.26 2018/09/05 21:05:40 kre Exp $ # .include <bsd.own.mk> @@ -8,7 +8,7 @@ TESTSDIR= ${TESTSBASE}/usr.bin TESTS_SUBDIRS= awk basename bzip2 cc cmp config cut \ diff dirname find gdb grep gzip id \ infocmp jot ld m4 make mixerctl mkdep nbperf netpgpverify \ - pkill pr rump_server shmif_dumpbus sdiff \ + pkill pr printf rump_server shmif_dumpbus sdiff \ sed sort tmux tr unifdef uniq vmstat xlint .if ${MKCXX} != "no" Added files: Index: src/tests/usr.bin/printf/Makefile diff -u /dev/null src/tests/usr.bin/printf/Makefile:1.1 --- /dev/null Wed Sep 5 21:05:40 2018 +++ src/tests/usr.bin/printf/Makefile Wed Sep 5 21:05:40 2018 @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.1 2018/09/05 21:05:40 kre Exp $ + +.include <bsd.own.mk> + +TESTSDIR= ${TESTSBASE}/usr.bin/printf + +TESTS_SH= t_builtin t_command + +t_builtin: t_builtin.sh printf.sh + +t_command: t_command.sh printf.sh + +.include <bsd.test.mk> Index: src/tests/usr.bin/printf/printf.sh diff -u /dev/null src/tests/usr.bin/printf/printf.sh:1.1 --- /dev/null Wed Sep 5 21:05:40 2018 +++ src/tests/usr.bin/printf/printf.sh Wed Sep 5 21:05:40 2018 @@ -0,0 +1,1781 @@ +# $NetBSD: printf.sh,v 1.1 2018/09/05 21:05:40 kre Exp $ +# +# Copyright (c) 2018 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. +# + +Running_under_ATF=false +test -n "${Atf_Shell}" && test -n "${Atf_Check}" && Running_under_ATF=true + +Tests= + +# create a test case: +# "$1" is basic test name, "$2" is description +define() +{ + NAME=$1; shift + + if $Running_under_ATF + then + eval "${NAME}_head() { set descr 'Tests printf: $*'; }" + eval "${NAME}_body() { ${NAME} ; }" + else + eval "TEST_${NAME}_MSG="'"$*"' + fi + + Tests="${Tests} ${NAME}" +} + + +# 1st arg is printf format conversion specifier +# other args (if any) are args to that format +# returns success if that conversion specifier is supported, false otherwise +supported() +{ + FMT="$1"; shift + + case "$#" in + 0) set -- 123;; # provide an arg for format to use + esac + + (do_printf >/dev/null 2>&1 "%${FMT}" "$@") +} + +LastErrorTest= + +$Running_under_ATF || { + + # Provide functions to emulate (roughly) what ATF gives us + # (that we actually use) + + atf_skip() { + echo >&2 "${CurrentTest} skipped: ${MSG} $*" + } + atf_fail() { + if [ "${CurrentTest}" != "${LastErrorTest}" ] + then + echo >&2 "======== In Test ${CurrentTest}:" + LastErrorTest="${CurrentTest}" + fi + echo >&2 "${CurrentTest} FAIL: ${MSG} $*" + RVAL=1 + } + atf_require_prog() { + # Just allow progs we want to run to be, or not be, found + return 0 + } +} + +# 1st arg is the result expected, remaining args are handed to do_printf +# to execute, fail if result does not match the expected result (treated +# as a sh pattern), or if do_printf fails +expect() +{ + WANT="$1"; shift + negated=false + + case "${WANT}" in + ('!') WANT="$1"; negated=true; shift;; + esac + + RES="$( do_printf "$@" 2>&3 && echo X )" || atf_fail "$* ... Exit $?" + + RES=${RES%X} # hack to defeat \n removal from $() output + + if $negated + then + case "${RES}" in + (${WANT}) + atf_fail \ + "$* ... Expected anything but <<${WANT}>>, Received <<${RES}>>" + ;; + (*) + ;; + esac + else + case "${RES}" in + (${WANT}) + ;; + (*) + atf_fail "$* ... Expected <<${WANT}>> Received <<${RES}>" + ;; + esac + fi + return 0 +} + +# a variant which allows for two possible results +# It would be nice to have just one function, and allow the pattern +# to contain alternatives ... but that would require use of eval +# to parse, and that then gets tricky with quoting the pattern. +# and we only ever need two (so far anyway), so this is easier... +expect2() +{ + WANT1="$1"; shift + WANT2="$1"; shift + + RES="$( do_printf "$@" 2>&3 && echo X )" || atf_fail "$* ... Exit $?" + + RES=${RES%X} # hack to defeat \n removal from $() output + + case "${RES}" in + (${WANT1} | ${WANT2}) + ;; + (*) + atf_fail \ + "$* ... Expected <<${WANT1}|${WANT2}>> Received <<${RES}>>" + ;; + esac + return 0 +} + +expect_fail() +{ + WANT="$1"; shift # we do not really expect this, but ... + + RES=$( do_printf "$@" 2>/dev/null && echo X ) && { + RES=${RES%X} + case "${RES}" in + (${WANT}) + atf_fail \ + "$* ... success${WANT:+ with expected <<${WANT}>>}" + ;; + ('') + atf_fail "$* ... success (without output)" + ;; + (*) + atf_fail "$* ... success with <<${RES}>> (not <<${WANT}>>)" + ;; + esac + + RVAL=1 + return 0 + } + + RES=$( do_printf "$@" 2>&1 >/dev/null ) + STAT=$? + test -z "${RES}" && + atf_fail "$* ... failed (${STAT}) without error message" + + RES="$( do_printf "$@" 2>/dev/null ; echo X )" + RES=${RES%X} # hack to defeat \n removal from $() output + + case "${RES}" in + (${WANT}) + # All is good, printf failed, sent a message to stderr + # and printed what it should to stdout + ;; + (*) + atf_fail \ + "$* ... should fail with <<${WANT}>> did exit(${STAT}) with <<${RES}>>" + ;; + esac + return 0 +} + +########################################################################## +########################################################################## +# +# Actual tests follow +# +########################################################################## +########################################################################## + +basic() +{ + setmsg basic + + for A in '' -- -@ # hope that '@' is not an option to printf... + do + if (do_printf $A >/dev/null 2>&1) + then + atf_fail "${A:-with no args} successful" + fi + if test -n "$( do_printf 2>/dev/null )" + then + atf_fail "${A:-with no args} produces text on stdout" + fi + if test -z "$( do_printf 2>&1 )" + then + atf_fail "${A:-with no args} no err/usage message" + fi + + test -z "$A" && continue + + if (do_printf "%${A}%" >/dev/null 2>&1) + then + atf_fail "%${A}% successful" + fi + done + + expect abcd abcd + expect % %% + expect xxx%yyy xxx%%yyy + expect -123 -- -123 + + # technically these are all unspecified, but the only rational thing + expect_fail '' %3% + expect_fail '' -123 + expect_fail a a%.% + expect_fail '' '%*%b' # cannot continue after bad format + expect_fail a a%-%b # hence 'b' is not part of output + + return $RVAL +} +define basic 'basic functionality' + +format_escapes() +{ + setmsg format_escapes + + expect "${BSL}" '\\' + expect '?' '\\' # must be just 1 char + + expect "${NL}" '\n' + expect " " '\t' # a literal <tab> in " " + + expect "0" '\60' + expect "1" '\061' + expect "21" '\0621' + expect "${NL}" '\12' + expect "" '\1' + + expect "" '\b' + expect "" '\f' + expect "" '\r' + expect "" '\a' + expect "" '\v' + + expect "hello${NL}world${NL}!!${NL}" 'hello\nworld\n\a\a!!\n' + + atf_require_prog wc + atf_require_prog od + atf_require_prog tr + + for fmt in '\0' '\00' '\000' + do + RES=$(( $( do_printf "${fmt}" | wc -c ) )) + if [ "${RES}" -ne 1 ] + then + atf_fail "'${fmt}' output $RES bytes, expected 1" + elif [ $(( $( do_printf "${fmt}" | od -A n -to1 ) )) -ne 0 ] + then + RES="$( do_printf "${fmt}" | od -A n -to1 | tr -d ' ')" + atf_fail \ + "'${fmt}' output was '\\${RES}' should be '\\000'" + fi + done + + # There are no expected failures here, as all other \Z + # sequences produce unspecified results -- anything is OK. + + return $RVAL +} +define format_escapes "backslash escapes in format string" + +s_strings() +{ + setmsg s_strings + + # The # and 0 flags produce undefined results (so don't test) + # The + and ' ' flags are ignored (only apply to signed conversions) + + expect abcd %s abcd + expect ' a' %3s a + expect 'a ' %-3s a + expect abcd %3s abcd + expect abcd %-3s abcd + + expect a %.1s abcd + expect ab %.2s abcd + expect abc %.3s abcd + expect abcd %.4s abcd + expect abcd %.5s abcd + expect abcd %.6s abcd + + expect ' a' %4.1s abcd + expect ' ab' %4.2s abcd + expect ' abc' %4.3s abcd + expect abcd %4.4s abcd + expect abcd %4.5s abcd + expect abcd %4.6s abcd + + expect ' a' %7.1s abcd + expect 'ab ' %-7.2s abcd + expect ' abc' %7.3s abcd + expect ' abcd' %7.4s abcd + expect 'abcd ' %-7.5s abcd + expect ' abcd' %7.6s abcd + + expect 'aba a' %.2s%.1s%2.1s abcd abcd abcd + + expect 123 %s 123 + expect 1 %.1s 123 + expect 12 %+.2s 123 + expect -1 %+.2s -123 + expect 12 '% .2s' 123 + expect -1 '%+.2s' -123 + + expect '' %s '' + expect ' ' %1s '' + expect ' ' %6s '' + expect ' ' %2.1s '' + expect '' %.0s abcd + expect ' ' %2.0s abcd + expect ' ' %-3.0s abcd + + # %s is just so boring! There are no possible failures to test. + + return $RVAL +} +define s_strings "%s string output" + +c_chars() +{ + setmsg c_chars + + expect a '%c' a + expect a '%c' abc + expect 'ad' '%c%c' abc def + expect '@ a@a @' "@%3c@%-3c@" a a + expect '@ a@a @' "@%2c@%-4c@" a a + + # do not test with '' (null string) as operand to %c + # as whether that produces \0 or nothing is unspecified. + # (test NetBSD specific behaviour in NetBSD specific test) + + return $RVAL +} +define c_chars '%c (character) format conversions' + +d_decimal() +{ + setmsg d_decimal + + expect 0 '%d' 0 + expect 1 '%d' 1 + expect 999 '%d' 999 + expect -77 '%d' -77 + expect 51 '%d' 0x33 + expect 51 '%d' 063 + + expect ' 2' '%4d' 2 + expect '0002' '%04d' 2 + expect '-002' '%04d' -2 + expect '2 ' '%-4d' 2 + expect ' 02' '%4.2d' 2 + expect ' 22' '%4.2d' 22 + expect ' 222' '%4.2d' 222 + expect '2222' '%4.2d' 2222 + expect '22222' '%4.2d' 22222 + expect ' -02' '%4.2d' -2 + expect '02 ' '%-4.2d' 2 + expect '-02 ' '%-4.2d' -2 + expect '22 ' '%-4.2d' 22 + expect '222 ' '%-4.2d' 222 + expect '2222' '%-4.2d' 2222 + expect '22222' '%-4.2d' 22222 + expect 1 '%.0d' 1 + expect '' '%.0d' 0 + expect '' '%.d' 0 + expect ' ' '%3.d' 0 + expect ' ' '%-4.d' 0 + expect ' ' '%05.d' 0 + + expect 65 '%d' "'A" + expect 065 '%03d' "'A" + expect 49 '%d' "'1" + expect 45 '%d' "'-1" + expect 43 '%d' "'+1" + + expect 68 '%d' '"D' + expect 069 '%03d' '"E' + expect 51 '%d' '"3' + expect 45 '%d' '"-3' + expect 43 '%d' '"+3' + + expect -1 '% d' -1 + expect ' 1' '% d' 1 + expect -1 '% 1d' -1 + expect ' 1' '% 1d' 1 + expect -1 '% 0d' -1 + expect ' 1' '% 0d' 1 + expect ' -1' '% 5d' -1 + expect ' 1' '% 5d' 1 + expect ' 01' '%0 3d' 1 + expect '-01' '%0 3d' -1 + expect ' 03' '% 4.2d' 3 + expect ' -03' '% 4.2d' -3 + + expect -1 '%+d' -1 + expect +1 '%+d' 1 + expect ' -7' '%+3d' -7 + expect ' +7' '%+3d' 7 + expect ' -02' '%+4.2d' -2 + expect ' +02' '%+4.2d' 2 + expect '-09 ' '%-+4.2d' -9 + expect '+09 ' '%+-4.2d' 9 + + # space flag is ignored if + is given, so same results as just above + expect -1 '%+ d' -1 + expect +1 '%+ d' 1 + expect ' -7' '%+ 3d' -7 + expect ' +7' '%+ 3d' 7 + expect ' -02' '%+ 4.2d' -2 + expect ' +02' '%+ 4.2d' 2 + expect '-09 ' '%- +4.2d' -9 + expect '+09 ' '% +-4.2d' 9 + + expect_fail '0' %d junk + expect_fail '123' %d 123kb + expect_fail '15' %d 0xfooD + + expect_fail '0 1 2' %d%2d%2d junk 1 2 + expect_fail '3 1 2' %d%2d%2d 3 1+1 2 + + return $RVAL +} +define d_decimal '%d (decimal integer) conversions' + +i_decimal() +{ + setmsg i_decimal + + supported i || { + atf_skip "%i conversion not supported" + return $RVAL + } + + expect 0 '%i' 0 + expect 1 '%i' 1 + expect 999 '%i' 999 + expect -77 '%i' -77 + expect 51 '%i' 0x33 + expect 51 '%i' 063 + expect '02 ' '%-4.2i' 2 + expect ' +02' '%+ 4.2i' 2 + + expect_fail '0' %i x22 + expect_fail '123' %i 123Mb + expect_fail '15' %i 0xfooD + + return $RVAL +} +define i_decimal '%i (decimal integer) conversions' + +u_unsigned() +{ + setmsg u_unsigned + + # Only tests of negative numbers are that we do not + # fail, and do not get a '-' in the result + + # This is because the number of bits available is not defined + # so we cannot anticipate what value a negative number will + # produce when interpreted as unsigned (unlike hex and octal + # where we can at least examine the least significant bits) + + expect 0 '%u' 0 + expect 1 '%u' 1 + expect 999 '%u' 999 + expect 51 '%u' 0x33 + expect 51 '%u' 063 + + expect ! '-*' '%u' -77 + + expect ' 2' '%4u' 2 + expect '0002' '%04u' 2 + expect '2 ' '%-4u' 2 + expect ' 02' '%4.2u' 2 + expect ' 22' '%4.2u' 22 + expect ' 222' '%4.2u' 222 + expect '2222' '%4.2u' 2222 + expect '22222' '%4.2u' 22222 + expect '02 ' '%-4.2u' 2 + expect '22 ' '%-4.2u' 22 + expect '222 ' '%-4.2u' 222 + expect '2222' '%-4.2u' 2222 + expect '22222' '%-4.2u' 22222 + expect 1 '%.0u' 1 + expect '' '%.0u' 0 + expect '' '%.u' 0 + expect ' ' '%3.u' 0 + expect ' ' '%-4.u' 0 + expect ' ' '%05.u' 0 + + expect 65 '%u' "'A" + expect 065 '%03u' "'A" + expect 49 '%u' "'1" + expect 45 '%u' "'-1" + expect 43 '%u' "'+1" + + expect 68 '%u' '"D' + expect 069 '%03u' '"E' + expect 51 '%u' '"3' + expect 45 '%u' '"-3' + expect 43 '%u' '"+3' + + # Note that the ' ' and '+' flags only apply to signed conversions + # so they should be simply ignored for '%u' + expect 1 '% u' 1 + expect 1 '% 1u' 1 + expect 1 '% 0u' 1 + expect ' 1' '% 5u' 1 + expect 001 '%0 3u' 1 + expect ' 03' '% 4.2u' 3 + + expect ! '-*' '% u' -1 + + expect 1 '%+u' 1 + expect ' 7' '%+3u' 7 + expect ' 02' '%+4.2u' 2 + expect '09 ' '%+-4.2u' 9 + + expect ! '-*' '%+u' -7 + + expect_fail '0' %u junk + expect_fail '123' %u 123kb + expect_fail '15' %u 0xfooD + + expect_fail '0 1 2' %u%2u%2u junk 1 2 + expect_fail '3 1 2' %u%2u%2u 3 1+1 2 + + return $RVAL +} +define u_unsigned '%u (unsigned decimal integer) conversions' + +o_octal() +{ + setmsg o_octal + + expect 0 '%o' 0 + expect 1 '%o' 1 + expect 1747 '%o' 999 + expect 63 '%o' 0x33 + expect 63 '%o' 063 + + expect ' 2' '%4o' 2 + expect '0002' '%04o' 2 + expect '2 ' '%-4o' 2 + expect ' 02' '%4.2o' 2 + expect '02 ' '%-4.2o' 2 + expect 1 '%.0o' 1 + expect '' '%.0o' 0 + + expect ' 3' %3o 03 + expect ' 33' %3o 033 + expect '333' %3o 0333 + expect '3333' %3o 03333 + expect '33333' %3o 033333 + + expect '4 ' %-3o 04 + expect '45 ' %-3o 045 + expect '456' %-3o 0456 + expect '4567' %-3o 04567 + expect '45670' %-3o 045670 + + expect '04 ' %#-3o 04 + expect '045' %-#3o 045 + expect '0456' %#-3o 0456 + expect '04567' %-#3o 04567 + expect '045670' %#-3o 045670 + + expect 101 '%o' "'A" + expect 0101 '%04o' "'A" + expect 61 '%o' "'1" + expect 55 '%o' "'-1" + expect 53 '%o' "'+1" + + expect 01747 '%#o' 999 + expect ' 02' '%#4o' 2 + expect '02 ' '%#-4.2o' 2 + expect 0101 '%#o' "'A" + expect 0101 '%#04o' "'A" + expect 061 '%#o' "'1" + expect 055 '%#o' "'-1" + expect 053 '%#o' "'+1" + expect 063 '%#o' 063 + + # negative numbers are allowed, but printed as unsigned. + # Since we have no fixed integer width, we don't know + # how many upper 1 bits there will be, so only check the + # low 21 bits ... + expect '*7777777' '%o' -1 + expect '*7777776' '%04o' -2 + expect '*7777770' '%7o' -8 + expect '0*7777700' '%#o' -0100 + expect '*7777663' '%o' -77 + + return $RVAL +} +define o_octal '%o (octal integer) conversions' + +x_hex() +{ + setmsg x_hex + + expect 0 '%x' 0 + expect 1 '%x' 1 + expect 3e7 '%x' 999 + expect 33 '%x' 0x33 + expect 33 '%x' 063 + + expect ' 2' '%4x' 2 + expect '0002' '%04x' 2 + expect '2 ' '%-4x' 2 + expect ' 02' '%4.2x' 2 + expect '02 ' '%-4.2x' 2 + expect 1 '%.0x' 1 + expect '' '%.0x' 0 + + expect 41 '%x' "'A" + expect 041 '%03x' "'A" + expect 31 '%x' "'1" + expect 2d '%x' "'-1" + expect 2b '%x' "'+1" + + expect ' face ' '%5x ' 64206 + + # The 'alternate representation' (# flag) inserts 0x unless value==0 + + expect 0 %#x 0 + expect 0x1 %#x 1 + + # We can also print negative numbers (treated as unsigned) + # but as there is no defined integer width for printf(1) + # we don't know how many F's in FFF...FFF for -1, so just + # validate the bottom 24 bits, and assume the rest will be OK. + # (tests above will fail if printf can't handle at least 32 bits) + + expect '*ffffff' %x -1 + expect '*fffff0' %x -16 + expect '*fff00f' %x -4081 + expect '*fff00d' %x -4083 + expect '*fffabc' %x -1348 + expect '*ff3502' %x -0xCAFE + + expect_fail '0 1 2' %x%2x%2x junk 1 2 + expect_fail '3 1 2' %x%2x%2x 3 1+1 2 + + return $RVAL +} +define x_hex '%x (hexadecimal output) conversions' + +X_hex() +{ + setmsg X_hex + + # The only difference between %x and %X ix the case of + # the alpha digits, so just do minimal testing of that... + + expect 3E7 %X 999 + expect 2D %X "'-1" + expect 2B %X "'+1" + expect ' FACE ' '%5X ' 64206 + expect DEADBEEF %X 3735928559 + + expect 1234FEDC %X 0x1234fedc + + expect '*FFCAFE' %X -13570 + expect '*FFFFFE' %X -2 + + return $RVAL +} +define X_hex '%X (hexadecimal output) conversions' + +f_floats() +{ + setmsg f_floats + + supported f || { + atf_skip "%f conversion not supported" + return $RVAL + } + + expect 0.000000 %f 0 + expect 1.000000 %f 1 + expect 1.500000 %f 1.5 + expect -1.000000 %f -1 + expect -1.500000 %f -1.5 + + expect 44.000000 %f 44 + expect -43.000000 %f -43 + expect '3.33333?' %f 3.333333333333333 + expect '0.78539?' %f .7853981633974483 + expect '0.00012?' %f .000123456789 + expect '1234.56789?' %f 1234.56789 + + expect 0 %.0f 0 + expect 1 %.0f 1 + expect 1. %#.0f 1.1 + expect 0. %#.0f 0 + expect 1. %#.0f 1 + expect 1. %#.0f 1.2 + + expect 0.0 %.1f 0 + expect 1.0 %.1f 1 + expect 1.1 %#.1f 1.1 + expect 0.0 %#.1f 0 + expect 1.2 %#.1f 1.2 + + expect ' 0.0' %6.1f 0 + expect ' 1.0' %6.1f 1 + expect ' -1.0' %6.1f -1 + + expect '0000.0' %06.1f 0 + expect '0001.0' %06.1f 1 + expect '-001.0' %06.1f -1 + + expect ' +0.0' %+6.1f 0 + expect ' +1.0' %+6.1f 1 + expect ' -1.0' %+6.1f -1 + + expect ' 0.0' '% 6.1f' 0 + expect ' 1.0' '% 6.1f' 1 + expect ' -1.0' '% 6.1f' -1 + + expect ' 000.0' '%0 6.1f' 0 + expect ' 001.0' '% 06.1f' 1 + expect '-001.0' '%0 6.1f' -1 + + expect '+000.0' '%0+6.1f' 0 + expect '+001.0' '%+06.1f' 1 + expect '-001.0' '%0+6.1f' -1 + + expect '0000000.00' %010.2f 0 + expect '-000009.00' %010.2f -9 + + expect '0.0 ' %-10.1f 0 + expect '1.0 ' %-10.1f 1 + expect '-1.0 ' %-10.1f -1 + + expect '0.00 ' %-10.2f 0 + expect '-9.00 ' %-10.2f -9 + + expect '0.0 ' %-010.1f 0 + expect '1.0 ' %-010.1f 1 + expect '-1.0 ' %-010.1f -1 + + expect '0.00 ' %-6.2f 0 + expect '-9.00 ' %-6.2f -9 + + expect '0.00 ' %-010.2f 0 + expect '-9.00 ' %-010.2f -9 + + expect ' 0' %7.0f 0 + expect '1 ' %-7.0f 1 + expect ' 0.' %#7.0f 0 + expect ' 1.' %#7.0f 1 + expect ' 1.' %#7.0f 1.1 + expect ' 1.' %#7.0f 1.2 + expect ' -1.' %#7.0f -1.2 + expect '1. ' %-#7.0f 1.1 + expect '0. ' %#-7.0f 0 + expect '1. ' %-#7.0f 1 + expect '1. ' %#-7.0f 1.2 + expect '-1. ' %#-7.0f -1.2 + expect ' +0' %+7.0f 0 + expect '+1 ' %-+7.0f 1 + expect ' +1.' %+#7.0f 1.1 + expect ' +0.' %#+7.0f 0 + expect ' +1.' %+#7.0f 1 + expect ' +1.' %#+7.0f 1.2 + expect ' -1.' %#+7.0f -1.2 + expect ' 0' '% 7.0f' 0 + expect ' 1 ' '%- 7.0f' 1 + expect '-1 ' '%- 7.0f' -1 + expect ' 1.' '% #7.0f' 1.1 + expect ' 0.' '%# 7.0f' 0 + expect ' 1.' '% #7.0f' 1 + expect ' 1.' '%# 7.0f' 1.2 + expect ' -1.' '%# 7.0f' -1.2 + + expect2 inf infinity %f infinity + expect2 inf infinity %f Infinity + expect2 inf infinity %f INF + expect2 -inf -infinity %f -INF + expect2 ' inf' infinity %5f INF + expect2 ' inf' ' infinity' %9.4f INF + expect2 'inf ' 'infinity ' %-11.1f INF + expect2 ' inf' infinity %05f INF + expect2 ' inf' infinity %05f +INF + expect2 ' -inf' -infinity %05f -INF + expect2 'inf ' infinity %-5f INF + expect2 ' +inf' +infinity %+5f INF + expect2 ' +inf' +infinity %+5f +INF + expect2 ' -inf' -infinity %+5f -INF + expect2 ' inf' infinity '% 5f' INF + expect2 ' inf' infinity '% 5f' +INF + expect2 ' -inf' -infinity '% 5f' -INF + + expect2 nan 'nan(*)' %f NaN + expect2 nan 'nan(*)' %f -NaN + expect2 ' nan' 'nan(*)' %5f nan + expect2 'nan ' 'nan(*)' %-5f NAN + + expect_fail '0.0 1.0 2.0' %.1f%4.1f%4.1f junk 1 2 + expect_fail '3.0 1.0 2.0' %.1f%4.1f%4.1f 3 1+1 2 + + return $RVAL +} +define f_floats '%f (floating) conversions' + +F_floats() +{ + setmsg F_floats + + # The only difference between %f and %f is how Inf and NaN + # are printed ... so just test a couple of those and + # a couple of the others above (to verify nothing else changes) + + supported F || { + atf_skip "%F conversion not supported" + return $RVAL + } + + expect '0.78539?' %F .7853981633974483 + expect '0.00012?' %F .000123456789 + expect '1234.56789?' %F 1234.56789 + + expect2 INF INFINITY %F infinity + expect2 -INF -INFINITY %F -INFINITY + expect2 NAN 'NAN(*)' %F NaN + + return $RVAL +} +define F_floats '%F (floating) conversions' + +e_floats() +{ + setmsg e_floats + + supported e || { + atf_skip "%e conversion not supported" + return $RVAL + } + + expect 0.000000e+00 %e 0 + expect 1.000000e+00 %e 1 + expect 1.500000e+00 %e 1.5 + expect -1.000000e+00 %e -1 + expect -1.500000e+00 %e -1.5 + + expect 4.400000e+01 %e 44 + expect -4.300000e+01 %e -43 + expect '3.33333?e+00' %e 3.333333333333333 + expect '7.85398?e-01' %e .7853981633974483 + expect '1.23456?e-04' %e .000123456789 + expect '1.23456?e+03' %e 1234.56789 + + expect 0e+00 %.0e 0 + expect 1e+00 %.0e 1 + expect 1.e+00 %#.0e 1.1 + expect 0.e+00 %#.0e 0 + expect 1.e+00 %#.0e 1 + expect 1.e+00 %#.0e 1.2 + + expect 0.0e+00 %.1e 0 + expect 1.0e+00 %.1e 1 + expect 1.1e+00 %#.1e 1.1 + expect 0.0e+00 %#.1e 0 + expect 1.2e+00 %#.1e 1.2 + + expect ' 0.0e+00' %10.1e 0 + expect ' 1.0e+00' %10.1e 1 + expect ' -1.0e+00' %10.1e -1 + + expect '0000.0e+00' %010.1e 0 + expect '0001.0e+00' %010.1e 1 + expect '-001.0e+00' %010.1e -1 + + expect ' +0.0e+00' %+10.1e 0 + expect ' +1.0e+00' %+10.1e 1 + expect ' -1.0e+00' %+10.1e -1 + + expect ' 0.0e+00' '% 10.1e' 0 + expect ' 1.0e+00' '% 10.1e' 1 + expect ' -1.0e+00' '% 10.1e' -1 + + expect ' 000.0e+00' '%0 10.1e' 0 + expect ' 001.0e+00' '% 010.1e' 1 + expect '-001.0e+00' '%0 10.1e' -1 + + expect '000.00e+00' %010.2e 0 + expect '-09.00e+00' %010.2e -9 + + expect '0.0e+00 ' %-10.1e 0 + expect '1.0e+00 ' %-10.1e 1 + expect '-1.0e+00 ' %-10.1e -1 + + expect '+0.0e+00 ' %-+10.1e 0 + expect '+1.0e+00 ' %+-10.1e 1 + expect '-1.0e+00 ' %+-10.1e -1 + + expect ' +0.0e+00' '%+ 10.1e' 0 + expect ' +1.0e+00' '% +10.1e' 1 + expect ' -1.0e+00' '%+ 10.1e' -1 + + expect '0.00e+00 ' %-10.2e 0 + expect '-9.00e+00 ' %-10.2e -9 + + expect '0.0e+00 ' %-010.1e 0 + expect '1.0e+00 ' %0-10.1e 1 + expect '-1.0e+00 ' %-010.1e -1 + + expect '0.00e+00 ' %-010.2e 0 + expect '-9.00e+00 ' %-010.2e -9 + + expect ' 0e+00' %7.0e 0 + expect '1e+00 ' %-7.0e 1 + expect ' 1.e+00' %#7.0e 1.1 + expect ' 0.e+00' %#7.0e 0 + expect ' 1.e+00' %#7.0e 1 + expect ' 1.e+00' %#7.0e 1.2 + expect '-1.e+00' %#7.0e -1.2 + expect '1.e+00 ' %-#7.0e 1.1 + expect '0.e+00 ' %#-7.0e 0 + expect '1.e+00 ' %-#7.0e 1 + expect '1.e+00 ' %#-7.0e 1.2 + expect '-1.e+00' %#-7.0e -1.2 + expect ' +0e+00' %+7.0e 0 + expect '+1e+00 ' %-+7.0e 1 + expect '+1.e+00' %+#7.0e 1.1 + expect '+0.e+00' %#+7.0e 0 + expect '+1.e+00' %+#7.0e 1 + expect '+1.e+00' %#+7.0e 1.2 + expect '-1.e+00' %#+7.0e -1.2 + expect ' 0e+00' '% 7.0e' 0 + expect ' 1e+00 ' '%- 7.0e' 1 + expect '-1e+00 ' '%- 7.0e' -1 + expect ' 1.e+00' '% #7.0e' 1.1 + expect ' 0.e+00' '%# 7.0e' 0 + expect ' 1.e+00' '% #7.0e' 1 + expect ' 1.e+00' '%# 7.0e' 1.2 + expect '-1.e+00' '%# 7.0e' -1.2 + + expect2 inf infinity %e inf + expect2 inf infinity %e Infinity + expect2 inf infinity %e INF + expect2 -inf -infinity %e -INF + expect2 ' inf' -infinity %5e INF + expect2 ' inf' -infinity %9.4e INF + expect2 ' inf' infinity %05e INF + expect2 ' inf' infinity %05e +INF + expect2 ' -inf' -infinity %05e -INF + expect2 'inf ' infinity %-5e INF + expect2 ' +inf' +infinity %+5e INF + expect2 ' +inf' +infinity %+5e +INF + expect2 ' -inf' -infinity %+5e -INF + expect2 ' inf' infinity '% 5e' INF + expect2 ' inf' infinity '% 5e' +INF + expect2 ' -inf' -infinity '% 5e' -INF + + expect2 nan 'nan(*)' %e NaN + expect2 nan 'nan(*)' %e -NaN + expect2 ' nan' 'nan(*)' %5e nan + expect2 'nan ' 'nan(*)' %-5e NAN + + expect 6.500000e+01 '%e' "'A" + expect 6.5e+01 '%.1e' "'A" + expect 5e+01 '%.0e' "'1" + expect 4.50e+01 '%.2e' "'-1" + expect 4.300e+01 '%.3e' "'+1" + + expect_fail 0.000000e+00 '%e' NOT-E + expect_fail 1.200000e+00 '%e' 1.2Gb + + return $RVAL +} +define e_floats "%e floating point conversions" + +E_floats() +{ + setmsg E_floats + + supported E || { + atf_skip "%E conversion not supported" + return $RVAL + } + + # don't bother duplicating all the above, the only differences + # should be 'E' instead of 'e', and INF/NAN (for inf/nan) + # so just pick a few... + + expect 0.000000E+00 %E 0 + expect -4.300000E+01 %E -43 + expect 1E+00 %.0E 1 + expect 1.E+00 %#.0E 1 + expect '-9.00E+00 ' %-010.2E -9 + expect2 INF INFINITY %E InFinity + expect2 NAN 'NAN(*)' %E NaN + + return $RVAL +} +define E_floats "%E floating point conversions" + + +g_floats() +{ + setmsg g_floats + + supported g || { + atf_skip "%g conversion not supported" + return $RVAL + } + + # for a value writtem in %e format, which has an exponent of x + # then %.Pg will produce 'f' format if x >= -4, and P > x, + # otherwise it produces 'e' format. + # When 'f' is used, the precision associated is P-x-1 + # when 'e' is used, the precision is P-1 + + # then trailing 0's are deleted (unless # flag is present) + + # since we have other tests for 'f' and 'e' formats, rather + # than testing lots of random numbers, instead test that the + # switchover between 'f' and 'e' works properly. + + expect 1 %.1g 1 # p = 1, x = 0 : %.0f + expect 0.5 %.1g 0.5 # p = 1, x = -1: %.1f + expect 1 %.2g 1 # p = 2, x = 0 : %.1f + expect 0.5 %.2g 0.5 # p = 2, x = -1: %.2f + + expect 1 %g 1 # p = 6, x = 0 : %.5f + expect -0.5 %g -0.5 # p = 6, x = -1: %.6f + + expect 0.001234 %.4g 0.001234 # p= 4, x = -3: %.6f + + expect 9999 %.4g 9999 # p = 4, x = 3 : %.0f + expect 9999 %.5g 9999 # p = 5, x = 3 : %.1f + + expect 1. %#.1g 1 # p = 1, x = 0 : %.0f + expect 0.5 %#.1g 0.5 # p = 1, x = -1: %.1f + expect 1.0 %#.2g 1 # p = 2, x = 0 : %.1f + expect 0.50 %#.2g 0.5 # p = 2, x = -1: %.2f + + expect 1.00000 %#g 1 # p = 6, x = 0 : %.5f + expect -0.500000 %#g -0.5 # p = 6, x = -1: %.6f + + expect 0.001234 %#.4g 0.001234 # p= 4, x = -3: %.6f + + expect 9999. %#.4g 9999 # p = 4, x = 3 : %.0f + expect 9999.0 %#.5g 9999 # p = 5, x = 3 : %.1f + + expect 4.4?e+03 %.3g 4444 # p = 3, x = 3 : %.2e + expect 1.2e-05 %.2g 0.000012 # p = 2, x = -5: $.1e + + expect2 inf infinity %g Infinity + expect2 -inf -infinity %g -INF + expect2 nan 'nan(*)' %g NaN + + return $RVAL +} +define g_floats '%g (floating) conversions' + +G_floats() +{ + setmsg G_floats + + supported G || { + atf_skip "%G conversion not supported" + return $RVAL + } + + # 'G' uses 'F' or 'E' instead or 'f' or 'e'. + + # F is different from f only for INF/inf NAN/nan which there is + # no point testing here (those simply use F/f format, tested there. + # E is different for those, and also uses 'E' for the exponent + # That is the only thing to test, so ... + + expect 1.2E-05 %.2G 0.000012 # p = 2, x = -5: $.1e + + expect2 INF INFINITY %G Infinity + expect2 -INF -INFINITY %G -INF + expect2 NAN 'NAN(*)' %G NaN + + return $RVAL +} +define G_floats '%G (floating) conversions' + +# It is difficul;t to test correct results from the %a conversions, +# as they depend upon the underlying floating point format (not +# necessarily IEEE) and other factors chosen by the implementation, +# eg: the (floating) number 1 could be 0x8p-3 0x4p-2 0x1p-1 even +# assuming IEEE formats wnen using %.0a. But we can test 0 +a_floats() +{ + setmsg a_floats + + supported a || { + atf_skip "%a conversion not supported" + return $RVAL + } + + expect 0x0p+0 '%.0a' 0 + expect 0x0.p+0 '%#.0a' 0 + expect 0x0.000p+0 '%.3a' 0 + expect '0x?.*p+*' '%a' 123 + expect '0x?.*p-*' '%a' 0.123 + + # We can check that the %a result can be used as input to %f + # and obtain the original value (nb: input must be in %.4f format) + + for VAL in 1.0000 2.0000 3.0000 4.0000 0.5000 0.1000 1000.0000 \ + 777777.0000 0.1234 -1.0000 -0.2500 -123.4567 + do + A_STRING=$( do_printf '%a' "${VAL}" 2>&3 ) + + expect "${VAL}" "%.4f" "${A_STRING}" + done + + expect_fail 0x0p+0 %a trash + expect_fail 0x0.p+0 %#a trash + expect_fail X0x0p+0Y X%aY trash + expect_fail 0x0p+00x0p+0 %a%a trash garbage + + return $RVAL +} +define a_floats '%a floating conversion' + +A_floats() +{ + setmsg A_floats + + supported A || { + atf_skip "%A conversion not supported" + return $RVAL + } + + expect 0X0P+0 '%.0A' 0 + expect 0X0.P+0 '%#.0A' 0 + expect 0X0.000P+0 '%.3A' 0 + expect '0X?.*P+*' '%A' 123 + expect '0X?.*P-*' '%A' 0.123 + + for VAL in 1.0000 2.0000 3.0000 4.0000 0.5000 0.1000 1000.0000 \ + 777777.0000 0.1234 -1.0000 -0.2500 -123.4567 + do + A_STRING=$( do_printf '%A' "${VAL}" 2>&3 ) + + expect "${VAL}" "%.4f" "${A_STRING}" + done + + expect_fail 0X0P+0 %A trash + expect_fail 0X0.P+0 %#A trash + expect_fail X0X0P+0X X%AX trash + expect_fail 0X0P+00X0P+0 %A%A trash garbage + + return $RVAL +} +define A_floats '%A floating conversion' + +missing_args() +{ + setmsg missing_args + + # Note: missing string arg is replaced by "" and behaviour + # of %c is either nothing or '\0' in that case, so avoid + # testing missing arg for %c. + + + expect '' %s + expect '' %b + expect 0 %d + expect 0 %o + expect 0 %x + expect 0 %#o + expect 0 %#X + + expect 'xxxyyyzzz' '%syyy%szzz' xxx + expect 'a=1, b=0' 'a=%d, b=%d' 1 + + expect 000000 %d%u%i%x%o%X + expect 437000 %d%u%i%x%o%X 4 3 7 + + if supported f + then + expect 0.000000 %f + expect 'x=0.0' '%s=%.1f' x + fi + + return $RVAL +} +define missing_args "format string when there are no more args" + +repeated_format() +{ + setmsg repeated_format + + expect abcd %s a b c d + expect 1234 %d 1 2 3 4 + expect ' 1 2 3 4' %2d 1 2 3 4 + expect abcd %.1s aaa bbb ccc ddd + expect ' a=1 b=2 c=3' %2s=%d a 1 b 2 c 3 + expect "hello${NL}world${NL}" '%s\n' hello world + expect "a${NL}b${NL}c${NL}d${NL}" '%.1s\n' aaa bbb ccc ddd + + expect "\ + 1.00"' + 9.75 + -3.00 + 999.99 +-101.01'"${NL}" '%7.2f\n' 1 9.75 -3 999.99 -101.01 + + expect " 1 010x1${NL} 220260x16${NL} 9201340x5c${NL}" \ + '%3d%#3o%#3x\n' 1 1 1 22 22 22 92 92 92 + + expect ' 1 2 3 4 5' %2d 1 2 3 4 5 + expect ' 1 2 3 4 5 0' %2d%2d%2d 1 2 3 4 5 + + + return $RVAL +} +define repeated_format 'format string is reused until all args used' + +b_SysV_echo() +{ + setmsg b_SysV_echo + + # Basic formatting + + expect '' %b '' + expect '' %.0b abcd + expect abcd %b abcd + expect ' ab' %3.2b abcd + expect 'a ' %-3.1b abcd + expect ' ' %3.0b abcd + + # The simple stuff. nb: no \c tests, it has a whole test case to itself + + expect "${BSL} ${NL}" %b '\\\t\n' + expect '' %b '\a\v\r\f\b' + expect 'ABC' %b '\01A\002\0102\0003C' + expect "a${NL}b${NL}" %b 'a\nb\n' + + # and unlikely to occur IRL + expect " ab" %7.4b 'ab\r\bxy\t\t\n' + expect "111 " %-6.3b '\00611\061\01\n\t\n' + + # and last, that pesky \0 + + atf_require_prog wc + atf_require_prog sed + + for fmt in '\0' '\00' '\000' '\0000' + do + if [ $( do_printf %b "${fmt}" | wc -c ) -ne 1 ] + then + atf_fail \ + "%b '${fmt}' did not output exactly 1 character (byte)" + elif [ $(( $( do_printf %b "${fmt}" | od -A n -to1 ) )) -ne 0 ] + then + atf_require_prog od + atf_require_prog tr + + RES="$(do_printf %b "${fmt}" | od -An -to1 | tr -d ' ')" + atf_fail \ + "%b '${fmt}' output was '\\${RES}' should be '\\000'" + fi + + for xt in "x${fmt}" "${fmt}q" "x${fmt}q" "${fmt}\\0" \ + "${fmt}|\\0|\\0|" "${fmt}${fmt}" "+${fmt}-${fmt}*" + do + # nb: we "know" here that the only \'s are \0's + # nb: not do_printf, we are not testing ... + bsl=$( printf %s "${xt}" | sed -e 's/\\00*/X/g' ) + xl=${#bsl} + + RES=$(( $( do_printf %b "${xt}" | wc -c ) )) + + if [ "${RES}" -ne "${xl}" ] + then + atf_fail \ + "%b '${xt}' output ${RES} chars, expected ${xl}" + fi + done + + test ${#fmt} -lt 5 && continue + + if [ $( do_printf %b "${fmt}1" | wc -c ) -ne 2 ] + then + atf_fail \ + "%b '${fmt}1' did not output exactly 2 characters" + fi + done + + return $RVAL +} +define b_SysV_echo '%b format - emulate SysV echo escapes' + +b_SysV_echo_backslash_c() +{ + setmsg b_SysV_echo_backslash_c + + # test \c in arg to printf %b .. causes instant death... + + expect ab %b 'ab\cdef' + expect ab a%bc 'b\cd' + + expect abcd %s%c%x%b a bcd 12 'd\c' + expect ad %.1s%x%b%c%x all 13 '\cars' cost 12 + expect "a${NL}b" '%b\n' a 'b\c' d '\ce' + + # This is undefined, though would be nice if we could rely upon it + # expect "abcd" %.1b 'a\c' 'b\c' 'c\c' 'd\c' '\c' e + + return $RVAL +} +define b_SysV_echo_backslash_c 'Use of \c in arg to %b format' + +indirect_width() +{ + setmsg indirect_width + + supported '*d' 5 123 || { + atf_skip "%*d not supported (indirect field width)" + return $RVAL + } + + lpad= rpad= zpad= + for i in 1 2 3 4 5 6 7 8 9 10 + do + expect "${lpad}7" '%*d' "$i" 7 + expect "6${rpad}" '%-*d' "$i" 6 + expect "${zpad}5" '%0*d' "$i" 5 + + lpad="${lpad} " + rpad="${rpad} " + zpad="${zpad}0" + done + + return $RVAL +} +define indirect_width "using * to get field width from arg" + +indirect_precision() +{ + setmsg indirect_precision + + supported '.*d' 5 123 || { + atf_skip "%.*d not supported (indirect precision)" + return $RVAL + } + + res= zpad=. + for i in 0 1 2 3 4 5 6 7 8 9 + do + expect "${res}" '%.*s' "$i" aaaaaaaaaaaaaaaa + res="${res}a" + + expect "3${zpad}" '%#.*f' "$i" 3 + zpad="${zpad}0" + done + + return $RVAL +} +define indirect_precision 'Using .* as to get precision from arg' + +indirect_both() +{ + setmsg indirect_both + + supported '*.*d' 5 2 123 || { + atf_skip "%*.*d not supported (indirect width & precision)" + return $RVAL + } + + res= + for i in 1 2 3 4 5 6 7 8 + do + res="${res}z" + expect " ${res}" '%*.*s' $(( $i + 2 )) "$i" zzzzzzzzzzz + done + + expect ' ab: 9: 1.20' "%*.*s:%*d:%*.*f" 4 2 abcde 3 9 5 2 1.2 + + return $RVAL +} +define indirect_both 'Using *.* as to get width & precision from args' + +q_quoting() +{ + setmsg q_quoting + + if ! supported q + then + atf_skip '%q format not supported' + return $RVAL + fi + + # Testing quoting isn't as straightforward as many of the + # others, as there is no specific form in which the output + # is required to appear + + # Instead, we will apply %q to various strings, and then + # process them again in this shell, and see if the string + # we get back is the same as the string we started with. + + for string in \ + abcd \ + 'hello world' \ + '# a comment ....' \ + '' \ + 'a* b* c*' \ + 'ls | wc' \ + '[<> # | { ~.** } $@]' \ + '( who & echo $! )' + do + QUOTED="$(do_printf %q "$string")" + + eval "RES=${QUOTED}" + + if [ "${RES}" != "${string}" ] + then + atf_fail \ + "%q <<${string}>> as <<${QUOTED}>> makes <<${RES}>>" + continue + fi + + QUOTED="$(do_printf %-32q "$string")" + + if [ ${#QUOTED} -lt 32 ] + then + atf-fail \ + "%-32q <<${string}>> short result (${#QUOTED}) <<${QUOTED}>>" + + fi + + eval "RES=${QUOTED}" + if [ "${RES}" != "${string}" ] + then + atf_fail \ + "%-32q <<${string}>> as <<${QUOTED}>> makes <<${RES}>>" + continue + fi + done + + # %q is a variant of %s, but using field width (except as above), + # and especially precision makes no sense, and is implrmented so + # badly that testing it would be hopeless. Other flags do nothing. + + return $RVAL +} +define q_quoting '%q quote string suitably for sh processing' + +NetBSD_extensions() +{ + setmsg NetBSD_extensions + + if $BUILTIN_TEST + then + # what matters if $TEST_SH is a NetBSD sh + ${TEST_SH} -c 'test -n "$NETBSD_SHELL"' || { + atf_skip \ + "- ${TEST_SH%% *} is not a (modern) NetBSD shell" + return $RVAL + } + fi + if ! supported '*.*%%_._' 78 66 + then + if $BUILTIN_TEST + then + atf_skip \ + "- ${TEST_SH%% *} is not a (modern enough) NetBSD shell" + else + atf_skip "- ${PRINTF} is not a (modern) NetBSD printf" + fi + return $RVAL + fi + + # Even in the most modern NetBSD printf the data length modifiers + # might not be supported. + + if supported zd + then + expect 88888 %jd 88888 + expect 88888 %ld 88888 + expect 88888 %lld 88888 + expect 88888 %Ld 88888 + expect 88888 %td 88888 + expect 88888 %zd 88888 + + expect 23352 %hd 88888 + expect 56 %hhd 88888 + + expect 300000 %jd 300000 + expect 300000 %Ld 300000 + expect -27680 %hd 300000 + expect -32 %hhd 300000 + + expect 15b38 %jx 88888 + expect 5b38 %hx 88888 + expect 38 %hhx 88888 + + expect 93e0 %hx 300000 + expect e0 %hhx 300000 + + # to test modifiers attached to floats we'd need to + # verify float support, so don't bother... + fi + + # NetBSD (non-POSIX) format excape extensions + expect '' '\e' + expect '' '\E' + expect '' '\e\E' + + # NetBSD (non-POSIX) %b string escape extensions + expect '' %b '\^A\^a\1' + expect 'S4=X' %b '\1234\75X' + expect 'xÙz' %b 'x\M-Yz' + expect 'x—z' %b 'x\M^wz' + expect 'ab' %b 'a\^?b' + expect '-ÿ-' %b '-\M^?-' + + expect 'A1b2c3D4' '\x411%b\x444' '\x622\x633' + expect '"'\' %b\\\' '\"\e' + expect '+' %b '\x1+\x3' + expect '[1m' %b '\E[\61\x6d' + + expect_fail "${BSL}" '\' + expect_fail '@' '\@' + expect_fail '%' '\%' + expect_fail "${BSL}" %b '\' + expect_fail '@' %b '\@' + + # This is unspecified in posix: + # If arg string uses no args, but there are some, run format just once + expect 'hello world' 'hello world' a b c d + + # Same as in format_escapes, but for \x (hex) constants + atf_require_prog wc + atf_require_prog od + atf_require_prog tr + + for fmt in '\x0' '\x00' + do + if [ $( do_printf "${fmt}" | wc -c ) -ne 1 ] + then + atf_fail \ + "printf '${fmt}' did not output exactly 1 character (byte)" + elif [ $(( $( do_printf "${fmt}" | od -A n -to1 ) )) -ne 0 ] + then + + RES="$( do_printf "${fmt}" | od -A n -to1 | tr -d ' ')" + atf_fail \ + "printf '${fmt}' output was '\\${RES}' should be '\\000'" + fi + done + + # We get different results here from the builtin and command + # versions of printf ... OK, as which result is unspecified. + if $BUILTIN_TEST + then + if [ $( do_printf %c '' | wc -c ) -ne 0 ] + then + atf_require_prog sed + + RES="$( do_printf %c '' | + od -A n -to1 | + sed -e 's/ [0-9]/\\&/g' -e 's/ //g' )" + atf_fail \ + "printf %c '' did not output nothing: got '${RES}'" + fi + else + if [ $( do_printf %c '' | wc -c ) -ne 1 ] + then + atf_require_prog sed + + RES="$( do_printf %c '' | + od -A n -to1 | + sed -e 's/ [0-9]/\\&/g' -e 's/ //g' )" + atf_fail \ + "printf %c '' did not output nothing: got '${RES}'" + elif [ $(( $( do_printf %c '' | od -A n -to1 ) )) -ne 0 ] + then + RES="$( do_printf %c '' | od -A n -to1 | tr -d ' ')" + atf_fail \ + "printf %c '' output was '\\${RES}' should be '\\000'" + fi + fi + + return $RVAL +} +define NetBSD_extensions "Local NetBSD additions to printf" + +B_string_expand() +{ + setmsg B_string_expand + + if ! supported B + then + atf_skip "%B format not supported" + return $RVAL + fi + + # Even if %B is supported, it is not necessarily *our* %B ... + + if $BUILTIN_TEST + then + # what matters if $TEST_SH is a NetBSD sh + ${TEST_SH} -c 'test -n "$NETBSD_SHELL"' || { + atf_skip \ + "- ${TEST_SH%% *} is not a (modern) NetBSD shell" + return $RVAL + } + else + atf_require_prog uname + + SYS="$(uname -s)" + case "${SYS}" in + (NetBSD) ;; + (*) atf_skip "- Not NetBSD (is $SYS), %B format unspecified" + return $RVAL + ;; + esac + fi + + # The trivial stuff... + expect abcd %B abcd + expect ' abcd' %5B abcd + expect 'abcd ' %-5B abcd + expect ab %.2B abcd + expect ' ab' %5.2B abcd + expect 'ab ' %-5.2B abcd + + # Next the semi-trivial + expect "abcd${BSL}n" %B "abcd${NL}" + expect "ab${BSL}tcd" %B "ab cd" + expect "${BSL}\"${BSL}e${BSL}a${BSL}b${BSL}f${BSL}r${BSL}v" \ + %B '"' + expect "${BSL}'${BSL}^?" %B \''' + expect "${BSL}^A${BSL}^B" %B '' + expect "x${BSL}M-Yz" %B 'xÙz' + expect "-${BSL}M^W-" %B '-—-' + expect ":${BSL}M^?:" %B ':ÿ:' + + # Then, more or less nonsense + expect " abcd${BSL}n" %9B "abcd${NL}" + expect "ab${BSL}tcd " %-9B "ab cd" + expect " ${BSL}'${BSL}^?" %6B \''' + expect "${BSL}^A${BSL}^B " %-7B '' + expect " -${BSL}M^W-" %8B '-—-' + expect ":${BSL}M^?: " %-8B ':ÿ:' + + # and finally, the absurd, ridiculous, and bizarre (useless) + expect "abcd${BSL}" %.5B "abcd${NL}" + expect "ab${BSL}" %.3B "ab cd" + expect "${BSL}\"${BSL}" %.3B '"' + expect "${BSL}" %.1B \''' + expect "${BSL}^" %.2B '' + expect "x${BSL}M-" %.4B 'xÙz' + expect "-${BSL}M^" %.4B '-—-' + expect ":${BSL}M" %.3B ':ÿ:' + + return $RVAL +} +define B_string_expand "NetBSD specific %B string expansion" + + +############################################################################# +############################################################################# +# +# The code to make the tests above actually run starts here... +# + +# if setup fails, then ignore any test names on command line +# Just run the (one) test that setup() established +setup || set -- + +NL=' +' +# test how the shell we're running handles quoted patterns in vars +# Note: it is not our task here to diagnose the broken shell +B1='\' +B2='\\' +case "${B1}" in +(${B2}) BSL="${B2}";; # This one is correct +(${B1}) BSL="${B1}";; # but some shells can't handle that +(*) BSL=BROKEN_SHELL;; # !!! +esac + +if $Running_under_ATF +then + # When in ATF, just add the test cases, and finish, and ATF + # will take care of running everything + + atf_init_test_cases() { + + for T in $Tests + do + atf_add_test_case "$T" + done + return 0 + } + exec 3>&2 +else + # When not in AFT, we need to do it all here... + + Failed= + Failures=0 + + STDERR=$(mktemp ${TMPDIR:-/tmp}/Test-XXXXXX) + trap 'rm -f "${STDERR}"' EXIT + exec 3>"${STDERR}" + + case "$#" in + (0) set -- $Tests ;; + esac + + for T + do + $T || { + Failed="${Failed}${Failed:+${NL}} ${T} : " + eval Failed='${Failed}${TEST_'"${T}"'_MSG}' + Failures=$(( $Failures + 1 )) + } + done + if [ $Failures -gt 0 ] + then + s=s + test $Failures -eq 1 && s= + + exec >&2 + echo + echo ================================================= + echo + echo "$Failures test$s failed:" + echo "$Failed" + echo + echo ================================================= + + if test -s "${STDERR}" + then + echo + echo The following appeared on stderr during the tests: + echo + cat "${STDERR}" + fi + fi +fi Index: src/tests/usr.bin/printf/t_builtin.sh diff -u /dev/null src/tests/usr.bin/printf/t_builtin.sh:1.1 --- /dev/null Wed Sep 5 21:05:40 2018 +++ src/tests/usr.bin/printf/t_builtin.sh Wed Sep 5 21:05:40 2018 @@ -0,0 +1,123 @@ +# $NetBSD: t_builtin.sh,v 1.1 2018/09/05 21:05:40 kre Exp $ +# +# Copyright (c) 2018 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 shell to use for tests of the builtin printf (or at least try) +: ${TEST_SH:=/bin/sh} + +# This tests the builtin printf command in ${TEST_SH} + +# For the actual code/tests see printf.sh +# (shared with tests for the external (filesystem) command printf + +# These tests are designed to be able to be run by ATF, or standalone +# +# That is, either +# atf_run t_builtin | atf-report (or whatever is needed) +# or +# sh t_builtin [sub_test]... (default is to run all sub_tests) +# +# nb: for standalone runs, do not attempt ./t_builtin as the #! line +# added will force ATF which will complain about the test being run +# in the wrong way. Instead use some Bourne shell compatible shell +# (any will do, caveat any bugs it might have, it need not be TEST_SH, +# but it can be) and run the script explicitly. + +do_printf() +{ + $Running_under_ATF && atf_require_prog "${TEST_SH%% *}" + + unset LANG LC_ALL LC_NUMERIC LC_CTYPE LC_MESSAGES + + case "$*" in + *\'*) + $Running_under_ATF && atf_require_prog printf + $Running_under_ATF && atf_require_pfog sed + ;; + esac + + COMMAND=printf + for ARG + do + case "${ARG}" in + (*\'*) + # This is kind of odd, we need a working + # printf in order to test printf ... + # nb: do not use echo here, an arg might be "-n'x" + COMMAND="${COMMAND} '$( + printf '%s\n' "${ARG}" | + sed "s/'/'\\\\''/g" + )'" + ;; + (*) + COMMAND="${COMMAND} '${ARG}'" + ;; + esac + done + ${TEST_SH} -c "${COMMAND}" +} + +Not_builtin() +{ + if $Running_under_ATF + then + atf_skip "${TEST_SH%% *} does not have printf built in" + else + echo >&2 "No builtin printf in ${TEST_SH}" + exit 1 + fi +} + +# See if we have a builtin "printf" command to test + +setup() +{ + case "$( ${TEST_SH} -c 'type printf' 2>&1 )" in + + ( *[Bb]uiltin* | *[Bb]uilt[-\ ][Ii]n* ) + # nothing here, it all happens below. + ;; + + (*) Tests= + define Not_builtin 'Dummy test to skip when no printf builtin' + return 1 + ;; + esac + + return 0 +} + +setmsg() +{ + MSG="${TEST_SH%% *} builtin printf" + CurrentTest="$1" + RVAL=0 +} + +BUILTIN_TEST=true + +# All the code to actually run the test comes from printf.sh ... + Index: src/tests/usr.bin/printf/t_command.sh diff -u /dev/null src/tests/usr.bin/printf/t_command.sh:1.1 --- /dev/null Wed Sep 5 21:05:40 2018 +++ src/tests/usr.bin/printf/t_command.sh Wed Sep 5 21:05:40 2018 @@ -0,0 +1,144 @@ +# $NetBSD: t_command.sh,v 1.1 2018/09/05 21:05:40 kre Exp $ +# +# Copyright (c) 2018 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 printf command to test (if not a full path, $PATH will be examined) +# Do not use relative (./ or ../) paths when running under ATF. +: ${TEST_PRINTF:=/usr/bin/printf} + +# This tests an external (filesystem) printf command + +# For the actual code/tests see printf.sh +# (shared with tests for the shell builtin printf + +# These tests are designed to be able to be run by ATF, or standalone +# +# That is, either +# atf_run t_command | atf-report (or whatever is needed) +# or +# sh t_command [sub_test]... ( default is to run all sub_tests ) +# +# nb: for standalone runs, do not attempt ./t_builtin as the #! line +# added will force ATF which will complain about the incorrect method +# of running the test. Instead use some Bourne shell compatible shell +# (any will do, caveat any bugs it might have), and run the script explicitly. + +do_printf() +{ + $Running_under_ATF && atf_require_prog "${PRINTF%% *}" + + unset LANG LC_ALL LC_NUMERIC LC_CTYPE LC_MESSAGES + + ${PRINTF} "$@" +} + +No_command() +{ + setmsg No_command + + case "${TEST_PRINTF%% *}" in + ( '' ) + msg='Configuration error: check $TEST_PRINTF' + ;; + ( /* | ./* | ../* ) + msg="Cannot find/execute ${TEST_PRINTF%% *}" + ;; + ( * ) + msg="No '${TEST_PRINTF%% *}' found in "'$PATH' + ;; + esac + atf_skip "${msg}" + + return $RVAL +} + +# See if we have a "printf" command in $PATH to test - pick the first + +setup() +{ + saveIFS="${IFS-UnSet}" + saveOPTS="$(set +o)" + + unset PRINTF + + case "${TEST_PRINTF%% *}" in + ( /* ) PRINTF="${TEST_PRINTF}" ;; + ( ./* | ../* ) PRINTF="${PWD}/${TEST_PRINTF}" ;; + (*) + set -f + IFS=: + for P in $PATH + do + case "$P" in + ('' | . ) D="${PWD}";; + ( /* ) D="${P}" ;; + ( * ) D="${PWD}/${P}";; + esac + + test -x "${D}/${TEST_PRINTF%% *}" || continue + + PRINTF="${D}/${TEST_PRINTF}" + break + done + unset IFS + eval "${saveOPTS}" + + case "${saveIFS}" in + (UnSet) unset IFS;; + (*) IFS="${saveIFS}";; + esac + ;; + esac + + test -x "${PRINTF%% *}" || PRINTF= + + case "${PRINTF}" in + + ('') Tests= + define No_command 'Dummy test to skip no printf command' + return 1 + ;; + + ( * ) + # nothing here, it all happens below. + ;; + + esac + + return 0 +} + +setmsg() +{ + MSG="${PRINTF}" + CurrentTest="$1" + RVAL=0 +} + +BUILTIN_TEST=false + +# All the code to actually run the test comes from printf.sh ... +