#!/bin/sh # shellcheck disable=1007,2015,2031,2164,2317,3043 # Requires mktemp(1), which is not a standard utility, but is commonly # available. The implementations provided by GNU coreutils, busybox and toybox # are known to be compatible. bailout() { printf 'Bail out! %s.\n' "$1" cleanup_tmpdir exit 1 } assign_tmpdir() { global_tmpdir=$(mktemp -d) \ && chdir "${global_tmpdir}" \ || bailout "Couldn't create or change to the temp dir" } cleanup_tmpdir() { if [ "${global_tmpdir}" ]; then rm -r -- "${global_tmpdir}" fi } test_local() { set -- eq 0 callback() { test_description="/bin/sh supports local" ( var=1 f() { local var=2 g test "${var}" = 3 || exit } g() { test "${var}" = 2 || exit var=3 } f test "${var}" = 1 ) 2>/dev/null } iterate_tests 2 "$@" } test_chdir() { set -- \ ge 1 '' \ ge 1 grandchild \ ge 1 var \ eq 0 -L \ eq 0 -p \ eq 0 -e \ eq 0 -@ \ eq 0 - \ eq 0 child if ! mkdir -p -- -L -p -e -@ - child child/grandchild; then bailout "Couldn't set up all test directories" fi callback() { local CDPATH var shift test_description="chdir $(quote_args "$@")" if [ "$BASH" ]; then # shellcheck disable=3044 shopt -s cdable_vars fi CDPATH=child var=child chdir "$@" \ && test "$PWD" != "$OLDPWD" \ && cd - >/dev/null } iterate_tests 3 "$@" } test_die() { set -- \ eq 1 0 \ eq 2 2 \ eq 126 126 \ eq 255 255 callback() { local retval stderr test_description="( exit $2 ); die" ( exit "$2" ) stderr=$(die "$2" 2>&1) retval=$? if [ "${stderr}" = "test-functions: $2" ]; then return "${retval}" else return 1 fi } iterate_tests 3 "$@" } test_ebegin() { set -- eq 0 callback() { test_description="ebegin message (expecting terminating newline)" ( _eprint() { shift _ends_with_newline "$*" } ebegin "message" ) } iterate_tests 2 "$@" } test_edo() { set -- \ eq 1 false \ eq 0 true callback() { shift test_description="edo $1" ( edo "$1" >/dev/null ) } iterate_tests 3 "$@" } test_is_older_than() { local age tstamp set -- \ ge 1 N/A N/A \ ge 1 newer N/A \ ge 1 newer-empty N/A \ ge 1 newer/file N/A \ ge 1 non-existent N/A \ eq 0 newer newer \ ge 1 newer newer-empty \ eq 0 newer newer/file \ ge 1 newer non-existent \ ge 1 newer older \ ge 1 newer older-empty \ ge 1 newer older/file \ eq 0 newer-empty newer \ ge 1 newer-empty newer-empty \ eq 0 newer-empty newer/file \ ge 1 newer-empty non-existent \ ge 1 newer-empty older \ ge 1 newer-empty older-empty \ ge 1 newer-empty older/file \ ge 1 newer/file newer \ ge 1 newer/file newer-empty \ ge 1 newer/file newer/file \ ge 1 newer/file non-existent \ ge 1 newer/file older \ ge 1 newer/file older-empty \ ge 1 newer/file older/file \ eq 0 non-existent newer \ eq 0 non-existent newer-empty \ eq 0 non-existent newer/file \ ge 1 non-existent non-existent \ eq 0 non-existent older \ eq 0 non-existent older-empty \ eq 0 non-existent older/file \ eq 0 older newer \ eq 0 older newer-empty \ eq 0 older newer/file \ ge 1 older non-existent \ eq 0 older older \ ge 1 older older-empty \ eq 0 older older/file \ eq 0 older-empty newer \ eq 0 older-empty newer-empty \ eq 0 older-empty newer/file \ ge 1 older-empty non-existent \ eq 0 older-empty older \ ge 1 older-empty older-empty \ eq 0 older-empty older/file \ eq 0 older/file newer \ eq 0 older/file newer-empty \ eq 0 older/file newer/file \ ge 1 older/file non-existent \ ge 1 older/file older \ ge 1 older/file older-empty \ ge 1 older/file older/file # The mtimes need to be explicitly assigned. Empirical evidence has # shown that executing mkdir(1) sequentially, with a single operand # each time, does not guarantee the order of the resulting mtimes. tstamp=197001010000 for age in older newer; do mkdir "${age}" "${age}-empty" \ && touch -m -t "${tstamp%0}1" "${age}"/file \ && touch -m -t "${tstamp}" "${age}" "${age}-empty" \ || bailout "Couldn't create or adjust the mtimes of the sample files" tstamp=197001010100 # add an hour done callback() { shift test_description="is_older_than $(quote_args "$@")" is_older_than "$@" } iterate_tests 4 "$@" } test_get_bootparam() { local cmdline cmdline="foo gentoo=bar,baz quux" set -- \ ge 1 "${cmdline}" N/A \ ge 1 "${cmdline}" '' \ ge 1 "gentoo=" '' \ ge 1 "${cmdline}" foo \ eq 0 "${cmdline}" bar \ eq 0 "foo gentoo=gentoo=1,bar baz" bar \ eq 0 "foo gentoo=bar,gentoo=1 baz" bar \ eq 0 "${cmdline}" baz \ ge 1 "${cmdline}" bar,baz \ eq 0 "foo gentoo=bar,gentoo=1 baz" gentoo=1 \ eq 0 "foo gentoo=gentoo=1,bar baz" gentoo=1 \ ge 1 "${cmdline}" quux callback() { local cmdline cmdline=$2 shift 2 test_description="get_bootparam $(quote_args "$@")" printf '%s\n' "${cmdline}" | get_bootparam "$@" } iterate_tests 4 "$@" } test_esyslog() { set -- \ ge 1 0 N/A N/A N/A \ ge 1 0 debug N/A N/A \ eq 0 0 debug user N/A \ eq 0 0 debug user '' \ eq 0 1 debug user message logger() { # esyslog() ignores empty messages. By overriding logger(1), it # can be determined whether a message would have been logged. printf '1\n' } callback() { local logged should_log should_log=$2 shift 2 test_description="esyslog $(quote_args "$@")" # shellcheck disable=2034 logged=$(EINFO_LOG=1; esyslog "$@") case $? in 0) test "${logged:-0}" -eq "${should_log}" ;; *) return "$?" esac } iterate_tests 6 "$@" } test_is_identifier() { set -- \ ge 1 '' \ ge 1 _ \ ge 1 0 \ ge 1 0a \ ge 1 0Z \ ge 1 9 \ ge 1 9a \ ge 1 9Z \ ge 1 /a \ ge 1 /Z \ ge 1 .a \ ge 1 .Z \ ge 1 '[a' \ ge 1 '[Z' \ ge 1 '`a' \ ge 1 '`Z' \ ge 1 '{a' \ ge 1 '{Z' \ ge 1 '|a' \ ge 1 '|Z' \ ge 1 a/ \ ge 1 Z/ \ ge 1 a. \ ge 1 Z. \ ge 1 'a[' \ ge 1 'Z[' \ ge 1 'a`' \ ge 1 'Z`' \ ge 1 'a{' \ ge 1 'Z{' \ ge 1 'a|' \ ge 1 'Z|' \ eq 0 a \ eq 0 Z \ eq 0 __ \ eq 0 _a \ eq 0 _Z \ eq 0 a_ \ eq 0 Z_ \ eq 0 a_a \ eq 0 a_Z \ eq 0 Z_a \ eq 0 Z_Z \ eq 0 a0 \ eq 0 a9 \ eq 0 Z0 \ eq 0 Z9 \ eq 0 a_0 \ eq 0 a_9 \ eq 0 Z_0 \ eq 0 Z_9 callback() { shift test_description="is_identifier $(quote_args "$@")" is_identifier "$@" } iterate_tests 3 "$@" } test_is_int() { set -- \ ge 1 N/A \ ge 1 ' ' \ ge 1 ' 1 ' \ ge 1 '' \ ge 1 +1 \ ge 1 +008 \ ge 1 -008 \ ge 1 008 \ ge 1 x \ eq 0 0 \ eq 0 1 \ eq 0 -1 \ eq 0 123456789 callback() { shift test_description="is_int $(quote_args "$@")" is_int "$@" } iterate_tests 3 "$@" } test_is_visible() { set -- \ ge 1 '' \ ge 1 ' ' \ ge 1 "$(printf '\t')" \ ge 1 "$(printf '\a')" \ eq 0 . \ eq 0 ' . ' \ eq 0 "$(printf '\t.\t')" \ eq 0 "$(printf '\a.\a')" callback() { shift test_description="_is_visible $(quote_args "$@")" _is_visible "$@" } iterate_tests 3 "$@" } test_yesno() { set -- \ eq 0 yes \ eq 0 YES \ eq 0 Yes \ eq 0 true \ eq 0 TRUE \ eq 0 true \ eq 0 on \ eq 0 ON \ eq 0 On \ eq 0 1 \ eq 0 truthful_nameref \ ge 1 no \ ge 1 NO \ ge 1 No \ ge 1 false \ ge 1 FALSE \ ge 1 False \ ge 1 off \ ge 1 OFF \ ge 1 Off \ ge 1 0 \ ge 1 not_a_nameref \ ge 1 not-a-valid-nameref \ ge 1 '_"; set -- yes # code injection' # shellcheck disable=2034 truthful_nameref=yes callback() { shift test_description="yesno $(quote_args "$@")" yesno "$@" } iterate_tests 3 "$@" } test_srandom() { set -- \ eq 0 \ eq 0 \ eq 0 \ eq 0 \ eq 0 \ eq 0 \ eq 0 \ eq 0 \ eq 0 \ eq 0 callback() { local number number=$(srandom) test_description="srandom ($(( row += 1 ))/10: ${number:-blank})" is_int "${number}" \ && awk -v "n=${number}" 'BEGIN { exit !(n >= 0 && n <= 2147483647) }' } row=0 iterate_tests 2 "$@" } test_srandom_forked() { set -- \ eq 0 unforked \ eq 0 forking callback() { local mode number shift mode=$1 set -- test_description="srandom equality where $mode" if [ "${mode}" = "forking" ]; then srandom ( srandom ) srandom ( srandom ) else srandom srandom srandom srandom fi > random_numbers || return while read -r number; do set -- "$@" "${number}" done < random_numbers test_description="srandom equality where $mode ($*)" if [ "${mode}" = "forking" ]; then test "$#" -eq 4 && test "$1" -ne "$2" && test "$3" -ne "$4" else test "$#" -eq 4 && ! trueof_all test "$1" -eq -- "$@" fi } iterate_tests 3 "$@" } test_newest() { set -- \ ge 1 non-existent non-existent \ ge 1 N/A N/A \ \ eq 0 newer/file N/A \ eq 0 newer/file newer/file \ eq 0 newer/file non-existent \ eq 0 newer/file older/file \ eq 0 newer/file older/file \ eq 0 non-existent newer/file \ eq 0 older/file newer/file \ eq 0 older/file newer/file \ \ eq 0 older/file N/A \ eq 0 older/file older/file \ eq 0 older/file non-existent \ eq 0 non-existent older/file \ callback() { shift test_description="newest $(quote_args "$@")" row=$(( row + 1 )) true | case ${row} in [1-2]) newest "$@" ;; [3-9]|10) test "$(newest "$@")" = "newer/file" ;; *) test "$(newest "$@")" = "older/file" esac } row=0 iterate_tests 4 "$@" callback() { shift if [ "$#" -eq 0 ]; then test_description=": | newest" else test_description="printf '%s\\0' $(quote_args "$@") | newest" fi row=$(( row + 1 )) { test "$#" -gt 0 && printf '%s\0' "$@" } | case ${row} in [1-2]) newest ;; [3-9]|10) test "$(newest)" = "newer/file" ;; *) test "$(newest)" = "older/file" esac } row=0 iterate_tests 4 "$@" } test_oldest() { set -- \ ge 1 non-existent non-existent \ ge 1 N/A N/A \ \ eq 0 newer/file N/A \ eq 0 newer/file newer/file \ eq 0 newer/file non-existent \ eq 0 non-existent newer/file \ \ eq 0 newer/file older/file \ eq 0 non-existent older/file \ eq 0 older/file N/A \ eq 0 older/file newer/file \ eq 0 older/file non-existent \ eq 0 older/file older/file \ eq 0 newer/file older/file \ eq 0 older/file newer/file callback() { shift test_description="oldest $(quote_args "$@")" row=$((row + 1)) true | case ${row} in [1-2]) oldest "$@" ;; [3-6]) test "$(oldest "$@")" = "newer/file" ;; *) test "$(oldest "$@")" = "older/file" esac } row=0 iterate_tests 4 "$@" callback() { shift if [ "$#" -eq 0 ]; then test_description=": | oldest" else test_description="printf '%s\\0' $(quote_args "$@") | oldest" fi row=$(( row + 1 )) { test "$#" -gt 0 && printf '%s\0' "$@" } | case ${row} in [1-2]) oldest ;; [3-6]) test "$(oldest)" = "newer/file" ;; *) test "$(oldest)" = "older/file" esac } row=0 iterate_tests 4 "$@" } test_trim() { set -- \ eq 0 '' '' \ eq 0 ' ' '' \ eq 0 ' ' '' \ eq 0 ' X' 'X' \ eq 0 ' X' 'X' \ eq 0 'X ' 'X' \ eq 0 ' X Y' 'X Y' \ eq 0 ' X Y' 'X Y' \ eq 0 'X Y ' 'X Y' \ eq 0 'X Y ' 'X Y' \ eq 0 "$(printf ' \tX')" 'X' \ eq 0 "$(printf ' \tX\t ')" 'X' \ eq 0 "$(printf 'X\t ')" 'X' \ eq 0 "$(printf ' \tX Y')" 'X Y' \ eq 0 "$(printf ' \tX Y\t ')" 'X Y' \ eq 0 "$(printf 'X Y\t ')" 'X Y' callback() { shift test_description="trim $(quote_args "$1") (expecting $(quote_args "$2"))" test "$(trim "$1")" = "$2" } iterate_tests 4 "$@" } test_hr() { # shellcheck disable=2183 set -- \ eq 0 "$(printf '%80s' | tr ' ' -)" N/A N/A \ eq 0 "$(printf '%80s' | tr ' ' -)" - N/A \ eq 0 '' - 0 \ eq 0 - - 1 \ eq 0 ----------------- - 17 \ eq 0 '' xyz 0 \ eq 0 x xyz 1 \ eq 0 xxxxxxxxxxxxxxxxx xyz 17 callback() { local expected shift expected=$1 shift test_description="hr $(quote_args "$@")" test "$(hr "$@")" = "${expected}" } iterate_tests 5 "$@" } test_whenceforth() { set -- \ ge 1 PATH N/A N/A \ ge 1 PATH . N/A \ ge 1 PATH unlikely-to-exist N/A \ ge 1 PATH /var/empty N/A \ ge 1 PATH /var/empty/nofile N/A \ eq 0 PATH /bin/sh N/A \ eq 0 PATH sh N/A \ ge 1 PATH -x . \ ge 1 PATH -x unlikely-to-exist \ ge 1 PATH -x /var/empty \ ge 1 PATH -x /var/empty/nofile \ eq 0 PATH -x /bin/sh \ eq 0 PATH -x sh \ eq 0 '' -x newer/file \ eq 0 . -x newer/file \ eq 0 :/var/empty/x -x newer/file \ eq 0 /var/empty/x: -x newer/file \ eq 0 /var/empty/x::/var/empty/y -x newer/file \ eq 0 '' -x newer/file \ eq 0 . -x newer/file \ eq 0 :/var/empty/x -x newer/file \ eq 0 /var/empty/x: -x newer/file \ eq 0 /var/empty/x::/var/empty/y -x newer/file \ eq 0 '' older/file N/A \ eq 0 . older/file N/A \ eq 0 :/var/empty/x older/file N/A \ eq 0 /var/empty/x: older/file N/A \ eq 0 /var/empty/x::/var/empty/y older/file N/A \ ge 1 '' -x older/file \ ge 1 . -x older/file \ ge 1 :/var/empty/x -x older/file \ ge 1 /var/empty/x: -x older/file \ ge 1 /var/empty/x::/var/empty/y -x older/file chmod +x newer/file callback() { local path shift path=$1 shift if [ "${path}" = PATH ]; then test_description="whenceforth $(quote_args "$@")" whenceforth "$@" >/dev/null else test_description="PATH=${path} whenceforth $(quote_args "$@")" ( # If necessary, declare functions to cover the # utilities that might otherwise be unavailable # on account of the various values of PATH # being tested. It cannot be assumed that the # utilities in question are builtins. case ${printf_cmd} in /*) printf() { "${printf_cmd}" "$@"; } esac case ${test_cmd} in /*) test() { "${test_cmd}" "$@"; } esac # shellcheck disable=2030 PATH=${path} whenceforth "$@" >/dev/null ) fi } printf_cmd=$(command -v printf) test_cmd=$(command -v test) iterate_tests 5 "$@" } test_get_nprocs() { set -- eq 0 callback() { local nproc shift test_description="get_nprocs" nproc=$(get_nprocs) && is_int "${nproc}" && test "${nproc}" -gt 0 } iterate_tests 2 "$@" } test_parallel_run() { set -- \ ge 1 N/A N/A \ eq 0 / N/A \ ge 1 /var/empty/nonexistent N/A \ eq 0 / / \ ge 1 / /var/empty/nonexistent \ ge 1 /var/empty/nonexistent /var/empty/nonexistent \ ge 1 /var/empty/nonexistent / callback() { shift test_description="parallel_run $(quote_args 0 ls "$@")" parallel_run 0 ls "$@" >/dev/null 2>&1 } iterate_tests 4 "$@" } test_is_anyof() { set -- \ ge 1 N/A N/A N/A \ ge 1 x N/A N/A \ ge 1 x y N/A \ ge 1 x y z \ eq 0 x x N/A \ eq 0 x x y \ eq 0 x y x callback() { shift test_description="is_anyof $(quote_args "$@")" is_anyof "$@" } iterate_tests 5 "$@" } test_is_subset() { set -- \ ge 1 N/A N/A N/A N/A N/A \ ge 1 -- N/A N/A N/A N/A \ ge 1 -- -- N/A N/A N/A \ ge 1 -- x N/A N/A N/A \ ge 1 x -- N/A N/A N/A \ ge 1 x y N/A N/A N/A \ ge 1 x y x N/A N/A \ eq 0 x -- x N/A N/A \ eq 0 x -- x y N/A \ eq 0 x -- y x N/A \ eq 0 x y -- x y \ eq 0 x y -- y x \ ge 1 x y -- x z \ ge 1 y x -- z x \ ge 1 x z -- x y \ ge 1 z x -- y x callback() { shift test_description="is_subset $(quote_args "$@")" is_subset "$@" } iterate_tests 7 "$@" } test_trueof_all() { set -- \ ge 1 N/A N/A N/A N/A N/A \ ge 1 test -d N/A N/A N/A \ ge 1 test -d -- N/A N/A \ ge 1 test -d -- /dev/null N/A \ ge 1 test -d -- /dev/null /dev/null \ eq 0 test -d -- / N/A \ eq 0 test -d -- / / \ ge 1 test -d -- / /dev/null \ ge 1 test -d -- /dev/null / callback() { shift test_description="trueof_all $(quote_args "$@")" trueof_all "$@" } iterate_tests 7 "$@" } test_trueof_any() { set -- \ ge 1 N/A N/A N/A N/A N/A \ ge 1 test -d N/A N/A N/A \ ge 1 test -d -- N/A N/A \ ge 1 test -d -- /dev/null N/A \ ge 1 test -d -- /dev/null /dev/null \ eq 0 test -d -- / N/A \ eq 0 test -d -- / / \ eq 0 test -d -- / /dev/null \ eq 0 test -d -- /dev/null / callback() { shift test_description="trueof_any $(quote_args "$@")" trueof_any "$@" } iterate_tests 7 "$@" } test_substr() { set -- \ ge 1 - foobar N/A N/A \ ge 1 - foobar '' N/A \ ge 1 - foobar x N/A \ ge 1 - foobar '' '' \ ge 1 - foobar x y \ eq 0 foobar foobar 1 N/A \ eq 0 foobar foobar -1 N/A \ eq 0 foobar foobar 1 7 \ eq 0 foobar foobar -1 7 \ eq 0 foo foobar 1 3 \ eq 0 foo foobar -1 3 \ eq 0 f foobar 1 1 \ eq 0 f foobar -1 1 \ eq 0 '' foobar 1 0 \ eq 0 '' foobar 1 -1 \ eq 0 '' foobar 0 0 \ eq 0 '' foobar 0 -1 \ eq 0 '' foobar -1 0 \ eq 0 '' foobar -1 -1 \ eq 0 bar foobar 4 N/A \ eq 0 bar foobar 4 4 \ eq 0 b foobar 4 1 \ eq 0 '' foobar 4 0 \ eq 0 '' foobar 4 -1 callback() { local expected str shift expected=$1 shift test_description="substr $(quote_args "$@")" str=$(substr "$@") && test "${str}" = "${expected}" } iterate_tests 6 "$@" } test_contains_all() { set -- \ ge 1 N/A N/A N/A N/A \ ge 1 ' foo bar ' '' N/A N/A \ ge 1 ' foo bar ' '' ' ' N/A \ ge 1 ' foo bar ' '' ' bar' N/A \ ge 1 ' foo bar ' '' 'foo ' N/A \ ge 1 ' foo bar ' '' 'foo bar' N/A \ ge 1 ' foo bar ' ' ' '' N/A \ ge 1 ' foo bar ' ' ' ' ' N/A \ ge 1 ' foo bar ' ' ' N/A N/A \ ge 1 ' foo bar ' ' bar' '' N/A \ ge 1 ' foo bar ' ' bar' N/A N/A \ ge 1 ' foo bar ' 'foo ' '' N/A \ ge 1 ' foo bar ' 'foo ' ' bar' N/A \ ge 1 ' foo bar ' 'foo ' N/A N/A \ ge 1 ' foo bar ' 'foo bar' '' N/A \ ge 1 ' foo bar ' 'foo bar' N/A N/A \ ge 1 ' foo bar ' N/A N/A N/A \ ge 1 ' foo bar ' bar foo '' \ ge 1 ' foo bar ' bar foo ' ' \ ge 1 ' foo bar ' baz bar foo \ ge 1 ' foo bar ' fo ba N/A \ ge 1 ' foo bar ' foo bar '' \ ge 1 ' foo bar ' foo bar ' ' \ ge 1 ' foo bar ' foo bar baz \ ge 1 ' foo bar ' o a N/A \ ge 1 ' foo bar ' oo ar N/A \ eq 0 ' foo bar ' foo bar N/A \ eq 0 ' foo bar ' bar foo N/A callback() { shift test_description="contains_all $(quote_args "$@")" contains_all "$@" } iterate_tests 6 "$@" } test_contains_any() { set -- \ ge 1 N/A N/A N/A \ ge 1 'foo bar' N/A N/A \ ge 1 'foo bar' fo ba \ ge 1 'foo bar' oo ar \ ge 1 'foo bar' o a \ ge 1 'foo bar' 'foo bar' 'foo bar' \ ge 1 'foo bar' 'foo bar' _ \ ge 1 'foo bar' _ 'foo bar' \ ge 1 'foo bar' 'foo ' ' bar' \ ge 1 'foo bar' 'foo ' _ \ ge 1 'foo bar' _ ' bar' \ ge 1 'foo bar' ' bar' _ \ ge 1 'foo bar' _ 'foo ' \ ge 1 'foo bar' '' '' \ ge 1 'foo bar' '' _ \ ge 1 'foo bar' _ '' \ ge 1 'foo bar' ' ' ' ' \ ge 1 'foo bar' ' ' _ \ ge 1 'foo bar' _ ' ' \ eq 0 'foo bar' foo bar \ eq 0 'foo bar' bar foo \ eq 0 'foo bar' foo _ \ eq 0 'foo bar' _ bar \ eq 0 'foo bar' bar _ \ eq 0 'foo bar' _ foo callback() { shift test_description="contains_any $(quote_args "$@")" contains_any "$@" } iterate_tests 5 "$@" } test_quote_args() { set -- eq 0 callback() { local POSIXLY_CORRECT cksum fmt i str test_description="quote_args output test (expecting cksum 380900690)" i=0 # The generator fails to produce the correct ouput in yash # unless the effective character type is C/POSIX. However, once # launched, yash ignores assignments to the LC_CTYPE variable # if in its posix mode. As things stand, there is little point # in fixing it because yash also disables the local builtin in # its posix mode, causing test-functions to bail out sooner. while [ "$((i += 1))" -le 255 ]; do fmt=$(printf '\\%o' "$i") # shellcheck disable=2059 str=$(printf "$fmt.") quote_args "${str%.}" || break done \ | cksum \ | { read -r cksum _ && test "${cksum}" = "380900690"; } } iterate_tests 2 "$@" } test_assign() { set -- \ ge 1 N/A N/A \ ge 1 '' N/A \ ge 1 0 N/A \ ge 1 valid_nameref N/A \ ge 1 '' marmoset \ ge 1 0 marmoset \ ge 1 valid_nameref N/A \ ge 1 'injection=1 #' comment \ eq 0 valid_nameref marmoset callback() { local injection shift test_description="assign $(quote_args "$@")" injection= assign "$@" 2>/dev/null || test "${injection}" } iterate_tests 4 "$@" } test_deref() { set -- \ ge 1 N/A N/A \ ge 1 '' N/A \ ge 1 0 N/A \ ge 1 '' '' \ ge 1 0 0 \ eq 0 valid_nameref N/A \ eq 0 valid_nameref assignee \ ge 1 PWD 'injection=1 #' callback() { local assignee injection stdout shift test_description="deref $(quote_args "$@")" case $# in 2) assignee= injection= deref "$@" \ && { test "${assignee}" = "marmoset" || test "${injection}"; } ;; *) stdout=$(deref "$@") && test "${stdout}" = "marmoset" ;; esac 2>/dev/null } iterate_tests 4 "$@" } test_update_time() { local locale # The yash shell dies upon integer overflow and _update_time() ends up # being deactivated for it. Hence, there is no reason to run this test. if [ "${YASH_VERSION}" ]; then return fi set -- \ de_BE de_DE es_ES fr_BE fr_CA fr_FR it_IT nl_BE nl_NL pl_PL \ pt_BR pt_PT ru_RU sv_SE # Try to test a locale for which the radix character isn't U+2E. locale=$( IFS='|' locale -a 2>/dev/null | awk "/^($*)\.(utf8|UTF-8)$/ { print; exit }" ) if [ "${locale}" ]; then set -- eq 0 "$locale" else set -- fi # Also test the currently effective locale, whichever it may be. set -- "$@" eq 0 '' callback() { local genfun_time shift if [ "$1" ]; then test_description="LC_ALL=$1 _update_time" genfun_time=$(LC_ALL=$1; _update_time && printf %s "${genfun_time}") else test_description="_update_time" genfun_time=$(_update_time && printf %s "${genfun_time}") fi case $? in 0) is_int "${genfun_time}" ;; 2) # Unsupported for the platform and therefore untestable. true ;; *) false esac } iterate_tests 3 "$@" } test_should_throttle() { local bits max_int # The yash shell dies upon integer overflow and _update_time() ends up # being deactivated for it. Hence, there is no reason to run this test. if [ "${YASH_VERSION}" ]; then return fi genfun_time= bits=30 while [ "${bits}" -lt 128 ]; do # Dash is buggy and fails to handle $(( 1 << ++bits )). bits=$(( bits + 1 )) case $(( max_int = 1 << bits )) in -*) max_int=$(( max_int - 1 )) genfun_time=$(( max_int - 4 )) break esac done if [ ! "${genfun_time}" ]; then bailout "Failed to calculate the maximum possible integer value" fi # For the first test, genfun_last_time is not yet known. Therefore, the # return value should always be 1. For the fifth test, integer overflow # is expected to occur. Again, the return value should always be 1. set -- \ ge 1 "${max_int}" \ eq 0 2 \ ge 1 1 \ ge 1 0 \ ge 1 2 \ eq 0 2 \ ge 1 1 \ ge 1 0 _update_time() { true } callback() { shift test_description="_should_throttle $1 (${genfun_time}, $(( genfun_time += 1 )))" _should_throttle "$1" } iterate_tests 3 "$@" } iterate_tests() { local code i j passed slice_width total slice_width=$1 shift total=$(( $# / slice_width )) passed=0 i=0 while [ "$((i += 1))" -le "${total}" ]; do code="callback" j=1 while [ "$((j += 1))" -le "${slice_width}" ]; do if eval "[ \"\$${j}\" = N/A ]"; then break else code="${code} \"\$${j}\"" fi done eval "${code}" retval=$? if test "${retval}" -"$1" "$2"; then passed=$((passed + 1)) else printf 'not ' fi printf 'ok %d - %s (test %d -%s %d)\n' \ "$((testnum += 1))" "${test_description}" "${retval}" "$1" "$2" shift "${slice_width}" done return "$(( passed < total ))" } printf 'TAP version 13\n' unset -v global_tmpdir # PATH is redefined to prevent ebuild-helpers such as die from interfering. export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/opt/pkg/bin export TEST_GENFUNCS=1 export TZ=UTC testnum=0 rc=0 if [ "${PORTAGE_BIN_PATH}" ] && [ "${S}" ]; then # shellcheck disable=2034 genfun_basedir=${S} fi if ! test_local; then # Currently, this test is known to fail for ksh93 and yash. As regards # the former, the commonly implemented behaviour of "local" can be # approximated with "typeset". However, to use typeset in this way # requires the use of the function f { ...; } syntax instead of the # POSIX-compatible f() compound-command syntax. Further, ksh93 # implements static scoping. As regards the latter, yash is rather # stringent and simply disables its local builtin if in its posix mode. # Running yash as "sh" would be one way of activating said mode. rc=1 elif ! GENFUN_MODULES="portage rc" . ./functions.sh; then bailout "Couldn't source ./functions.sh" else assign_tmpdir test_chdir || rc=1 test_ebegin || rc=1 test_is_older_than || rc=1 test_get_bootparam || rc=1 test_esyslog || rc=1 test_is_identifier || rc=1 test_is_int || rc=1 test_is_visible || rc=1 test_yesno || rc=1 test_die || rc=1 test_edo || rc=1 if ! test_srandom; then rc=1 else test_srandom_forked || rc=1 fi test_newest || rc=1 test_oldest || rc=1 test_trim || rc=1 test_hr || rc=1 test_whenceforth || rc=1 test_parallel_run || rc=1 test_is_anyof || rc=1 #test_is_subset || rc=1 test_trueof_all || rc=1 test_trueof_any || rc=1 #test_substr || rc=1 test_contains_all || rc=1 test_contains_any || rc=1 test_quote_args || rc=1 test_assign || rc=1 test_deref || rc=1 test_update_time || rc=1 test_should_throttle || rc=1 fi cleanup_tmpdir printf '1..%d\n' "${testnum}" exit "${rc}"