#!/bin/bash unset LANGUAGE export LANG=C export LC_ALL=C GREEN="" YELLOW="" RED="" RESET="" if [ -z "$NO_COLOR" ] ; then if [ -n "$CLICOLOR_FORCE" ] || [[ -t 1 ]] ; then # See https://bixense.com/clicolors/ . We only check isatty() on # file descriptor 1, to decide whether colorizing happens (although, # we might also colorize on other places/FDs). GREEN=$'\e[32m' YELLOW=$'\e[33m' RED=$'\e[31m' RESET=$'\e[0m' fi fi array_contains() { local needle="$1" local a shift for a; do [ "$a" = "$needle" ] && return 0 done return 1 } array_remove_first() { local _varname="$1" local _needle="$2" local _result=() local _a eval "local _input=( \"\${$_varname[@]}\" )" for _a in "${_input[@]}" ; do if [ -n "${_needle+x}" -a "$_needle" = "$_a" ] ; then unset _needle else _result+=("$_a") fi done eval "$_varname="'( "${_result[@]}" )' } colorize_keywords() { local out_variable="$1" local color="$2" local val="$3" local val2 shift 3 printf -v val2 '%q' "$val" array_contains "$val" "$@" && val2="$color$val2$RESET" printf -v "$out_variable" '%s' "$val2" } strtonum() { local s="$1" local n local n2 re='^[[:space:]]*([0-9]+)[[:space:]]*$' if [[ "$s" =~ $re ]] ; then n="${BASH_REMATCH[1]}" if [ "$(( n + 0 ))" = "$n" ] ; then echo "$n" return 0 fi fi re='^[[:space:]]*0x([0-9a-fA-F]+)[[:space:]]*$' if [[ "$s" =~ $re ]] ; then n="${BASH_REMATCH[1]}" n2="$(( 16#$n + 0 ))" if [ "$n2" = "$(printf '%d' "0x$n" 2>/dev/null)" ] ; then echo "$n2" return 0 fi fi return 1 } _msg() { local level="$1" shift if [ "$level" = E ] ; then printf '%s\n' "$RED$level$RESET: $*" elif [ "$level" = W ] ; then printf '%s\n' "$YELLOW$level$RESET: $*" else printf '%s\n' "$level: $*" fi if [ "$level" = E ] ; then exit 1 fi } msg_error() { _msg E "$@" } msg_warn() { _msg W "$@" } msg_info() { _msg I "$@" } align_text() { local _OUT_VARNAME="$1" local _LEFT_OR_RIGHT="$2" local _INDENT="$3" shift 3 local _text="$*" local _text_plain local _text_align local _text_result local _i # This function is needed, because "$text" might contain color escape # sequences. A plain `printf '%12s' "$text"` will not align properly. # strip escape sequences _text_plain="${_text//$'\e['[0-9]m/}" _text_plain="${_text_plain//$'\e['[0-9][0-9]m/}" _text_align="" for (( _i = "${#_text_plain}" ; "$_i" < "$_INDENT" ; _i++ )) ; do _text_align="$_text_align " done if [ "$_LEFT_OR_RIGHT" = left ] ; then _text_result="$(printf "%s$_text_align-" "$_text")" else _text_result="$(printf "$_text_align%s-" "$_text")" fi _text_result="${_text_result%-}" eval "$_OUT_VARNAME=\"\$_text_result\"" } bool_n() { case "$1" in n|N|no|No|NO|0|false|False|FALSE) printf n ;; *) printf y ;; esac } bool_y() { case "$1" in y|Y|yes|Yes|YES|1|true|True|TRUE) printf y ;; *) printf n ;; esac } usage() { echo " $0 [OPTIONS] [TESTS...]" echo echo "OPTIONS:" echo " -h|--help : Print usage." echo " -L|--list-tests : List test names and quit." echo " -v : Sets VERBOSE=y." echo " -g : Sets DUMPGEN=y." echo " -V : Sets VALGRIND=y." echo " -K : Sets KMEMLEAK=y." echo " -R|--without-realroot : Sets NFT_TEST_HAS_REALROOT=n." echo " -U|--no-unshare : Sets NFT_TEST_UNSHARE_CMD=\"\"." echo " -k|--keep-logs : Sets NFT_TEST_KEEP_LOGS=y." echo " -x : Sets NFT_TEST_VERBOSE_TEST=y." echo " -s|--sequential : Sets NFT_TEST_JOBS=0, which also enables global cleanups." echo " Also sets NFT_TEST_SHUFFLE_TESTS=n if left unspecified." echo " -Q|--quick : Sets NFT_TEST_SKIP_slow=y." echo " -S|--setup-host : Modify the host to run as rootless. Otherwise, some tests will be" echo " skipped. Basically, this bumps /proc/sys/net/core/{wmem_max,rmem_max}." echo " Must run as root and this option must be specified alone." echo " -- : Separate options from tests." echo " [TESTS...] : Other options are treated as test names," echo " that is, executables that are run by the runner." echo echo "ENVIRONMENT VARIABLES:" echo " NFT= : Path to nft executable. Will be called as \`\$NFT [...]\` so" echo " it can be a command with parameters. Note that in this mode quoting" echo " does not work, so the usage is limited and the command cannot contain" echo " spaces." echo " NFT_REAL= : Real nft comand. Usually this is just the same as \$NFT," echo " however, you may set NFT='valgrind nft' and NFT_REAL to the real command." echo " VERBOSE=*|y : Enable verbose output." echo " NFT_TEST_VERBOSE_TEST=*|y: if true, enable verbose output for tests. For bash scripts, this means" echo " to pass \"-x\" to the interpreter." echo " DUMPGEN=*|y|all : Regenerate dump files \".{nft,json-nft,nodump}\". \"DUMPGEN=y\" only regenerates existing" echo " files, unless the test has no files (then all three files are generated, and you need to" echo " choose which to keep). With \"DUMPGEN=all\" all 3 files are regenerated, regardless" echo " whether they already exist." echo " VALGRIND=*|y : Run \$NFT in valgrind." echo " KMEMLEAK=*|y : Check for kernel memleaks." echo " NFT_TEST_HAS_REALROOT=*|y : To indicate whether the test has real root permissions." echo " Usually, you don't need this and it gets autodetected." echo " You might want to set it, if you know better than the" echo " \`id -u\` check, whether the user is root in the main namespace." echo " Note that without real root, certain tests may not work," echo " e.g. due to limited /proc/sys/net/core/{wmem_max,rmem_max}." echo " Checks that cannot pass in such environment should check for" echo " [ \"\$NFT_TEST_HAS_REALROOT\" != y ] and skip gracefully." echo " NFT_TEST_HAS_SOCKET_LIMITS=*|n : some tests will fail if /proc/sys/net/core/{wmem_max,rmem_max} is" echo " too small. When running as real root, then test can override those limits. However," echo " with rootless the test would fail. Tests will check for [ "\$NFT_TEST_HAS_SOCKET_LIMITS" = y ]" echo " and skip. You may set NFT_TEST_HAS_SOCKET_LIMITS=n if you ensure those limits are" echo " suitable to run the test rootless. Otherwise will be autodetected." echo " Set /proc/sys/net/core/{wmem_max,rmem_max} to at least 4MB to get them to pass automatically." echo " NFT_TEST_UNSHARE_CMD=cmd : when set, this is the command line for an unshare" echo " command, which is used to sandbox each test invocation. By" echo " setting it to empty, no unsharing is done." echo " By default it is unset, in which case it's autodetected as" echo " \`unshare -f -p\` (for root) or as \`unshare -f -p --mount-proc -U --map-root-user -n\`" echo " for non-root." echo " When setting this, you may also want to set NFT_TEST_HAS_UNSHARED=," echo " NFT_TEST_HAS_REALROOT= and NFT_TEST_HAS_UNSHARED_MOUNT= accordingly." echo " NFT_TEST_HAS_UNSHARED=*|y : To indicate to the test whether the test run will be unshared." echo " Test may consider this." echo " This is only honored when \$NFT_TEST_UNSHARE_CMD= is set. Otherwise it's detected." echo " NFT_TEST_HAS_UNSHARED_MOUNT=*|y : To indicate to the test whether the test run will have a private" echo " mount namespace." echo " This is only honored when \$NFT_TEST_UNSHARE_CMD= is set. Otherwise it's detected." echo " NFT_TEST_KEEP_LOGS=*|y: Keep the temp directory. On success, it will be deleted by default." echo " NFT_TEST_JOBS=: number of jobs for parallel execution. Defaults to \"\$(nproc)*1.5\" for parallel run." echo " Setting this to \"0\" or \"1\", means to run jobs sequentially." echo " Setting this to \"0\" means also to perform global cleanups between tests (remove" echo " kernel modules)." echo " Parallel jobs requires unshare and are disabled with NFT_TEST_UNSHARE_CMD=\"\"." echo " NFT_TEST_FAIL_ON_SKIP=*|y: if any jobs are skipped, exit with error." echo " NFT_TEST_RANDOM_SEED=: The test runner will export the environment variable NFT_TEST_RANDOM_SEED" echo " set to a random number. This can be used as a stable seed for tests to randomize behavior." echo " Set this to a fixed value to get reproducible behavior." echo " NFT_TEST_SHUFFLE_TESTS=*|n|y: control whether to randomly shuffle the order of tests. By default, if" echo " tests are specified explicitly, they are not shuffled while they are shuffled when" echo " all tests are run. The shuffling is based on NFT_TEST_RANDOM_SEED." echo " TMPDIR= : select a different base directory for the result data." echo echo " NFT_TEST_HAVE_=*|y: Some tests requires certain features or will be skipped." echo " The features are autodetected, but you can force it by setting the variable." echo " Supported s are: ${_HAVE_OPTS[@]}." echo " NFT_TEST_SKIP_