summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/bfs/nohidden.out8
-rw-r--r--tests/bfs/nohidden_depth.out8
-rw-r--r--tests/bfs/printf_invalid_flag.sh1
-rw-r--r--tests/bfs/xtype_depth.sh9
-rw-r--r--tests/bfstd.c4
-rw-r--r--tests/bsd/X.out4
-rw-r--r--tests/bsd/printx.out8
-rw-r--r--tests/bsd/s.out4
-rw-r--r--tests/color.sh116
-rw-r--r--tests/gnu/L_loops_continue.out1
-rw-r--r--tests/gnu/files0_from_file.out12
-rw-r--r--tests/gnu/files0_from_stdin.out12
-rw-r--r--tests/gnu/follow_comma.out8
-rw-r--r--tests/gnu/ignore_readdir_race_loop.out12
-rw-r--r--tests/gnu/ignore_readdir_race_loop.sh2
-rw-r--r--tests/gnu/printf_flags.sh2
-rw-r--r--tests/gnu/regextype_awk.out2
-rw-r--r--tests/gnu/regextype_awk.sh3
-rw-r--r--tests/gnu/regextype_egrep.out0
-rw-r--r--tests/gnu/regextype_egrep.sh3
-rw-r--r--tests/gnu/regextype_emacs.sh2
-rw-r--r--tests/gnu/regextype_findutils_default.out3
-rw-r--r--tests/gnu/regextype_findutils_default.sh3
-rw-r--r--tests/gnu/regextype_gnu_awk.out2
-rw-r--r--tests/gnu/regextype_gnu_awk.sh3
-rw-r--r--tests/gnu/regextype_posix_awk.out2
-rw-r--r--tests/gnu/regextype_posix_awk.sh3
-rw-r--r--tests/gnu/regextype_posix_minimal_basic.out1
-rw-r--r--tests/gnu/regextype_posix_minimal_basic.sh2
-rw-r--r--tests/ioq.c6
-rw-r--r--tests/main.c6
-rw-r--r--tests/posix/prune_error.out1
-rw-r--r--tests/posix/prune_error.sh8
-rw-r--r--tests/run.sh90
-rw-r--r--tests/sighook.c97
-rw-r--r--tests/stddirs.sh3
-rw-r--r--tests/tests.h19
-rw-r--r--tests/util.sh9
-rw-r--r--tests/xspawn.c42
-rw-r--r--tests/xtime.c20
-rw-r--r--tests/xtouch.c2
41 files changed, 439 insertions, 104 deletions
diff --git a/tests/bfs/nohidden.out b/tests/bfs/nohidden.out
index d3ec901..84e6bd2 100644
--- a/tests/bfs/nohidden.out
+++ b/tests/bfs/nohidden.out
@@ -1,4 +1,8 @@
+
+/n
weirdnames
+weirdnames/
+weirdnames/
weirdnames/
weirdnames/ /j
weirdnames/!
@@ -11,6 +15,8 @@ weirdnames/(-/c
weirdnames/(/b
weirdnames/)
weirdnames/)/g
+weirdnames/*
+weirdnames/*/m
weirdnames/,
weirdnames/,/f
weirdnames/-
@@ -19,3 +25,5 @@ weirdnames/[
weirdnames/[/k
weirdnames/\
weirdnames/\/i
+weirdnames/{
+weirdnames/{/l
diff --git a/tests/bfs/nohidden_depth.out b/tests/bfs/nohidden_depth.out
index d3ec901..84e6bd2 100644
--- a/tests/bfs/nohidden_depth.out
+++ b/tests/bfs/nohidden_depth.out
@@ -1,4 +1,8 @@
+
+/n
weirdnames
+weirdnames/
+weirdnames/
weirdnames/
weirdnames/ /j
weirdnames/!
@@ -11,6 +15,8 @@ weirdnames/(-/c
weirdnames/(/b
weirdnames/)
weirdnames/)/g
+weirdnames/*
+weirdnames/*/m
weirdnames/,
weirdnames/,/f
weirdnames/-
@@ -19,3 +25,5 @@ weirdnames/[
weirdnames/[/k
weirdnames/\
weirdnames/\/i
+weirdnames/{
+weirdnames/{/l
diff --git a/tests/bfs/printf_invalid_flag.sh b/tests/bfs/printf_invalid_flag.sh
new file mode 100644
index 0000000..70dfe97
--- /dev/null
+++ b/tests/bfs/printf_invalid_flag.sh
@@ -0,0 +1 @@
+! invoke_bfs basic -printf '% p'
diff --git a/tests/bfs/xtype_depth.sh b/tests/bfs/xtype_depth.sh
index 02c8173..93c4fb7 100644
--- a/tests/bfs/xtype_depth.sh
+++ b/tests/bfs/xtype_depth.sh
@@ -1,2 +1,9 @@
+cd "$TEST"
+"$XTOUCH" -p foo/bar
+ln -s foo/bar baz
+
+chmod a-rx foo
+defer chmod +rx foo
+
# Make sure -xtype is considered side-effecting for facts_when_impure
-! invoke_bfs loops -xtype l -depth 100
+! invoke_bfs . -xtype l -depth 100
diff --git a/tests/bfstd.c b/tests/bfstd.c
index 07b68b0..f0f61ce 100644
--- a/tests/bfstd.c
+++ b/tests/bfstd.c
@@ -15,12 +15,12 @@ static bool check_base_dir(const char *path, const char *dir, const char *base)
bool ret = true;
char *xdir = xdirname(path);
- bfs_verify(xdir, "xdirname(): %s", xstrerror(errno));
+ bfs_everify(xdir, "xdirname()");
ret &= bfs_check(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir);
free(xdir);
char *xbase = xbasename(path);
- bfs_verify(xbase, "xbasename(): %s", xstrerror(errno));
+ bfs_everify(xbase, "xbasename()");
ret &= bfs_check(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base);
free(xbase);
diff --git a/tests/bsd/X.out b/tests/bsd/X.out
index afa84f7..dbe2408 100644
--- a/tests/bsd/X.out
+++ b/tests/bsd/X.out
@@ -9,6 +9,8 @@ weirdnames/(-/c
weirdnames/(/b
weirdnames/)
weirdnames/)/g
+weirdnames/*
+weirdnames/*/m
weirdnames/,
weirdnames/,/f
weirdnames/-
@@ -17,3 +19,5 @@ weirdnames/...
weirdnames/.../h
weirdnames/[
weirdnames/[/k
+weirdnames/{
+weirdnames/{/l
diff --git a/tests/bsd/printx.out b/tests/bsd/printx.out
index 04bf9a9..034b2da 100644
--- a/tests/bsd/printx.out
+++ b/tests/bsd/printx.out
@@ -1,3 +1,5 @@
+
+/n
weirdnames
weirdnames/!
weirdnames/!-
@@ -9,6 +11,8 @@ weirdnames/(-/c
weirdnames/(/b
weirdnames/)
weirdnames/)/g
+weirdnames/*
+weirdnames/*/m
weirdnames/,
weirdnames/,/f
weirdnames/-
@@ -17,7 +21,11 @@ weirdnames/...
weirdnames/.../h
weirdnames/[
weirdnames/[/k
+weirdnames/\
+weirdnames/\
weirdnames/\
weirdnames/\ /j
weirdnames/\\
weirdnames/\\/i
+weirdnames/{
+weirdnames/{/l
diff --git a/tests/bsd/s.out b/tests/bsd/s.out
index 6b790c2..5c85ac8 100644
--- a/tests/bsd/s.out
+++ b/tests/bsd/s.out
@@ -1,12 +1,16 @@
weirdnames
+weirdnames/
+
weirdnames/
weirdnames/!
weirdnames/!-
weirdnames/(
weirdnames/(-
weirdnames/)
+weirdnames/*
weirdnames/,
weirdnames/-
weirdnames/...
weirdnames/[
weirdnames/\
+weirdnames/{
diff --git a/tests/color.sh b/tests/color.sh
index 805d2b8..4f4312e 100644
--- a/tests/color.sh
+++ b/tests/color.sh
@@ -35,3 +35,119 @@ color() {
"$@" | "$SED" $'s/\e\\[[^m]*m//g'
fi
}
+
+## Status bar
+
+# Show the terminal status bar
+show_bar() {
+ if [ -z "$TTY" ]; then
+ return 1
+ fi
+
+ # Name the pipe deterministically based on the ttyname, so that concurrent
+ # tests.sh runs on the same terminal (e.g. make -jN check) cooperate
+ local pipe
+ pipe=$(printf '%s' "$TTY" | tr '/' '-')
+ pipe="${TMPDIR:-/tmp}/bfs$pipe.bar"
+
+ if mkfifo "$pipe" 2>/dev/null; then
+ # We won the race, create the background process to manage the bar
+ bar_proc "$pipe" &
+ exec {BAR}>"$pipe"
+ elif [ -p "$pipe" ]; then
+ # We lost the race, connect to the existing process.
+ # There is a small TOCTTOU race here but I don't see how to avoid it.
+ exec {BAR}>"$pipe"
+ else
+ return 1
+ fi
+}
+
+# Print to the terminal status bar
+print_bar() {
+ printf 'PRINT:%d:%s\0' $$ "$1" >&$BAR
+}
+
+# Hide the terminal status bar
+hide_bar() {
+ printf 'HIDE:%d:\0' $$ >&$BAR
+ exec {BAR}>&-
+ unset BAR
+}
+
+# The background process that muxes multiple status bars for one TTY
+bar_proc() {
+ # Read from the pipe, write to the TTY
+ exec <"$1" >"$TTY"
+
+ # Delete the pipe when done
+ defer rm "$1"
+ # Reset the scroll region when done
+ defer printf '\e7\e[r\e8\e[J'
+
+ # 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 $LINES
+ shopt -s checkwinsize
+ (:)
+
+ BAR_HEIGHT=0
+ resize_bar
+ # Adjust the bar when the TTY size changes
+ trap resize_bar WINCH
+
+ # Map from PID to status bar
+ local -A pid2bar
+
+ # Read commands of the form "OP:PID:STRING\0"
+ while IFS=':' read -r -d '' op pid str; do
+ # Map the pid to a bar, creating a new one if necessary
+ if [ -z "${pid2bar[$pid]:-}" ]; then
+ pid2bar["$pid"]=$((BAR_HEIGHT++))
+ resize_bar
+ fi
+ bar="${pid2bar[$pid]}"
+
+ case "$op" in
+ PRINT)
+ printf '\e7\e[%d;0f\e[K%s\e8' $((TTY_HEIGHT - bar)) "$str"
+ ;;
+ HIDE)
+ bar="${pid2bar[$pid]}"
+ # Delete this status bar
+ unset 'pid2bar[$pid]'
+ # Shift all higher status bars down
+ for i in "${!pid2bar[@]}"; do
+ ibar="${pid2bar[$i]}"
+ if ((ibar > bar)); then
+ pid2bar["$i"]=$((ibar - 1))
+ fi
+ done
+ ((BAR_HEIGHT--))
+ resize_bar
+ ;;
+ esac
+ done
+}
+
+# Resize the status bar
+resize_bar() {
+ # Bash gets $LINES from stderr, so if it's redirected use tput instead
+ TTY_HEIGHT="${LINES:-$(tput lines 2>"$TTY")}"
+
+ if ((BAR_HEIGHT == 0)); then
+ return
+ fi
+
+ # Hide the bars temporarily
+ local seq='\e7\e[r\e8\e[J'
+ # Print \eD (IND) N times to ensure N blank lines at the bottom
+ for ((i = 0; i < BAR_HEIGHT; ++i)); do
+ seq="${seq}\\eD"
+ done
+ # Go back up N lines
+ seq="${seq}\\e[${BAR_HEIGHT}A"
+ # Create the new scroll region
+ seq="${seq}\\e7\\e[;$((TTY_HEIGHT - BAR_HEIGHT))r\\e8\\e[J"
+ printf "$seq"
+}
diff --git a/tests/gnu/L_loops_continue.out b/tests/gnu/L_loops_continue.out
index a514555..faf33d3 100644
--- a/tests/gnu/L_loops_continue.out
+++ b/tests/gnu/L_loops_continue.out
@@ -4,6 +4,7 @@ loops/deeply
loops/deeply/nested
loops/deeply/nested/dir
loops/file
+loops/loop
loops/notdir
loops/skip
loops/skip/dir
diff --git a/tests/gnu/files0_from_file.out b/tests/gnu/files0_from_file.out
index 1d87e6b..0f6b00d 100644
--- a/tests/gnu/files0_from_file.out
+++ b/tests/gnu/files0_from_file.out
@@ -1,3 +1,7 @@
+
+
+
+
/j
/j
@@ -16,6 +20,9 @@
)
)/g
)/g
+*
+*/m
+*/m
,
,/f
,/f
@@ -25,9 +32,14 @@
...
.../h
.../h
+/n
+/n
[
[/k
[/k
\
\/i
\/i
+{
+{/l
+{/l
diff --git a/tests/gnu/files0_from_stdin.out b/tests/gnu/files0_from_stdin.out
index 1d87e6b..0f6b00d 100644
--- a/tests/gnu/files0_from_stdin.out
+++ b/tests/gnu/files0_from_stdin.out
@@ -1,3 +1,7 @@
+
+
+
+
/j
/j
@@ -16,6 +20,9 @@
)
)/g
)/g
+*
+*/m
+*/m
,
,/f
,/f
@@ -25,9 +32,14 @@
...
.../h
.../h
+/n
+/n
[
[/k
[/k
\
\/i
\/i
+{
+{/l
+{/l
diff --git a/tests/gnu/follow_comma.out b/tests/gnu/follow_comma.out
index 920b3d3..5e4b806 100644
--- a/tests/gnu/follow_comma.out
+++ b/tests/gnu/follow_comma.out
@@ -1,4 +1,7 @@
+
.
+./
+./
./
./ /j
./!
@@ -11,6 +14,8 @@
./(/b
./)
./)/g
+./*
+./*/m
./,
./,/f
./-
@@ -21,3 +26,6 @@
./[/k
./\
./\/i
+./{
+./{/l
+/n
diff --git a/tests/gnu/ignore_readdir_race_loop.out b/tests/gnu/ignore_readdir_race_loop.out
new file mode 100644
index 0000000..faf33d3
--- /dev/null
+++ b/tests/gnu/ignore_readdir_race_loop.out
@@ -0,0 +1,12 @@
+loops
+loops/broken
+loops/deeply
+loops/deeply/nested
+loops/deeply/nested/dir
+loops/file
+loops/loop
+loops/notdir
+loops/skip
+loops/skip/dir
+loops/skip/loop
+loops/symlink
diff --git a/tests/gnu/ignore_readdir_race_loop.sh b/tests/gnu/ignore_readdir_race_loop.sh
new file mode 100644
index 0000000..3329169
--- /dev/null
+++ b/tests/gnu/ignore_readdir_race_loop.sh
@@ -0,0 +1,2 @@
+# Make sure -ignore_readdir_race doesn't suppress ELOOP from an actual filesystem loop
+! bfs_diff -L loops -ignore_readdir_race
diff --git a/tests/gnu/printf_flags.sh b/tests/gnu/printf_flags.sh
index 2ef37ad..98e8faa 100644
--- a/tests/gnu/printf_flags.sh
+++ b/tests/gnu/printf_flags.sh
@@ -1 +1 @@
-bfs_diff basic -printf '|%- 10.10p| %+03d %#4m\n'
+bfs_diff basic -printf '|%-10.10p| %+03d % #4m\n'
diff --git a/tests/gnu/regextype_awk.out b/tests/gnu/regextype_awk.out
new file mode 100644
index 0000000..0f32fc4
--- /dev/null
+++ b/tests/gnu/regextype_awk.out
@@ -0,0 +1,2 @@
+weirdnames/*/m
+weirdnames/[/k
diff --git a/tests/gnu/regextype_awk.sh b/tests/gnu/regextype_awk.sh
new file mode 100644
index 0000000..3718473
--- /dev/null
+++ b/tests/gnu/regextype_awk.sh
@@ -0,0 +1,3 @@
+invoke_bfs -regextype awk -quit || skip
+
+bfs_diff weirdnames -regextype awk -regex '.*/[\[\*]/.*'
diff --git a/tests/gnu/regextype_egrep.out b/tests/gnu/regextype_egrep.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/gnu/regextype_egrep.out
diff --git a/tests/gnu/regextype_egrep.sh b/tests/gnu/regextype_egrep.sh
new file mode 100644
index 0000000..281d9c0
--- /dev/null
+++ b/tests/gnu/regextype_egrep.sh
@@ -0,0 +1,3 @@
+invoke_bfs -regextype egrep -quit || skip
+
+bfs_diff weirdnames -regextype egrep -regex '*.*/{l'
diff --git a/tests/gnu/regextype_emacs.sh b/tests/gnu/regextype_emacs.sh
index 3cc388c..164d17a 100644
--- a/tests/gnu/regextype_emacs.sh
+++ b/tests/gnu/regextype_emacs.sh
@@ -1,3 +1,3 @@
invoke_bfs -regextype emacs -quit || skip
-bfs_diff basic -regextype emacs -regex '.*/\(f+o?o?\|bar\)'
+bfs_diff basic -regextype emacs -regex '.*/\(?:f+o?o?\|bar\)'
diff --git a/tests/gnu/regextype_findutils_default.out b/tests/gnu/regextype_findutils_default.out
new file mode 100644
index 0000000..709a7ba
--- /dev/null
+++ b/tests/gnu/regextype_findutils_default.out
@@ -0,0 +1,3 @@
+/n
+weirdnames/
+weirdnames/*/m
diff --git a/tests/gnu/regextype_findutils_default.sh b/tests/gnu/regextype_findutils_default.sh
new file mode 100644
index 0000000..c870312
--- /dev/null
+++ b/tests/gnu/regextype_findutils_default.sh
@@ -0,0 +1,3 @@
+invoke_bfs -regextype findutils-default -quit || skip
+
+bfs_diff weirdnames -regextype findutils-default -regex '.*/./\(m\|n\)'
diff --git a/tests/gnu/regextype_gnu_awk.out b/tests/gnu/regextype_gnu_awk.out
new file mode 100644
index 0000000..0f32fc4
--- /dev/null
+++ b/tests/gnu/regextype_gnu_awk.out
@@ -0,0 +1,2 @@
+weirdnames/*/m
+weirdnames/[/k
diff --git a/tests/gnu/regextype_gnu_awk.sh b/tests/gnu/regextype_gnu_awk.sh
new file mode 100644
index 0000000..6b66496
--- /dev/null
+++ b/tests/gnu/regextype_gnu_awk.sh
@@ -0,0 +1,3 @@
+invoke_bfs -regextype gnu-awk -quit || skip
+
+bfs_diff weirdnames -regextype gnu-awk -regex '.*/[\[\*]/(\<.\>)'
diff --git a/tests/gnu/regextype_posix_awk.out b/tests/gnu/regextype_posix_awk.out
new file mode 100644
index 0000000..0f32fc4
--- /dev/null
+++ b/tests/gnu/regextype_posix_awk.out
@@ -0,0 +1,2 @@
+weirdnames/*/m
+weirdnames/[/k
diff --git a/tests/gnu/regextype_posix_awk.sh b/tests/gnu/regextype_posix_awk.sh
new file mode 100644
index 0000000..86377d7
--- /dev/null
+++ b/tests/gnu/regextype_posix_awk.sh
@@ -0,0 +1,3 @@
+invoke_bfs -regextype posix-awk -quit || skip
+
+bfs_diff weirdnames -regextype posix-awk -regex '.*/[\[\*]/.*'
diff --git a/tests/gnu/regextype_posix_minimal_basic.out b/tests/gnu/regextype_posix_minimal_basic.out
new file mode 100644
index 0000000..0f0971e
--- /dev/null
+++ b/tests/gnu/regextype_posix_minimal_basic.out
@@ -0,0 +1 @@
+./(
diff --git a/tests/gnu/regextype_posix_minimal_basic.sh b/tests/gnu/regextype_posix_minimal_basic.sh
new file mode 100644
index 0000000..ee324f3
--- /dev/null
+++ b/tests/gnu/regextype_posix_minimal_basic.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+bfs_diff -regextype posix-minimal-basic -regex '\./\((\)'
diff --git a/tests/ioq.c b/tests/ioq.c
index ef5ee3b..99c98a2 100644
--- a/tests/ioq.c
+++ b/tests/ioq.c
@@ -40,15 +40,15 @@ static void check_ioq_push_block(void) {
const size_t depth = 2;
struct ioq *ioq = ioq_create(depth, 1);
- bfs_verify(ioq, "ioq_create(): %s", xstrerror(errno));
+ bfs_everify(ioq, "ioq_create()");
// Push enough operations to fill the queue
for (size_t i = 0; i < depth; ++i) {
struct bfs_dir *dir = bfs_allocdir();
- bfs_verify(dir, "bfs_allocdir(): %s", xstrerror(errno));
+ bfs_everify(dir, "bfs_allocdir()");
int ret = ioq_opendir(ioq, dir, AT_FDCWD, ".", 0, NULL);
- bfs_verify(ret == 0, "ioq_opendir(): %s", xstrerror(errno));
+ bfs_everify(ret == 0, "ioq_opendir()");
}
bfs_verify(ioq_capacity(ioq) == 0);
diff --git a/tests/main.c b/tests/main.c
index 429772b..aef0583 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -9,7 +9,6 @@
#include "tests.h"
#include "bfstd.h"
#include "color.h"
-#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
@@ -90,10 +89,6 @@ static void run_test(struct test_ctx *ctx, const char *test, test_fn *fn) {
}
}
-const char *bfs_errstr(void) {
- return xstrerror(errno);
-}
-
int main(int argc, char *argv[]) {
// Try to set a UTF-8 locale
if (!setlocale(LC_ALL, "C.UTF-8")) {
@@ -116,6 +111,7 @@ int main(int argc, char *argv[]) {
run_test(&ctx, "bfstd", check_bfstd);
run_test(&ctx, "bit", check_bit);
run_test(&ctx, "ioq", check_ioq);
+ run_test(&ctx, "sighook", check_sighook);
run_test(&ctx, "trie", check_trie);
run_test(&ctx, "xspawn", check_xspawn);
run_test(&ctx, "xtime", check_xtime);
diff --git a/tests/posix/prune_error.out b/tests/posix/prune_error.out
new file mode 100644
index 0000000..9c558e3
--- /dev/null
+++ b/tests/posix/prune_error.out
@@ -0,0 +1 @@
+.
diff --git a/tests/posix/prune_error.sh b/tests/posix/prune_error.sh
new file mode 100644
index 0000000..9158a17
--- /dev/null
+++ b/tests/posix/prune_error.sh
@@ -0,0 +1,8 @@
+cd "$TEST"
+"$XTOUCH" -p foo/bar
+ln -s foo/bar baz
+
+chmod a-rx foo
+defer chmod +rx foo
+
+! bfs_diff -L . -name '???' -prune -o -print
diff --git a/tests/run.sh b/tests/run.sh
index ad9c0be..8c1402d 100644
--- a/tests/run.sh
+++ b/tests/run.sh
@@ -5,23 +5,6 @@
## Running test cases
-# Beginning/end of line escape sequences
-BOL=$'\n'
-EOL=$'\n'
-
-# Update $EOL for the terminal size
-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 2>/dev/tty)
- fi
-
- # Put the cursor at the last column, then write a space so the next
- # character will wrap
- EOL=$'\e['"${cols}G "
-}
-
# ERR trap for tests
debug_err() {
local ret=$? line func file
@@ -64,19 +47,19 @@ run_test() {
case $ret in
0)
if ((VERBOSE_TESTS)); then
- color printf "${BOL}${GRN}[PASS]${RST} ${BLD}%s${RST}\n" "$TEST"
+ color printf "${GRN}[PASS]${RST} ${BLD}%s${RST}\n" "$TEST"
fi
;;
$EX_SKIP)
if ((VERBOSE_SKIPPED || VERBOSE_TESTS)); then
- color printf "${BOL}${CYN}[SKIP]${RST} ${BLD}%s${RST}\n" "$TEST"
+ color printf "${CYN}[SKIP]${RST} ${BLD}%s${RST}\n" "$TEST"
fi
;;
*)
if ((!VERBOSE_ERRORS)); then
cat "$TMP/$TEST.err" >&2
fi
- color printf "${BOL}${RED}[FAIL]${RST} ${BLD}%s${RST}\n" "$TEST"
+ color printf "${RED}[FAIL]${RST} ${BLD}%s${RST}\n" "$TEST"
;;
esac
@@ -112,12 +95,21 @@ reap_test() {
# Wait for a background test to finish
wait_test() {
local pid
- wait -n -ppid
- ret=$?
- if [ -z "${pid:-}" ]; then
- debug "${BASH_SOURCE[0]}" $((LINENO - 3)) "${RED}error $ret${RST}" >&$DUPERR
- exit 1
- fi
+
+ while true; do
+ wait -n -ppid
+ ret=$?
+
+ if [ "${pid:-}" ]; then
+ break
+ elif ((ret > 128)); then
+ # Interrupted by signal
+ continue
+ else
+ debug "${BASH_SOURCE[0]}" $((LINENO - 3)) "${RED}error $ret${RST}" >&$DUPERR
+ exit 1
+ fi
+ done
reap_test $ret
}
@@ -164,35 +156,24 @@ comake() {
exec {READY_PIPE}<&${COPROC[0]} {DONE_PIPE}>&${COPROC[1]}
}
-# Run all the tests
-run_tests() {
- if ((VERBOSE_TESTS)); then
- BOL=''
- elif ((COLOR_STDOUT)); then
- # Carriage return + clear line
- BOL=$'\r\e[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
+# Print the current test progess
+progress() {
+ if [ "${BAR:-}" ]; then
+ print_bar "$(printf "$@")"
+ elif ((VERBOSE_TESTS)); then
+ color printf "$@"
fi
+}
+# Run all the tests
+run_tests() {
passed=0
failed=0
skipped=0
ran=0
total=${#TEST_CASES[@]}
- if ((COLOR_STDOUT || VERBOSE_TESTS)); then
- TEST_FMT="${BOL}${YLW}[%3d%%]${RST} ${BLD}%s${RST}${EOL}"
- else
- TEST_FMT="."
- fi
+ TEST_FMT="${YLW}[%3d%%]${RST} ${BLD}%s${RST}\\n"
if ((${#MAKE[@]})); then
comake
@@ -201,6 +182,10 @@ run_tests() {
# Turn off set -e (but turn it back on in run_test)
set +e
+ if ((COLOR_STDOUT && !VERBOSE_TESTS)); then
+ show_bar
+ fi
+
for TEST in "${TEST_CASES[@]}"; do
wait_ready
if ((STOP && failed > 0)); then
@@ -208,7 +193,7 @@ run_tests() {
fi
percent=$((100 * ran / total))
- color printf "$TEST_FMT" $percent "$TEST"
+ progress "${YLW}[%3d%%]${RST} ${BLD}%s${RST}\\n" $percent "$TEST"
mkdir -p "$TMP/$TEST"
OUT="$TMP/$TEST.out"
@@ -221,7 +206,9 @@ run_tests() {
wait_test
done
- printf "${BOL}"
+ if [ "${BAR:-}" ]; then
+ hide_bar
+ fi
if ((passed > 0)); then
color printf "${GRN}[PASS]${RST} ${BLD}%3d${RST} / ${BLD}%d${RST}\n" $passed $total
@@ -253,7 +240,6 @@ skip() {
if ((VERBOSE_SKIPPED)); then
caller | {
read -r line file
- printf "${BOL}"
debug "$file" $line "" >&$DUPOUT
}
fi
@@ -430,8 +416,8 @@ make_xattrs() {
EX_DIFF=20
# Detect colored diff support
-if diff --color /dev/null /dev/null &>/dev/null; then
- DIFF="diff --color"
+if ((COLOR_STDERR)) && diff --color=always /dev/null /dev/null &>/dev/null; then
+ DIFF="diff --color=always"
else
DIFF="diff"
fi
diff --git a/tests/sighook.c b/tests/sighook.c
new file mode 100644
index 0000000..c94526e
--- /dev/null
+++ b/tests/sighook.c
@@ -0,0 +1,97 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include "prelude.h"
+#include "tests.h"
+#include "sighook.h"
+#include "atomic.h"
+#include "thread.h"
+#include <pthread.h>
+#include <signal.h>
+#include <sys/time.h>
+
+static atomic size_t count = 0;
+
+/** SIGALRM handler. */
+static void alrm_hook(int sig, siginfo_t *info, void *arg) {
+ fetch_add(&count, 1, relaxed);
+}
+
+/** Swap out an old hook for a new hook. */
+static int swap_hooks(struct sighook **hook) {
+ struct sighook *next = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
+ if (!bfs_echeck(next, "sighook(SIGALRM)")) {
+ return -1;
+ }
+
+ sigunhook(*hook);
+ *hook = next;
+ return 0;
+}
+
+/** Background thread that rapidly (un)registers signal hooks. */
+static void *hook_thread(void *ptr) {
+ struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
+ if (!bfs_echeck(hook, "sighook(SIGALRM)")) {
+ return NULL;
+ }
+
+ while (load(&count, relaxed) < 1000) {
+ if (swap_hooks(&hook) != 0) {
+ sigunhook(hook);
+ return NULL;
+ }
+ }
+
+ sigunhook(hook);
+ return &count;
+}
+
+bool check_sighook(void) {
+ bool ret = true;
+
+ struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
+ ret &= bfs_echeck(hook, "sighook(SIGALRM)");
+ if (!ret) {
+ goto done;
+ }
+
+ struct itimerval ival = {
+ .it_value = {
+ .tv_usec = 100,
+ },
+ .it_interval = {
+ .tv_usec = 100,
+ },
+ };
+ ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0);
+ if (!ret) {
+ goto unhook;
+ }
+
+ pthread_t thread;
+ ret &= bfs_echeck(thread_create(&thread, NULL, hook_thread, NULL) == 0);
+ if (!ret) {
+ goto untime;
+ }
+
+ while (ret && load(&count, relaxed) < 1000) {
+ ret &= swap_hooks(&hook) == 0;
+ }
+
+ void *ptr;
+ thread_join(thread, &ptr);
+ ret &= bfs_check(ptr);
+
+untime:
+ ival.it_value.tv_usec = 0;
+ ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0);
+ if (!ret) {
+ goto unhook;
+ }
+
+unhook:
+ sigunhook(hook);
+done:
+ return ret;
+}
diff --git a/tests/stddirs.sh b/tests/stddirs.sh
index e08e6bf..8f8440d 100644
--- a/tests/stddirs.sh
+++ b/tests/stddirs.sh
@@ -71,6 +71,9 @@ make_weirdnames() {
"$XTOUCH" -p "$1/\\/i"
"$XTOUCH" -p "$1/ /j"
"$XTOUCH" -p "$1/[/k"
+ "$XTOUCH" -p "$1/{/l"
+ "$XTOUCH" -p "$1/*/m"
+ "$XTOUCH" -p "$1/"$'\n/n'
}
# Creates a very deep directory structure for testing PATH_MAX handling
diff --git a/tests/tests.h b/tests/tests.h
index 9078938..de95a49 100644
--- a/tests/tests.h
+++ b/tests/tests.h
@@ -9,6 +9,7 @@
#define BFS_TESTS_H
#include "prelude.h"
+#include "bfstd.h"
#include "diag.h"
/** Unit test function type. */
@@ -26,6 +27,9 @@ bool check_bit(void);
/** I/O queue tests. */
bool check_ioq(void);
+/** Signal hook tests. */
+bool check_sighook(void);
+
/** Trie tests. */
bool check_trie(void);
@@ -54,20 +58,17 @@ static inline bool bfs_check(bool ret) {
: "Check failed: `%s`%s", \
str, __VA_ARGS__), false))
-/** Get a string description of the last error. */
-const char *bfs_errstr(void);
-
/**
* Check a condition, logging the current error string on failure.
*/
-#define bfs_pcheck(...) \
- bfs_pcheck_(#__VA_ARGS__, __VA_ARGS__, "", "")
+#define bfs_echeck(...) \
+ bfs_echeck_(#__VA_ARGS__, __VA_ARGS__, "", errstr())
-#define bfs_pcheck_(str, cond, format, ...) \
+#define bfs_echeck_(str, cond, format, ...) \
((cond) ? true : (bfs_diag( \
sizeof(format) > 1 \
- ? "%.0s" format "%s%s: %s" \
- : "Check failed: `%s`%s: %s", \
- str, __VA_ARGS__, bfs_errstr()), false))
+ ? "%.0s" format "%s: %s" \
+ : "Check failed: `%s`: %s", \
+ str, __VA_ARGS__), false))
#endif // BFS_TESTS_H
diff --git a/tests/util.sh b/tests/util.sh
index 3969db5..76b72b9 100644
--- a/tests/util.sh
+++ b/tests/util.sh
@@ -59,6 +59,15 @@ stdenv() {
# Close stdin so bfs doesn't think we're interactive
# dup() the standard fds for logging even when redirected
exec </dev/null {DUPOUT}>&1 {DUPERR}>&2
+
+ # Get the ttyname
+ if [ -t $DUPOUT ]; then
+ TTY=$(tty <&$DUPOUT)
+ elif [ -t $DUPERR ]; then
+ TTY=$(tty <&$DUPERR)
+ else
+ TTY=
+ fi
}
# Drop root priviliges or bail
diff --git a/tests/xspawn.c b/tests/xspawn.c
index 785ea48..b1d6dc1 100644
--- a/tests/xspawn.c
+++ b/tests/xspawn.c
@@ -54,7 +54,7 @@ static bool check_use_path(bool use_posix) {
bool ret = true;
struct bfs_spawn spawn;
- ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0);
+ ret &= bfs_echeck(bfs_spawn_init(&spawn) == 0);
if (!ret) {
goto out;
}
@@ -64,18 +64,18 @@ static bool check_use_path(bool use_posix) {
spawn.flags &= ~BFS_SPAWN_USE_POSIX;
}
- ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, "bin", O_RDONLY | O_DIRECTORY, 0) == 0);
- ret &= bfs_pcheck(bfs_spawn_adddup2(&spawn, 10, 11) == 0);
- ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0);
- ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 11) == 0);
- ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 11) == 0);
+ ret &= bfs_echeck(bfs_spawn_addopen(&spawn, 10, "bin", O_RDONLY | O_DIRECTORY, 0) == 0);
+ ret &= bfs_echeck(bfs_spawn_adddup2(&spawn, 10, 11) == 0);
+ ret &= bfs_echeck(bfs_spawn_addclose(&spawn, 10) == 0);
+ ret &= bfs_echeck(bfs_spawn_addfchdir(&spawn, 11) == 0);
+ ret &= bfs_echeck(bfs_spawn_addclose(&spawn, 11) == 0);
if (!ret) {
goto destroy;
}
// Check that $PATH is resolved in the parent's environment
char **envp;
- ret &= bfs_pcheck(envp = envdup());
+ ret &= bfs_echeck(envp = envdup());
if (!ret) {
goto destroy;
}
@@ -84,7 +84,7 @@ static bool check_use_path(bool use_posix) {
char *old_path = getenv("PATH");
dchar *new_path = NULL;
if (old_path) {
- ret &= bfs_pcheck(old_path = strdup(old_path));
+ ret &= bfs_echeck(old_path = strdup(old_path));
if (!ret) {
goto env;
}
@@ -97,20 +97,20 @@ static bool check_use_path(bool use_posix) {
goto path;
}
- ret &= bfs_pcheck(setenv("PATH", new_path, true) == 0);
+ ret &= bfs_echeck(setenv("PATH", new_path, true) == 0);
if (!ret) {
goto path;
}
char *argv[] = {"xspawnee", old_path, NULL};
pid_t pid = bfs_spawn("xspawnee", &spawn, argv, envp);
- ret &= bfs_pcheck(pid >= 0, "bfs_spawn()");
+ ret &= bfs_echeck(pid >= 0, "bfs_spawn()");
if (!ret) {
goto unset;
}
int wstatus;
- ret &= bfs_pcheck(xwaitpid(pid, &wstatus, 0) == pid)
+ ret &= bfs_echeck(xwaitpid(pid, &wstatus, 0) == pid)
&& bfs_check(WIFEXITED(wstatus));
if (ret) {
int wexit = WEXITSTATUS(wstatus);
@@ -119,9 +119,9 @@ static bool check_use_path(bool use_posix) {
unset:
if (old_path) {
- ret &= bfs_pcheck(setenv("PATH", old_path, true) == 0);
+ ret &= bfs_echeck(setenv("PATH", old_path, true) == 0);
} else {
- ret &= bfs_pcheck(unsetenv("PATH") == 0);
+ ret &= bfs_echeck(unsetenv("PATH") == 0);
}
path:
dstrfree(new_path);
@@ -132,7 +132,7 @@ env:
}
free(envp);
destroy:
- ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0);
+ ret &= bfs_echeck(bfs_spawn_destroy(&spawn) == 0);
out:
return ret;
}
@@ -142,7 +142,7 @@ static bool check_enoent(bool use_posix) {
bool ret = true;
struct bfs_spawn spawn;
- ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0);
+ ret &= bfs_echeck(bfs_spawn_init(&spawn) == 0);
if (!ret) {
goto out;
}
@@ -154,9 +154,9 @@ static bool check_enoent(bool use_posix) {
char *argv[] = {"eW6f5RM9Qi", NULL};
pid_t pid = bfs_spawn("eW6f5RM9Qi", &spawn, argv, NULL);
- ret &= bfs_pcheck(pid < 0 && errno == ENOENT, "bfs_spawn()");
+ ret &= bfs_echeck(pid < 0 && errno == ENOENT, "bfs_spawn()");
- ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0);
+ ret &= bfs_echeck(bfs_spawn_destroy(&spawn) == 0);
out:
return ret;
}
@@ -166,18 +166,18 @@ static bool check_resolve(void) {
char *exe;
exe = bfs_spawn_resolve("sh");
- ret &= bfs_pcheck(exe, "bfs_spawn_resolve('sh')");
+ ret &= bfs_echeck(exe, "bfs_spawn_resolve('sh')");
free(exe);
exe = bfs_spawn_resolve("/bin/sh");
- ret &= bfs_pcheck(exe && strcmp(exe, "/bin/sh") == 0);
+ ret &= bfs_echeck(exe && strcmp(exe, "/bin/sh") == 0);
free(exe);
exe = bfs_spawn_resolve("bin/tests/xspawnee");
- ret &= bfs_pcheck(exe && strcmp(exe, "bin/tests/xspawnee") == 0);
+ ret &= bfs_echeck(exe && strcmp(exe, "bin/tests/xspawnee") == 0);
free(exe);
- ret &= bfs_pcheck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT);
+ ret &= bfs_echeck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT);
return ret;
}
diff --git a/tests/xtime.c b/tests/xtime.c
index a7c63d2..1907e26 100644
--- a/tests/xtime.c
+++ b/tests/xtime.c
@@ -29,9 +29,9 @@ static bool check_one_xgetdate(const char *str, int error, time_t expected) {
int ret = xgetdate(str, &ts);
if (error) {
- return bfs_pcheck(ret == -1 && errno == error, "xgetdate('%s')", str);
+ return bfs_echeck(ret == -1 && errno == error, "xgetdate('%s')", str);
} else {
- return bfs_pcheck(ret == 0, "xgetdate('%s')", str)
+ return bfs_echeck(ret == 0, "xgetdate('%s')", str)
&& bfs_check(ts.tv_sec == expected && ts.tv_nsec == 0,
"xgetdate('%s'): %jd.%09jd != %jd",
str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected);
@@ -82,12 +82,12 @@ static bool check_xgetdate(void) {
static bool check_one_xmktime(time_t expected) {
struct tm tm;
if (!localtime_r(&expected, &tm)) {
- bfs_diag("localtime_r(%jd): %s", (intmax_t)expected, xstrerror(errno));
+ bfs_ediag("localtime_r(%jd)", (intmax_t)expected);
return false;
}
time_t actual;
- return bfs_pcheck(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT ")", TM_PRINTF(tm))
+ return bfs_echeck(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT ")", TM_PRINTF(tm))
&& bfs_check(actual == expected, "xmktime(" TM_FORMAT "): %jd != %jd", TM_PRINTF(tm), (intmax_t)actual, (intmax_t)expected);
}
@@ -137,6 +137,7 @@ static bool check_one_xtimegm(const struct tm *tm) {
return ret;
}
+#if !BFS_HAS_TIMEGM
/** Check an overflowing xtimegm() call. */
static bool check_xtimegm_overflow(const struct tm *tm) {
struct tm copy = *tm;
@@ -154,6 +155,7 @@ static bool check_xtimegm_overflow(const struct tm *tm) {
return ret;
}
+#endif
/** xtimegm() tests. */
static bool check_xtimegm(void) {
@@ -173,11 +175,13 @@ static bool check_xtimegm(void) {
ret &= check_one_xtimegm(&tm);
}
+#if !BFS_HAS_TIMEGM
// Check integer overflow cases
- check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX });
- check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX });
- check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX });
- check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX });
+ ret &= check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX });
+#endif
return ret;
}
diff --git a/tests/xtouch.c b/tests/xtouch.c
index cd41842..427e3e0 100644
--- a/tests/xtouch.c
+++ b/tests/xtouch.c
@@ -120,7 +120,7 @@ static int at_flags(const struct args *args) {
/** Touch one path. */
static int xtouch(const struct args *args, const char *path) {
int dfd = open_parent(args, &path);
- if (dfd < 0 && dfd != AT_FDCWD) {
+ if (dfd < 0 && dfd != (int)AT_FDCWD) {
return -1;
}