summaryrefslogtreecommitdiffstats
path: root/tests/tests.sh
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2023-10-19 16:37:47 -0400
committerTavian Barnes <tavianator@tavianator.com>2023-10-19 16:37:47 -0400
commit785a3f2d777627f39bed44f4ae7a0180d5184109 (patch)
tree2bee8cb669ab91982a7d6d1e8ada9958a2369ffd /tests/tests.sh
parentd484cba3424dcfca4851ba867d8877e3a9381a0e (diff)
downloadbfs-785a3f2d777627f39bed44f4ae7a0180d5184109.tar.xz
tests: Refactor implementation into separate files
Diffstat (limited to 'tests/tests.sh')
-rwxr-xr-xtests/tests.sh824
1 files changed, 12 insertions, 812 deletions
diff --git a/tests/tests.sh b/tests/tests.sh
index dcef28e..3890243 100755
--- a/tests/tests.sh
+++ b/tests/tests.sh
@@ -6,815 +6,15 @@
set -euP
umask 022
-export LC_ALL=C
-export TZ=UTC0
-
-SAN_OPTIONS="halt_on_error=1:log_to_syslog=0"
-export ASAN_OPTIONS="$SAN_OPTIONS"
-export LSAN_OPTIONS="$SAN_OPTIONS"
-export MSAN_OPTIONS="$SAN_OPTIONS"
-export TSAN_OPTIONS="$SAN_OPTIONS"
-export UBSAN_OPTIONS="$SAN_OPTIONS"
-
-export LS_COLORS=""
-unset BFS_COLORS
-
-BLD=$'\e[01m'
-RED=$'\e[01;31m'
-GRN=$'\e[01;32m'
-YLW=$'\e[01;33m'
-BLU=$'\e[01;34m'
-MAG=$'\e[01;35m'
-CYN=$'\e[01;36m'
-RST=$'\e[0m'
-
-function color_fd() {
- [ -z "${NO_COLOR:-}" ] && [ -t "$1" ]
-}
-
-color_fd 1 && COLOR_STDOUT=1 || COLOR_STDOUT=0
-color_fd 2 && COLOR_STDERR=1 || COLOR_STDERR=0
-
-# Filter out escape sequences if necessary
-function color() {
- if color_fd 1; then
- cat
- else
- sed $'s/\e\\[[^m]*m//g'
- fi
-}
-
-# printf with auto-detected color support
-function cprintf() {
- printf "$@" | color
-}
-
-UNAME=$(uname)
-
-if [ "$UNAME" = Darwin ]; then
- # ASan on macOS likes to report
- #
- # malloc: nano zone abandoned due to inability to preallocate reserved vm space.
- #
- # to syslog, which as a side effect opens a socket which might take the
- # place of one of the standard streams if the process is launched with it
- # closed. This environment variable avoids the message.
- export MallocNanoZone=0
-fi
-
-if command -v capsh &>/dev/null; then
- if capsh --has-p=cap_dac_override &>/dev/null || capsh --has-p=cap_dac_read_search &>/dev/null; then
- if [ -n "${BFS_TRIED_DROP:-}" ]; then
- color >&2 <<EOF
-${RED}error:${RST} Failed to drop capabilities.
-EOF
-
- exit 1
- fi
-
- color >&2 <<EOF
-${YLW}warning:${RST} Running as ${BLD}$(id -un)${RST} is not recommended. Dropping ${BLD}cap_dac_override${RST} and
-${BLD}cap_dac_read_search${RST}.
-
-EOF
-
- BFS_TRIED_DROP=y exec capsh \
- --drop=cap_dac_override,cap_dac_read_search \
- --caps=cap_dac_override,cap_dac_read_search-eip \
- -- "$0" "$@"
- fi
-elif ((EUID == 0)); then
- UNLESS=
- if [ "$UNAME" = "Linux" ]; then
- UNLESS=" unless ${GRN}capsh${RST} is installed"
- fi
-
- color >&2 <<EOF
-${RED}error:${RST} These tests expect filesystem permissions to be enforced, and therefore
-will not work when run as ${BLD}$(id -un)${RST}${UNLESS}.
-EOF
- exit 1
-fi
-
-function usage() {
- local pad=$(printf "%*s" ${#0} "")
- color <<EOF
-Usage: ${GRN}$0${RST} [${BLU}--bfs${RST}=${MAG}path/to/bfs${RST}] [${BLU}--sudo${RST}[=${BLD}COMMAND${RST}]] [${BLU}--stop${RST}]
- $pad [${BLU}--no-clean${RST}] [${BLU}--update${RST}] [${BLU}--verbose${RST}[=${BLD}LEVEL${RST}]] [${BLU}--help${RST}]
- $pad [${BLU}--posix${RST}] [${BLU}--bsd${RST}] [${BLU}--gnu${RST}] [${BLU}--all${RST}] [${BLD}TEST${RST} [${BLD}TEST${RST} ...]]
-
- ${BLU}--bfs${RST}=${MAG}path/to/bfs${RST}
- Set the path to the bfs executable to test (default: ${MAG}./bin/bfs${RST})
-
- ${BLU}--sudo${RST}[=${BLD}COMMAND${RST}]
- Run tests that require root using ${GRN}sudo${RST} or the given ${BLD}COMMAND${RST}
-
- ${BLU}--stop${RST}
- Stop when the first error occurs
-
- ${BLU}--no-clean${RST}
- Keep the test directories around after the run
-
- ${BLU}--update${RST}
- Update the expected outputs for the test cases
-
- ${BLU}--verbose${RST}=${BLD}commands${RST}
- Log the commands that get executed
- ${BLU}--verbose${RST}=${BLD}errors${RST}
- Don't redirect standard error
- ${BLU}--verbose${RST}=${BLD}skipped${RST}
- Log which tests get skipped
- ${BLU}--verbose${RST}=${BLD}tests${RST}
- Log all tests that get run
- ${BLU}--verbose${RST}
- Log everything
-
- ${BLU}--help${RST}
- This message
-
- ${BLU}--posix${RST}, ${BLU}--bsd${RST}, ${BLU}--gnu${RST}, ${BLU}--all${RST}
- Choose which test cases to run (default: ${BLU}--all${RST})
-
- ${BLD}TEST${RST}
- Select individual test cases to run (e.g. ${BLD}posix/basic${RST}, ${BLD}"*exec*"${RST}, ...)
-EOF
-}
-
-PATTERNS=()
-SUDO=()
-STOP=0
-CLEAN=1
-UPDATE=0
-VERBOSE_COMMANDS=0
-VERBOSE_ERRORS=0
-VERBOSE_SKIPPED=0
-VERBOSE_TESTS=0
-
-for arg; do
- case "$arg" in
- --bfs=*)
- BFS="${arg#*=}"
- ;;
- --posix)
- PATTERNS+=("posix/*")
- ;;
- --bsd)
- PATTERNS+=("posix/*" "common/*" "bsd/*")
- ;;
- --gnu)
- PATTERNS+=("posix/*" "common/*" "gnu/*")
- ;;
- --all)
- PATTERNS+=("*")
- ;;
- --sudo)
- SUDO=(sudo)
- ;;
- --sudo=*)
- read -a SUDO <<<"${arg#*=}"
- ;;
- --stop)
- STOP=1
- ;;
- --no-clean|--noclean)
- CLEAN=0
- ;;
- --update)
- UPDATE=1
- ;;
- --verbose=commands)
- VERBOSE_COMMANDS=1
- ;;
- --verbose=errors)
- VERBOSE_ERRORS=1
- ;;
- --verbose=skipped)
- VERBOSE_SKIPPED=1
- ;;
- --verbose=tests)
- VERBOSE_TESTS=1
- ;;
- --verbose)
- VERBOSE_COMMANDS=1
- VERBOSE_ERRORS=1
- VERBOSE_SKIPPED=1
- VERBOSE_TESTS=1
- ;;
- --help)
- usage
- exit 0
- ;;
- -*)
- cprintf "${RED}error:${RST} Unrecognized option '%s'.\n\n" "$arg" >&2
- usage >&2
- exit 1
- ;;
- *)
- PATTERNS+=("$arg")
- ;;
- esac
-done
-
-function _realpath() {
- (
- cd "$(dirname -- "$1")"
- echo "$PWD/$(basename -- "$1")"
- )
-}
-
-TESTS=$(_realpath "$(dirname -- "${BASH_SOURCE[0]}")")
-
-if [ "${BUILDDIR-}" ]; then
- BIN=$(_realpath "$BUILDDIR/bin")
-else
- BIN=$(_realpath "$TESTS/../bin")
-fi
-MKSOCK="$BIN/tests/mksock"
-XTOUCH="$BIN/tests/xtouch"
-
-# Try to resolve the path to $BFS before we cd, while also supporting
-# --bfs="./bin/bfs -S ids"
-read -a BFS <<<"${BFS:-$BIN/bfs}"
-BFS[0]=$(_realpath "$(command -v "${BFS[0]}")")
-
-# The temporary directory that will hold our test data
-TMP=$(mktemp -d "${TMPDIR:-/tmp}"/bfs.XXXXXXXXXX)
-chown "$(id -u):$(id -g)" "$TMP"
-
-cd "$TESTS"
-
-if ((${#PATTERNS[@]} == 0)); then
- PATTERNS=("*")
-fi
-
-TEST_CASES=()
-for TEST in {posix,common,bsd,gnu,bfs}/*.sh; do
- TEST="${TEST%.sh}"
- for PATTERN in "${PATTERNS[@]}"; do
- if [[ $TEST == $PATTERN ]]; then
- TEST_CASES+=("$TEST")
- break
- fi
- done
-done
-
-if ((${#TEST_CASES[@]} == 0)); then
- cprintf "${RED}error:${RST} No tests matched" >&2
- cprintf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2
- cprintf ".\n\n" >&2
- usage >&2
- exit 1
-fi
-
-function quote() {
- printf '%q' "$1"
- shift
- if (($# > 0)); then
- printf ' %q' "$@"
- fi
-}
-
-# Run a command when this (sub)shell exits
-function defer() {
- trap -- KILL
- if ! trap -p EXIT | grep -q pop_defers; then
- DEFER_CMDS=()
- DEFER_LINES=()
- DEFER_FILES=()
- trap pop_defers EXIT
- fi
-
- DEFER_CMDS+=("$(quote "$@")")
-
- local line file
- read -r line file < <(caller)
- DEFER_LINES+=("$line")
- DEFER_FILES+=("$file")
-}
-
-function report_err() {
- local file="${1/#*\/tests\//tests\/}"
- set -- "$file" "${@:2}"
-
- if ((COLOR_STDERR)); then
- printf "${BLD}%s:%d:${RST} ${RED}error %d:${RST}\n %s\n" "$@" >&2
- else
- printf "%s:%d: error %d:\n %s\n" "$@" >&2
- fi
-}
-
-function pop_defer() {
- local cmd="${DEFER_CMDS[-1]}"
- local file="${DEFER_FILES[-1]}"
- local line="${DEFER_LINES[-1]}"
- unset "DEFER_CMDS[-1]"
- unset "DEFER_FILES[-1]"
- unset "DEFER_LINES[-1]"
-
- local ret=0
- eval "$cmd" || ret=$?
-
- if ((ret != 0)); then
- report_err "$file" $line $ret "defer $cmd"
- fi
-
- return $ret
-}
-
-function pop_defers() {
- local ret=0
-
- while ((${#DEFER_CMDS[@]} > 0)); do
- pop_defer || ret=$?
- done
-
- return $ret
-}
-
-function bfs_sudo() {
- if ((${#SUDO[@]})); then
- "${SUDO[@]}" "$@"
- else
- return 1
- fi
-}
-
-function clean_scratch() {
- if [ -e "$TMP/scratch" ]; then
- # Try to unmount anything left behind
- if ((${#SUDO[@]})) && command -v mountpoint &>/dev/null; then
- for path in "$TMP"/scratch/*; do
- if mountpoint -q "$path"; then
- sudo umount "$path"
- fi
- done
- fi
-
- # Reset any modified permissions
- chmod -R +rX "$TMP/scratch"
-
- rm -rf "$TMP/scratch"
- fi
-
- mkdir "$TMP/scratch"
-}
-
-# Clean up temporary directories on exit
-function cleanup() {
- # Don't force rm to deal with long paths
- for dir in "$TMP"/deep/*/*; do
- if [ -d "$dir" ]; then
- (cd "$dir" && rm -rf *)
- fi
- done
-
- # In case a test left anything weird in scratch/
- clean_scratch
-
- rm -rf "$TMP"
-}
-
-if ((CLEAN)); then
- defer cleanup
-else
- echo "Test files saved to $TMP"
-fi
-
-# Creates a simple file+directory structure for tests
-function make_basic() {
- "$XTOUCH" -p "$1"/{a,b,c/d,e/f,g/h/,i/}
- "$XTOUCH" -p "$1"/{j/foo,k/foo/bar,l/foo/bar/baz}
- echo baz >"$1/l/foo/bar/baz"
-}
-make_basic "$TMP/basic"
-
-# Creates a file+directory structure with various permissions for tests
-function make_perms() {
- "$XTOUCH" -p -M000 "$1/0"
- "$XTOUCH" -p -M444 "$1/r"
- "$XTOUCH" -p -M222 "$1/w"
- "$XTOUCH" -p -M644 "$1/rw"
- "$XTOUCH" -p -M555 "$1/rx"
- "$XTOUCH" -p -M311 "$1/wx"
- "$XTOUCH" -p -M755 "$1/rwx"
-}
-make_perms "$TMP/perms"
-
-# Creates a file+directory structure with various symbolic and hard links
-function make_links() {
- "$XTOUCH" -p "$1/file"
- ln -s file "$1/symlink"
- ln "$1/file" "$1/hardlink"
- ln -s nowhere "$1/broken"
- ln -s symlink/file "$1/notdir"
- "$XTOUCH" -p "$1/deeply/nested"/{dir/,file}
- ln -s file "$1/deeply/nested/link"
- ln -s nowhere "$1/deeply/nested/broken"
- ln -s deeply/nested "$1/skip"
-}
-make_links "$TMP/links"
-
-# Creates a file+directory structure with symbolic link loops
-function make_loops() {
- "$XTOUCH" -p "$1/file"
- ln -s file "$1/symlink"
- ln -s nowhere "$1/broken"
- ln -s symlink/file "$1/notdir"
- ln -s loop "$1/loop"
- mkdir -p "$1/deeply/nested/dir"
- ln -s ../../deeply "$1/deeply/nested/loop"
- ln -s deeply/nested/loop/nested "$1/skip"
-}
-make_loops "$TMP/loops"
-
-# Creates a file+directory structure with varying timestamps
-function make_times() {
- "$XTOUCH" -p -t "1991-12-14 00:00" "$1/a"
- "$XTOUCH" -p -t "1991-12-14 00:01" "$1/b"
- "$XTOUCH" -p -t "1991-12-14 00:02" "$1/c"
- ln -s a "$1/l"
- "$XTOUCH" -p -h -t "1991-12-14 00:03" "$1/l"
- "$XTOUCH" -p -t "1991-12-14 00:04" "$1"
-}
-make_times "$TMP/times"
-
-# Creates a file+directory structure with various weird file/directory names
-function make_weirdnames() {
- "$XTOUCH" -p "$1/-/a"
- "$XTOUCH" -p "$1/(/b"
- "$XTOUCH" -p "$1/(-/c"
- "$XTOUCH" -p "$1/!/d"
- "$XTOUCH" -p "$1/!-/e"
- "$XTOUCH" -p "$1/,/f"
- "$XTOUCH" -p "$1/)/g"
- "$XTOUCH" -p "$1/.../h"
- "$XTOUCH" -p "$1/\\/i"
- "$XTOUCH" -p "$1/ /j"
- "$XTOUCH" -p "$1/[/k"
-}
-make_weirdnames "$TMP/weirdnames"
-
-# Creates a very deep directory structure for testing PATH_MAX handling
-function make_deep() {
- mkdir -p "$1"
-
- # $name will be 255 characters, aka _XOPEN_NAME_MAX
- local name="0123456789ABCDEF"
- name="${name}${name}${name}${name}"
- name="${name}${name}${name}${name}"
- name="${name:0:255}"
-
- for i in {0..9} A B C D E F; do
- "$XTOUCH" -p "$1/$i/$name"
-
- (
- cd "$1/$i"
-
- # 8 * 512 == 4096 >= PATH_MAX
- for _ in {1..8}; do
- mv "$name" ..
- mkdir -p "$name/$name"
- mv "../$name" "$name/$name/"
- done
- )
- done
-}
-make_deep "$TMP/deep"
-
-# Creates a directory structure with many different types, and therefore colors
-function make_rainbow() {
- "$XTOUCH" -p "$1/file.txt"
- "$XTOUCH" -p "$1/file.dat"
- "$XTOUCH" -p "$1/lower".{gz,tar,tar.gz}
- "$XTOUCH" -p "$1/upper".{GZ,TAR,TAR.GZ}
- "$XTOUCH" -p "$1/lu.tar.GZ" "$1/ul.TAR.gz"
- ln -s file.txt "$1/link.txt"
- "$XTOUCH" -p "$1/mh1"
- ln "$1/mh1" "$1/mh2"
- mkfifo "$1/pipe"
- # TODO: block
- ln -s /dev/null "$1/chardev_link"
- ln -s nowhere "$1/broken"
- "$MKSOCK" "$1/socket"
- "$XTOUCH" -p "$1"/s{u,g,ug}id
- chmod u+s "$1"/su{,g}id
- chmod g+s "$1"/s{u,}gid
- mkdir "$1/ow" "$1"/sticky{,_ow}
- chmod o+w "$1"/*ow
- chmod +t "$1"/sticky*
- "$XTOUCH" -p "$1"/exec.sh
- chmod +x "$1"/exec.sh
- "$XTOUCH" -p "$1/"$'\e[1m/\e[0m'
-}
-make_rainbow "$TMP/rainbow"
-
-mkdir "$TMP/scratch"
-
-# Close stdin so bfs doesn't think we're interactive
-exec </dev/null
-
-if ((VERBOSE_COMMANDS)); then
- # dup stdout for verbose logging even when redirected
- exec 3>&1
-fi
-
-function bfs_verbose() {
- if ((!VERBOSE_COMMANDS)); then
- return
- fi
-
- {
- printf "${GRN}%q${RST} " "${BFS[@]}"
-
- local expr_started=
- for arg; do
- if [[ $arg == -[A-Z]* ]]; then
- printf "${CYN}%q${RST} " "$arg"
- elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then
- expr_started=yes
- printf "${RED}%q${RST} " "$arg"
- elif [[ $expr_started && $arg == [\),] ]]; then
- printf "${RED}%q${RST} " "$arg"
- elif [[ $arg == -?* ]]; then
- expr_started=yes
- printf "${BLU}%q${RST} " "$arg"
- elif [ "$expr_started" ]; then
- printf "${BLD}%q${RST} " "$arg"
- else
- printf "${MAG}%q${RST} " "$arg"
- fi
- done
-
- printf '\n'
- } | color >&3
-}
-
-function invoke_bfs() {
- bfs_verbose "$@"
-
- local ret=0
- "${BFS[@]}" "$@" || ret=$?
-
- # Allow bfs to fail, but not crash
- if ((ret > 125)); then
- exit "$ret"
- else
- return "$ret"
- fi
-}
-
-if command -v unbuffer &>/dev/null; then
- UNBUFFER=unbuffer
-elif command -v expect_unbuffer &>/dev/null; then
- UNBUFFER=expect_unbuffer
-fi
-
-function bfs_pty() {
- test -n "${UNBUFFER:-}" || skip
-
- bfs_verbose "$@"
-
- local ret=0
- "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" || ret=$?
-
- if ((ret > 125)); then
- exit "$ret"
- else
- return "$ret"
- fi
-}
-
-function check_exit() {
- local expected="$1"
- local actual="0"
- shift
- "$@" || actual="$?"
- ((actual == expected))
-}
-
-# Detect colored diff support
-if ((COLOR_STDERR)) && diff --color=always /dev/null /dev/null 2>/dev/null; then
- DIFF="diff --color=always"
-else
- DIFF="diff"
-fi
-
-# Return value when a difference is detected
-EX_DIFF=20
-# Return value when a test is skipped
-EX_SKIP=77
-
-function sort_output() {
- sort -o "$OUT" "$OUT"
-}
-
-function diff_output() {
- local GOLD="$TESTS/$TEST.out"
-
- if ((UPDATE)); then
- cp "$OUT" "$GOLD"
- else
- $DIFF -u "$GOLD" "$OUT" >&2
- fi
-}
-
-function bfs_diff() (
- bfs_verbose "$@"
-
- # Close the dup()'d stdout to make sure we have enough fd's for the process
- # substitution, even with low ulimit -n
- exec 3>&-
-
- "${BFS[@]}" "$@" | sort >"$OUT"
- local status="${PIPESTATUS[0]}"
-
- diff_output || exit $EX_DIFF
- return "$status"
-)
-
-function skip() {
- if ((VERBOSE_SKIPPED)); then
- caller | {
- read -r line file
- cprintf "${BOL}${CYN}%s skipped!${RST} (%s)\n" "$TEST" "$(awk "NR == $line" "$file")"
- }
- elif ((VERBOSE_TESTS)); then
- cprintf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST"
- fi
-
- exit $EX_SKIP
-}
-
-function closefrom() {
- if [ -d /proc/self/fd ]; then
- local fds=/proc/self/fd
- else
- local fds=/dev/fd
- fi
-
- for fd in "$fds"/*; do
- if [ ! -e "$fd" ]; then
- continue
- fi
-
- local fd="${fd##*/}"
- if ((fd >= $1)); then
- eval "exec ${fd}<&-"
- fi
- done
-}
-
-function inum() {
- ls -id "$@" | awk '{ print $1 }'
-}
-
-function set_acl() {
- case "$UNAME" in
- Darwin)
- chmod +a "$(id -un) allow read,write" "$1"
- ;;
- FreeBSD)
- if (($(getconf ACL_NFS4 "$1") > 0)); then
- setfacl -m "u:$(id -un):rw::allow" "$1"
- else
- setfacl -m "u:$(id -un):rw" "$1"
- fi
- ;;
- *)
- setfacl -m "u:$(id -un):rw" "$1"
- ;;
- esac
-}
-
-function make_xattrs() {
- clean_scratch
-
- "$XTOUCH" scratch/{normal,xattr,xattr_2}
- ln -s xattr scratch/link
- ln -s normal scratch/xattr_link
-
- case "$UNAME" in
- Darwin)
- xattr -w bfs_test true scratch/xattr \
- && xattr -w bfs_test_2 true scratch/xattr_2 \
- && xattr -s -w bfs_test true scratch/xattr_link
- ;;
- FreeBSD)
- setextattr user bfs_test true scratch/xattr \
- && setextattr user bfs_test_2 true scratch/xattr_2 \
- && setextattr -h user bfs_test true scratch/xattr_link
- ;;
- *)
- # Linux tmpfs doesn't support the user.* namespace, so we use the security.*
- # namespace, which is writable by root and readable by others
- bfs_sudo setfattr -n security.bfs_test scratch/xattr \
- && bfs_sudo setfattr -n security.bfs_test_2 scratch/xattr_2 \
- && bfs_sudo setfattr -h -n security.bfs_test scratch/xattr_link
- ;;
- esac
-}
-
-cd "$TMP"
-set +e
-
-BOL='\n'
-EOL='\n'
-
-function update_eol() {
- # Bash gets $COLUMNS from stderr, so if it's redirected use tput instead
- local cols="${COLUMNS-}"
- if [ -z "$cols" ]; then
- cols=$(tput cols)
- fi
-
- # Put the cursor at the last column, then write a space so the next
- # character will wrap
- EOL="\\033[${cols}G "
-}
-
-if ((VERBOSE_TESTS)); then
- BOL=''
-elif ((COLOR_STDOUT)); then
- BOL='\r\033[K'
-
- # Workaround for bash 4: checkwinsize is off by default. We can turn it on,
- # but we also have to explicitly trigger a foreground job to finish so that
- # it will update the window size before we use $COLUMNS
- shopt -s checkwinsize
- (:)
-
- update_eol
- trap update_eol WINCH
-fi
-
-function callers() {
- local frame=0
- while caller $frame; do
- ((++frame))
- done
-}
-
-function debug_err() {
- local ret=$? line func file
- callers | while read -r line func file; do
- if [ "$func" = source ]; then
- local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || :
- report_err "$file" $line $ret "$cmd"
- break
- fi
- done
-}
-
-function run_test() (
- set -eE
- trap debug_err ERR
- source "$@"
-)
-
-passed=0
-failed=0
-skipped=0
-
-if ((COLOR_STDOUT || VERBOSE_TESTS)); then
- TEST_FMT="${BOL}${YLW}%s${RST}${EOL}"
-else
- TEST_FMT="."
-fi
-
-for TEST in "${TEST_CASES[@]}"; do
- printf "$TEST_FMT" "$TEST"
-
- OUT="$TMP/$TEST.out"
- mkdir -p "${OUT%/*}"
-
- if ((VERBOSE_ERRORS)); then
- run_test "$TESTS/$TEST.sh"
- else
- run_test "$TESTS/$TEST.sh" 2>"$TMP/$TEST.err"
- fi
- status=$?
-
- if ((status == 0)); then
- ((++passed))
- elif ((status == EX_SKIP)); then
- ((++skipped))
- else
- ((++failed))
- ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2
- cprintf "${BOL}${RED}%s failed!${RST}\n" "$TEST"
- ((STOP)) && break
- fi
-done
-
-printf "${BOL}"
-
-if ((passed > 0)); then
- cprintf "${GRN}tests passed: %d${RST}\n" "$passed"
-fi
-if ((skipped > 0)); then
- cprintf "${CYN}tests skipped: %s${RST}\n" "$skipped"
-fi
-if ((failed > 0)); then
- cprintf "${RED}tests failed: %s${RST}\n" "$failed"
- exit 1
-fi
+TESTS="$(dirname -- "${BASH_SOURCE[0]}")"
+. "$TESTS/util.sh"
+. "$TESTS/color.sh"
+. "$TESTS/stddirs.sh"
+. "$TESTS/getopts.sh"
+. "$TESTS/run.sh"
+
+stdenv
+drop_root "$@"
+parse_args "$@"
+make_stddirs
+run_tests