diff options
Diffstat (limited to 'tests')
307 files changed, 3632 insertions, 1590 deletions
diff --git a/tests/alloc.c b/tests/alloc.c new file mode 100644 index 0000000..6c0defd --- /dev/null +++ b/tests/alloc.c @@ -0,0 +1,52 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "alloc.h" +#include "diag.h" +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> + +bool check_alloc(void) { + bool ret = true; + + // Check sizeof_flex() + struct flexible { + alignas(64) int foo[8]; + int bar[]; + }; + ret &= bfs_check(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); + ret &= bfs_check(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); + + size_t too_many = SIZE_MAX / sizeof(int) + 1; + ret &= bfs_check(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); + + // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) + // Doesn't happen in typical ABIs + ret &= bfs_check(flex_size(8, 16, 4, 4, 1) == 16); + + // Make sure we detect allocation size overflows +#if __GNUC__ && !__clang__ +# pragma GCC diagnostic ignored "-Walloc-size-larger-than=" +#endif + + ret &= bfs_check(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + + // varena tests + struct varena varena; + VARENA_INIT(&varena, struct flexible, bar); + + for (size_t i = 0; i < 256; ++i) { + bfs_verify(varena_alloc(&varena, i)); + struct arena *arena = &varena.arenas[varena.narenas - 1]; + ret &= bfs_check(arena->size >= sizeof_flex(struct flexible, bar, i)); + } + + varena_destroy(&varena); + return ret; +} diff --git a/tests/bfs/D_incomplete.sh b/tests/bfs/D_incomplete.sh index 396d365..30c522a 100644 --- a/tests/bfs/D_incomplete.sh +++ b/tests/bfs/D_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -D +! invoke_bfs -D diff --git a/tests/bfs/D_opt.out b/tests/bfs/D_opt.out new file mode 100644 index 0000000..6218a0c --- /dev/null +++ b/tests/bfs/D_opt.out @@ -0,0 +1,7 @@ +basic/a +basic/b +basic/c/d +basic/e/f +basic/j/foo +basic/k/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/D_opt.sh b/tests/bfs/D_opt.sh new file mode 100644 index 0000000..c14fe70 --- /dev/null +++ b/tests/bfs/D_opt.sh @@ -0,0 +1 @@ +bfs_diff -D opt -nohidden -not \( -type c -o -type d \) -links -5 -links -10 -not -hidden basic diff --git a/tests/bfs/D_unknown.sh b/tests/bfs/D_unknown.sh index e3614ba..cac9bd9 100644 --- a/tests/bfs/D_unknown.sh +++ b/tests/bfs/D_unknown.sh @@ -1,4 +1,4 @@ stderr=$(invoke_bfs -warn -D unknown basic 2>&1 >"$OUT") -[ -n "$stderr" ] || return 1 +[ -n "$stderr" ] sort_output diff_output diff --git a/tests/bfs/L_capable.out b/tests/bfs/L_capable.out index e5ba3c7..0810d4a 100644 --- a/tests/bfs/L_capable.out +++ b/tests/bfs/L_capable.out @@ -1,2 +1,2 @@ -scratch/capable -scratch/link +./capable +./link diff --git a/tests/bfs/L_capable.sh b/tests/bfs/L_capable.sh index 533ac2f..97c404f 100644 --- a/tests/bfs/L_capable.sh +++ b/tests/bfs/L_capable.sh @@ -1,12 +1,10 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$UNAME" = "Linux" || skip +invoke_bfs . -quit -capable || skip -clean_scratch +cd "$TEST" -skip_unless invoke_bfs scratch -quit -capable +"$XTOUCH" normal capable +bfs_sudo setcap all+ep capable || skip +ln -s capable link -"$XTOUCH" scratch/{normal,capable} -sudo setcap all+ep scratch/capable -ln -s capable scratch/link - -bfs_diff -L scratch -capable +bfs_diff -L . -capable diff --git a/tests/bfs/O9.sh b/tests/bfs/O9.sh index 12f6c2d..c12a7a3 100644 --- a/tests/bfs/O9.sh +++ b/tests/bfs/O9.sh @@ -1,4 +1,4 @@ stderr=$(invoke_bfs -warn -O9 basic 2>&1 >"$OUT") -[ -n "$stderr" ] || return 1 +[ -n "$stderr" ] sort_output diff_output diff --git a/tests/bfs/and_incomplete.sh b/tests/bfs/and_incomplete.sh index f7bc2c3..05abc2d 100644 --- a/tests/bfs/and_incomplete.sh +++ b/tests/bfs/and_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -print -a +! invoke_bfs -print -a diff --git a/tests/bfs/capable.out b/tests/bfs/capable.out index 78b5bd9..ac7b5ce 100644 --- a/tests/bfs/capable.out +++ b/tests/bfs/capable.out @@ -1 +1 @@ -scratch/capable +./capable diff --git a/tests/bfs/capable.sh b/tests/bfs/capable.sh index 256b9bc..35bb0b4 100644 --- a/tests/bfs/capable.sh +++ b/tests/bfs/capable.sh @@ -1,12 +1,10 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$UNAME" = "Linux" || skip +invoke_bfs . -quit -capable || skip -clean_scratch +cd "$TEST" -skip_unless invoke_bfs scratch -quit -capable +"$XTOUCH" normal capable +bfs_sudo setcap all+ep capable || skip +ln -s capable link -"$XTOUCH" scratch/{normal,capable} -sudo setcap all+ep scratch/capable -ln -s capable scratch/link - -bfs_diff scratch -capable +bfs_diff . -capable diff --git a/tests/bfs/closed_stderr.sh b/tests/bfs/closed_stderr.sh new file mode 100644 index 0000000..26abd85 --- /dev/null +++ b/tests/bfs/closed_stderr.sh @@ -0,0 +1,4 @@ +# Check if the platform automatically re-opens stderr before we can +(bash -c 'echo >&2' 2>&-) && skip + +! invoke_bfs basic >&- 2>&- diff --git a/tests/posix/closed_stdin.out b/tests/bfs/closed_stdin.out index a7ccfe4..a7ccfe4 100644 --- a/tests/posix/closed_stdin.out +++ b/tests/bfs/closed_stdin.out diff --git a/tests/posix/closed_stdin.sh b/tests/bfs/closed_stdin.sh index 6932be8..6932be8 100644 --- a/tests/posix/closed_stdin.sh +++ b/tests/bfs/closed_stdin.sh diff --git a/tests/bfs/closed_stdout.sh b/tests/bfs/closed_stdout.sh new file mode 100644 index 0000000..5b6f7c3 --- /dev/null +++ b/tests/bfs/closed_stdout.sh @@ -0,0 +1,4 @@ +# Check if the platform automatically re-opens stdout before we can +(bash -c echo >&-) && skip + +! invoke_bfs basic >&- diff --git a/tests/bfs/color.out b/tests/bfs/color.out index 77fc8a8..a439814 100644 --- a/tests/bfs/color.out +++ b/tests/bfs/color.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_L.out b/tests/bfs/color_L.out index b60dd4a..85923db 100644 --- a/tests/bfs/color_L.out +++ b/tests/bfs/color_L.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;33mchardev_link[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt [01;34mrainbow/[0mlink.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_L_ln_target.out b/tests/bfs/color_L_ln_target.out index cd4ec5e..23fe8d7 100644 --- a/tests/bfs/color_L_ln_target.out +++ b/tests/bfs/color_L_ln_target.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;31mbroken[0m [01;34mrainbow/[0m[01;32mexec.sh[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt [01;34mrainbow/[0mlink.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_L_no_stat.out b/tests/bfs/color_L_no_stat.out index c0bb1be..72e0319 100644 --- a/tests/bfs/color_L_no_stat.out +++ b/tests/bfs/color_L_no_stat.out @@ -1,8 +1,7 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;33mchardev_link[0m -[01;34mrainbow/[0m[01;34mow[0m -[01;34mrainbow/[0m[01;34msticky[0m -[01;34mrainbow/[0m[01;34msticky_ow[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01mfile.txt[0m @@ -10,11 +9,19 @@ [01;34mrainbow/[0m[33mpipe[0m [01;34mrainbow/[0mexec.sh [01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 [01;34mrainbow/[0msgid -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz [01;34mrainbow/[0msugid [01;34mrainbow/[0msuid +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/ow[0m +[01;34mrainbow/sticky[0m +[01;34mrainbow/sticky_ow[0m diff --git a/tests/bfs/color_star.out b/tests/bfs/color_auto.out index 77fc8a8..a439814 100644 --- a/tests/bfs/color_star.out +++ b/tests/bfs/color_auto.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_auto.sh b/tests/bfs/color_auto.sh new file mode 100644 index 0000000..7e875cc --- /dev/null +++ b/tests/bfs/color_auto.sh @@ -0,0 +1,4 @@ +unset NO_COLOR +bfs_pty rainbow >"$OUT" +sort_output +diff_output diff --git a/tests/bfs/color_cd0_no.out b/tests/bfs/color_cd0_no.out new file mode 100644 index 0000000..37b3fbc --- /dev/null +++ b/tests/bfs/color_cd0_no.out @@ -0,0 +1,27 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m[01;92m$'\e[0m'[0m +[01;34mrainbow[0m +[01;34mrainbow/[0m[01;32mexec.sh[0m +[01;34mrainbow/[0m[01;35msocket[0m +[01;34mrainbow/[0m[01;92mbroken[0m +[01;34mrainbow/[0m[01;92mfile.dat[0m +[01;34mrainbow/[0m[01;92mfile.txt[0m +[01;34mrainbow/[0m[01;92mlink.txt[0m +[01;34mrainbow/[0m[01;92mlower.gz[0m +[01;34mrainbow/[0m[01;92mlower.tar[0m +[01;34mrainbow/[0m[01;92mlower.tar.gz[0m +[01;34mrainbow/[0m[01;92mlu.tar.GZ[0m +[01;34mrainbow/[0m[01;92mmh1[0m +[01;34mrainbow/[0m[01;92mmh2[0m +[01;34mrainbow/[0m[01;92mul.TAR.gz[0m +[01;34mrainbow/[0m[01;92mupper.GZ[0m +[01;34mrainbow/[0m[01;92mupper.TAR[0m +[01;34mrainbow/[0m[01;92mupper.TAR.GZ[0m +[01;34mrainbow/[0m[30;42msticky_ow[0m +[01;34mrainbow/[0m[30;43msgid[0m +[01;34mrainbow/[0m[33mpipe[0m +[01;34mrainbow/[0m[34;42mow[0m +[01;34mrainbow/[0m[37;41msugid[0m +[01;34mrainbow/[0m[37;41msuid[0m +[01;34mrainbow/[0m[37;44msticky[0m +[01;34mrainbow/[0mchardev_link diff --git a/tests/bfs/color_cd0_no.sh b/tests/bfs/color_cd0_no.sh new file mode 100644 index 0000000..325a782 --- /dev/null +++ b/tests/bfs/color_cd0_no.sh @@ -0,0 +1 @@ +LS_COLORS="ln=target:cd=0:no=01;92:" bfs_diff rainbow -color diff --git a/tests/bfs/color_deep.out b/tests/bfs/color_deep.out new file mode 100644 index 0000000..fb990d5 --- /dev/null +++ b/tests/bfs/color_deep.out @@ -0,0 +1,16 @@ +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m +[01m0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE[0m diff --git a/tests/bfs/color_deep.sh b/tests/bfs/color_deep.sh new file mode 100644 index 0000000..a83ee0e --- /dev/null +++ b/tests/bfs/color_deep.sh @@ -0,0 +1,7 @@ +name="0123456789ABCDEF" +name="${name}${name}${name}${name}" +name="${name}${name}${name}${name}" +name="${name:0:255}" +export LS_COLORS="*${name}=01:" + +bfs_diff deep -color -type f -printf '%f\n' diff --git a/tests/bfs/color_escapes.out b/tests/bfs/color_escapes.out index b71e138..0bf9fbb 100644 --- a/tests/bfs/color_escapes.out +++ b/tests/bfs/color_escapes.out @@ -1,3 +1,5 @@ +[01;34m:$'rainbow/\e[1m'[m +[01;34m:$'rainbow/\e[1m/'[m$'\e[0m' [01;34m:rainbow[m [01;34m:rainbow/[m[01;32m:exec.sh[m [01;34m:rainbow/[m[01;35m:socket[m @@ -13,8 +15,13 @@ [01;34m:rainbow/[m[37;44m:sticky[m [01;34m:rainbow/[mfile.dat [01;34m:rainbow/[mfile.txt +[01;34m:rainbow/[mlower.gz +[01;34m:rainbow/[mlower.tar +[01;34m:rainbow/[mlower.tar.gz +[01;34m:rainbow/[mlu.tar.GZ [01;34m:rainbow/[mmh1 [01;34m:rainbow/[mmh2 -[01;34m:rainbow/[mstar.gz -[01;34m:rainbow/[mstar.tar -[01;34m:rainbow/[mstar.tar.gz +[01;34m:rainbow/[mul.TAR.gz +[01;34m:rainbow/[mupper.GZ +[01;34m:rainbow/[mupper.TAR +[01;34m:rainbow/[mupper.TAR.GZ diff --git a/tests/bfs/color_ext.out b/tests/bfs/color_ext.out index cf26e73..218100f 100644 --- a/tests/bfs/color_ext.out +++ b/tests/bfs/color_ext.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;41msuid[0m [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_ext0.out b/tests/bfs/color_ext0.out index e764a6b..d2a7fd5 100644 --- a/tests/bfs/color_ext0.out +++ b/tests/bfs/color_ext0.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[00mfile.txt[0m [01;34mrainbow/[0m[01;32mexec.sh[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;41msuid[0m [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_ext_case.out b/tests/bfs/color_ext_case.out new file mode 100644 index 0000000..93dc8f6 --- /dev/null +++ b/tests/bfs/color_ext_case.out @@ -0,0 +1,27 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' +[01;34mrainbow[0m +[01;34mrainbow/[0m[01;31mlower.gz[0m +[01;34mrainbow/[0m[01;31mlower.tar.gz[0m +[01;34mrainbow/[0m[01;32mexec.sh[0m +[01;34mrainbow/[0m[01;32mupper.GZ[0m +[01;34mrainbow/[0m[01;32mupper.TAR.GZ[0m +[01;34mrainbow/[0m[01;33mlower.tar[0m +[01;34mrainbow/[0m[01;33mupper.TAR[0m +[01;34mrainbow/[0m[01;34mul.TAR.gz[0m +[01;34mrainbow/[0m[01;35mlu.tar.GZ[0m +[01;34mrainbow/[0m[01;35msocket[0m +[01;34mrainbow/[0m[01;36mbroken[0m +[01;34mrainbow/[0m[01;36mchardev_link[0m +[01;34mrainbow/[0m[01;36mlink.txt[0m +[01;34mrainbow/[0m[30;42msticky_ow[0m +[01;34mrainbow/[0m[30;43msgid[0m +[01;34mrainbow/[0m[33mpipe[0m +[01;34mrainbow/[0m[34;42mow[0m +[01;34mrainbow/[0m[36mfile.txt[0m +[01;34mrainbow/[0m[37;41msugid[0m +[01;34mrainbow/[0m[37;41msuid[0m +[01;34mrainbow/[0m[37;44msticky[0m +[01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mmh1 +[01;34mrainbow/[0mmh2 diff --git a/tests/bfs/color_ext_case.sh b/tests/bfs/color_ext_case.sh new file mode 100644 index 0000000..4adba69 --- /dev/null +++ b/tests/bfs/color_ext_case.sh @@ -0,0 +1,6 @@ +# *.gz=01;31:*.GZ=01;32 -- case sensitive +# *.tAr=01;33:*.TaR=01;33 -- case-insensitive +# *.TAR.gz=01;34:*.tar.GZ=01;35 -- case-sensitive +# *.txt=35:*TXT=36 -- case-insensitive +export LS_COLORS="*.gz=01;31:*.GZ=01;32:*.tAr=01;33:*.TaR=01;33:*.TAR.gz=01;34:*.tar.GZ=01;35:*.txt=35:*TXT=36" +bfs_diff rainbow -color diff --git a/tests/bfs/color_ext_override.out b/tests/bfs/color_ext_override.out index 1377b65..0acfcbc 100644 --- a/tests/bfs/color_ext_override.out +++ b/tests/bfs/color_ext_override.out @@ -1,8 +1,15 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;32mstar.tar[0m -[01;34mrainbow/[0m[01;33mstar.gz[0m -[01;34mrainbow/[0m[01;33mstar.tar.gz[0m +[01;34mrainbow/[0m[01;32mlower.tar[0m +[01;34mrainbow/[0m[01;32mupper.TAR[0m +[01;34mrainbow/[0m[01;33mlower.gz[0m +[01;34mrainbow/[0m[01;33mlower.tar.gz[0m +[01;34mrainbow/[0m[01;33mlu.tar.GZ[0m +[01;34mrainbow/[0m[01;33mul.TAR.gz[0m +[01;34mrainbow/[0m[01;33mupper.GZ[0m +[01;34mrainbow/[0m[01;33mupper.TAR.GZ[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m diff --git a/tests/bfs/color_ext_underride.out b/tests/bfs/color_ext_underride.out index 787248a..5c98341 100644 --- a/tests/bfs/color_ext_underride.out +++ b/tests/bfs/color_ext_underride.out @@ -1,8 +1,15 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m -[01;34mrainbow/[0m[01;31mstar.tar.gz[0m +[01;34mrainbow/[0m[01;31mlower.tar.gz[0m +[01;34mrainbow/[0m[01;31mlu.tar.GZ[0m +[01;34mrainbow/[0m[01;31mul.TAR.gz[0m +[01;34mrainbow/[0m[01;31mupper.TAR.GZ[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;32mstar.tar[0m -[01;34mrainbow/[0m[01;33mstar.gz[0m +[01;34mrainbow/[0m[01;32mlower.tar[0m +[01;34mrainbow/[0m[01;32mupper.TAR[0m +[01;34mrainbow/[0m[01;33mlower.gz[0m +[01;34mrainbow/[0m[01;33mupper.GZ[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m diff --git a/tests/bfs/color_fi0_no.out b/tests/bfs/color_fi0_no.out new file mode 100644 index 0000000..a439814 --- /dev/null +++ b/tests/bfs/color_fi0_no.out @@ -0,0 +1,27 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' +[01;34mrainbow[0m +[01;34mrainbow/[0m[01;32mexec.sh[0m +[01;34mrainbow/[0m[01;35msocket[0m +[01;34mrainbow/[0m[01;36mbroken[0m +[01;34mrainbow/[0m[01;36mchardev_link[0m +[01;34mrainbow/[0m[01;36mlink.txt[0m +[01;34mrainbow/[0m[30;42msticky_ow[0m +[01;34mrainbow/[0m[30;43msgid[0m +[01;34mrainbow/[0m[33mpipe[0m +[01;34mrainbow/[0m[34;42mow[0m +[01;34mrainbow/[0m[37;41msugid[0m +[01;34mrainbow/[0m[37;41msuid[0m +[01;34mrainbow/[0m[37;44msticky[0m +[01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ +[01;34mrainbow/[0mmh1 +[01;34mrainbow/[0mmh2 +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_fi0_no.sh b/tests/bfs/color_fi0_no.sh new file mode 100644 index 0000000..f947d64 --- /dev/null +++ b/tests/bfs/color_fi0_no.sh @@ -0,0 +1 @@ +LS_COLORS="fi=0:no=01;92:" bfs_diff rainbow -color diff --git a/tests/bfs/color_fi_no.out b/tests/bfs/color_fi_no.out new file mode 100644 index 0000000..1c1ad8e --- /dev/null +++ b/tests/bfs/color_fi_no.out @@ -0,0 +1,27 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m[01;91m$'\e[0m'[0m +[01;34mrainbow[0m +[01;34mrainbow/[0m[01;32mexec.sh[0m +[01;34mrainbow/[0m[01;35msocket[0m +[01;34mrainbow/[0m[01;36mbroken[0m +[01;34mrainbow/[0m[01;36mchardev_link[0m +[01;34mrainbow/[0m[01;36mlink.txt[0m +[01;34mrainbow/[0m[01;91mfile.dat[0m +[01;34mrainbow/[0m[01;91mfile.txt[0m +[01;34mrainbow/[0m[01;91mlower.gz[0m +[01;34mrainbow/[0m[01;91mlower.tar[0m +[01;34mrainbow/[0m[01;91mlower.tar.gz[0m +[01;34mrainbow/[0m[01;91mlu.tar.GZ[0m +[01;34mrainbow/[0m[01;91mmh1[0m +[01;34mrainbow/[0m[01;91mmh2[0m +[01;34mrainbow/[0m[01;91mul.TAR.gz[0m +[01;34mrainbow/[0m[01;91mupper.GZ[0m +[01;34mrainbow/[0m[01;91mupper.TAR[0m +[01;34mrainbow/[0m[01;91mupper.TAR.GZ[0m +[01;34mrainbow/[0m[30;42msticky_ow[0m +[01;34mrainbow/[0m[30;43msgid[0m +[01;34mrainbow/[0m[33mpipe[0m +[01;34mrainbow/[0m[34;42mow[0m +[01;34mrainbow/[0m[37;41msugid[0m +[01;34mrainbow/[0m[37;41msuid[0m +[01;34mrainbow/[0m[37;44msticky[0m diff --git a/tests/bfs/color_fi_no.sh b/tests/bfs/color_fi_no.sh new file mode 100644 index 0000000..c2b4ec7 --- /dev/null +++ b/tests/bfs/color_fi_no.sh @@ -0,0 +1 @@ +LS_COLORS="fi=01;91:no=01;92:" bfs_diff rainbow -color diff --git a/tests/bfs/color_ln_target.out b/tests/bfs/color_ln_target.out index cd4ec5e..23fe8d7 100644 --- a/tests/bfs/color_ln_target.out +++ b/tests/bfs/color_ln_target.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;31mbroken[0m [01;34mrainbow/[0m[01;32mexec.sh[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt [01;34mrainbow/[0mlink.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_ls.out b/tests/bfs/color_ls.out index b08d894..f69eb9c 100644 --- a/tests/bfs/color_ls.out +++ b/tests/bfs/color_ls.out @@ -1,12 +1,12 @@ -[01;31mscratch/foo/[0m[01;31mbar[0m -[01;31mscratch/foo/[0m[01;31mbar[0m -[01;34m/[0m[01;31m__bfs__/[0m[01;31mnowhere[0m -[01;34m/[0m[01;31m__bfs__/[0m[01;31mnowhere[0m -[01;34mfoo/bar/[0m[01;31mbaz/[0m[01;31mqux[0m -[01;34mfoo/bar/[0m[01;31mbaz/[0m[01;31mqux[0m +[01;31mscratch/foo/bar[0m +[01;31mscratch/foo/bar[0m +[01;34m/[0m[01;31m__bfs__/nowhere[0m +[01;34m/[0m[01;31m__bfs__/nowhere[0m [01;34mfoo/bar/[0m[01;31mnowhere[0m [01;34mfoo/bar/[0m[01;31mnowhere[0m -[01;34mfoo/bar/[0m[01;31mnowhere/[0m[01;31mnothing[0m -[01;34mfoo/bar/[0m[01;31mnowhere/[0m[01;31mnothing[0m +[01;34mfoo/bar/[0m[01;31mnowhere/nothing[0m +[01;34mfoo/bar/[0m[01;31mnowhere/nothing[0m [01;34mfoo/bar/[0mbaz [01;34mfoo/bar/[0mbaz +[01;34mfoo/bar/baz[0m[01;31m//qux[0m +[01;34mfoo/bar/baz[0m[01;31m//qux[0m diff --git a/tests/bfs/color_ls.sh b/tests/bfs/color_ls.sh index 37d088f..f1cc216 100644 --- a/tests/bfs/color_ls.sh +++ b/tests/bfs/color_ls.sh @@ -1,15 +1,15 @@ -clean_scratch +cd "$TEST" "$XTOUCH" -p scratch/foo/bar/baz ln -s foo/bar/baz scratch/link ln -s foo/bar/nowhere scratch/broken ln -s foo/bar/nowhere/nothing scratch/nested -ln -s foo/bar/baz/qux scratch/notdir +ln -s foo/bar/baz//qux scratch/notdir ln -s scratch/foo/bar scratch/relative mkdir scratch/__bfs__ ln -s /__bfs__/nowhere scratch/absolute -LS_COLORS="or=01;31:" invoke_bfs scratch/{,link,broken,nested,notdir,relative,absolute} -color -type l -ls \ +export LS_COLORS="or=01;31:" +invoke_bfs scratch/{,link,broken,nested,notdir,relative,absolute} -color -type l -ls \ | sed 's/.* -> //' \ | sort >"$OUT" - diff_output diff --git a/tests/bfs/color_mh.out b/tests/bfs/color_mh.out index 757a6a1..c658082 100644 --- a/tests/bfs/color_mh.out +++ b/tests/bfs/color_mh.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -15,6 +17,11 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_mh0.out b/tests/bfs/color_mh0.out index 77fc8a8..a439814 100644 --- a/tests/bfs/color_mh0.out +++ b/tests/bfs/color_mh0.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_mi.out b/tests/bfs/color_mi.out index 77fc8a8..a439814 100644 --- a/tests/bfs/color_mi.out +++ b/tests/bfs/color_mi.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_missing_colon.out b/tests/bfs/color_missing_colon.out index cf26e73..218100f 100644 --- a/tests/bfs/color_missing_colon.out +++ b/tests/bfs/color_missing_colon.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;41msuid[0m [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_no.out b/tests/bfs/color_no.out new file mode 100644 index 0000000..67e1eee --- /dev/null +++ b/tests/bfs/color_no.out @@ -0,0 +1,27 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m[01;92m$'\e[0m'[0m +[01;34mrainbow[0m +[01;34mrainbow/[0m[01;32mexec.sh[0m +[01;34mrainbow/[0m[01;35msocket[0m +[01;34mrainbow/[0m[01;36mbroken[0m +[01;34mrainbow/[0m[01;36mchardev_link[0m +[01;34mrainbow/[0m[01;36mlink.txt[0m +[01;34mrainbow/[0m[01;92mfile.dat[0m +[01;34mrainbow/[0m[01;92mfile.txt[0m +[01;34mrainbow/[0m[01;92mlower.gz[0m +[01;34mrainbow/[0m[01;92mlower.tar[0m +[01;34mrainbow/[0m[01;92mlower.tar.gz[0m +[01;34mrainbow/[0m[01;92mlu.tar.GZ[0m +[01;34mrainbow/[0m[01;92mmh1[0m +[01;34mrainbow/[0m[01;92mmh2[0m +[01;34mrainbow/[0m[01;92mul.TAR.gz[0m +[01;34mrainbow/[0m[01;92mupper.GZ[0m +[01;34mrainbow/[0m[01;92mupper.TAR[0m +[01;34mrainbow/[0m[01;92mupper.TAR.GZ[0m +[01;34mrainbow/[0m[30;42msticky_ow[0m +[01;34mrainbow/[0m[30;43msgid[0m +[01;34mrainbow/[0m[33mpipe[0m +[01;34mrainbow/[0m[34;42mow[0m +[01;34mrainbow/[0m[37;41msugid[0m +[01;34mrainbow/[0m[37;41msuid[0m +[01;34mrainbow/[0m[37;44msticky[0m diff --git a/tests/bfs/color_no.sh b/tests/bfs/color_no.sh new file mode 100644 index 0000000..b7527cb --- /dev/null +++ b/tests/bfs/color_no.sh @@ -0,0 +1 @@ +LS_COLORS="no=01;92:" bfs_diff rainbow -color diff --git a/tests/bfs/color_no_stat.out b/tests/bfs/color_no_stat.out index 1fc5324..e3031b2 100644 --- a/tests/bfs/color_no_stat.out +++ b/tests/bfs/color_no_stat.out @@ -1,7 +1,6 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m -[01;34mrainbow/[0m[01;34mow[0m -[01;34mrainbow/[0m[01;34msticky[0m -[01;34mrainbow/[0m[01;34msticky_ow[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m @@ -10,11 +9,19 @@ [01;34mrainbow/[0m[33mpipe[0m [01;34mrainbow/[0mexec.sh [01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 [01;34mrainbow/[0msgid -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz [01;34mrainbow/[0msugid [01;34mrainbow/[0msuid +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/ow[0m +[01;34mrainbow/sticky[0m +[01;34mrainbow/sticky_ow[0m diff --git a/tests/bfs/color_nul.out b/tests/bfs/color_nul.out Binary files differindex c328f82..8ccd9a7 100644 --- a/tests/bfs/color_nul.out +++ b/tests/bfs/color_nul.out diff --git a/tests/bfs/color_nul.sh b/tests/bfs/color_nul.sh index 4979569..cb662d6 100644 --- a/tests/bfs/color_nul.sh +++ b/tests/bfs/color_nul.sh @@ -1,2 +1,3 @@ -LS_COLORS="ec=\33[m\0:" invoke_bfs rainbow -color -maxdepth 0 >"$OUT" +LS_COLORS="ec=\33[\0m:*.gz=\0\61;31:" invoke_bfs rainbow -color | tr '\0' '0' >"$OUT" +sort_output diff_output diff --git a/tests/bfs/color_or.out b/tests/bfs/color_or.out index 9e1fe5c..0bd2570 100644 --- a/tests/bfs/color_or.out +++ b/tests/bfs/color_or.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_or0_mi.out b/tests/bfs/color_or0_mi.out index 77fc8a8..a439814 100644 --- a/tests/bfs/color_or0_mi.out +++ b/tests/bfs/color_or0_mi.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_or0_mi0.out b/tests/bfs/color_or0_mi0.out index 77fc8a8..a439814 100644 --- a/tests/bfs/color_or0_mi0.out +++ b/tests/bfs/color_or0_mi0.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_or_mi.out b/tests/bfs/color_or_mi.out index 5667f56..fb67e58 100644 --- a/tests/bfs/color_or_mi.out +++ b/tests/bfs/color_or_mi.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;31mbroken[0m [01;34mrainbow/[0m[01;32mexec.sh[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_or_mi0.out b/tests/bfs/color_or_mi0.out index 5667f56..fb67e58 100644 --- a/tests/bfs/color_or_mi0.out +++ b/tests/bfs/color_or_mi0.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;31mbroken[0m [01;34mrainbow/[0m[01;32mexec.sh[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_rs_lc_rc_ec.out b/tests/bfs/color_rs_lc_rc_ec.out index d39bbe7..077ef8d 100644 --- a/tests/bfs/color_rs_lc_rc_ec.out +++ b/tests/bfs/color_rs_lc_rc_ec.out @@ -1,3 +1,5 @@ +LC01;34RC$'rainbow/\e[1m'EC +LC01;34RC$'rainbow/\e[1m/'EC$'\e[0m' LC01;34RCrainbow/ECLC01;32RCexec.shEC LC01;34RCrainbow/ECLC01;35RCsocketEC LC01;34RCrainbow/ECLC01;36RCbrokenEC @@ -12,9 +14,14 @@ LC01;34RCrainbow/ECLC37;41RCsuidEC LC01;34RCrainbow/ECLC37;44RCstickyEC LC01;34RCrainbow/ECfile.dat LC01;34RCrainbow/ECfile.txt +LC01;34RCrainbow/EClower.gz +LC01;34RCrainbow/EClower.tar +LC01;34RCrainbow/EClower.tar.gz +LC01;34RCrainbow/EClu.tar.GZ LC01;34RCrainbow/ECmh1 LC01;34RCrainbow/ECmh2 -LC01;34RCrainbow/ECstar.gz -LC01;34RCrainbow/ECstar.tar -LC01;34RCrainbow/ECstar.tar.gz +LC01;34RCrainbow/ECul.TAR.gz +LC01;34RCrainbow/ECupper.GZ +LC01;34RCrainbow/ECupper.TAR +LC01;34RCrainbow/ECupper.TAR.GZ LC01;34RCrainbowEC diff --git a/tests/bfs/color_st0_tw0_ow.out b/tests/bfs/color_st0_tw0_ow.out index 9a47ef2..a82762b 100644 --- a/tests/bfs/color_st0_tw0_ow.out +++ b/tests/bfs/color_st0_tw0_ow.out @@ -1,6 +1,7 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;34msticky[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m @@ -13,8 +14,14 @@ [01;34mrainbow/[0m[37;41msuid[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/sticky[0m diff --git a/tests/bfs/color_st0_tw0_ow0.out b/tests/bfs/color_st0_tw0_ow0.out index 2b86fe4..041f1d4 100644 --- a/tests/bfs/color_st0_tw0_ow0.out +++ b/tests/bfs/color_st0_tw0_ow0.out @@ -1,8 +1,7 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;34mow[0m -[01;34mrainbow/[0m[01;34msticky[0m -[01;34mrainbow/[0m[01;34msticky_ow[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m @@ -13,8 +12,16 @@ [01;34mrainbow/[0m[37;41msuid[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/ow[0m +[01;34mrainbow/sticky[0m +[01;34mrainbow/sticky_ow[0m diff --git a/tests/bfs/color_st0_tw_ow.out b/tests/bfs/color_st0_tw_ow.out index 42549a1..4dcb2f2 100644 --- a/tests/bfs/color_st0_tw_ow.out +++ b/tests/bfs/color_st0_tw_ow.out @@ -1,6 +1,7 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;34msticky[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m @@ -13,8 +14,14 @@ [01;34mrainbow/[0m[40;32msticky_ow[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/sticky[0m diff --git a/tests/bfs/color_st0_tw_ow0.out b/tests/bfs/color_st0_tw_ow0.out index 535b8ae..954ce9c 100644 --- a/tests/bfs/color_st0_tw_ow0.out +++ b/tests/bfs/color_st0_tw_ow0.out @@ -1,7 +1,7 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;34mow[0m -[01;34mrainbow/[0m[01;34msticky[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m @@ -13,8 +13,15 @@ [01;34mrainbow/[0m[40;32msticky_ow[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/ow[0m +[01;34mrainbow/sticky[0m diff --git a/tests/bfs/color_st_tw0_ow.out b/tests/bfs/color_st_tw0_ow.out index c9a86f4..a6e9a16 100644 --- a/tests/bfs/color_st_tw0_ow.out +++ b/tests/bfs/color_st_tw0_ow.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -13,8 +15,13 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_st_tw0_ow0.out b/tests/bfs/color_st_tw0_ow0.out index 2d94f3a..756dafb 100644 --- a/tests/bfs/color_st_tw0_ow0.out +++ b/tests/bfs/color_st_tw0_ow0.out @@ -1,6 +1,7 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;34mow[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m @@ -13,8 +14,14 @@ [01;34mrainbow/[0m[37;44msticky_ow[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/ow[0m diff --git a/tests/bfs/color_st_tw_ow0.out b/tests/bfs/color_st_tw_ow0.out index 317ef90..6e4a260 100644 --- a/tests/bfs/color_st_tw_ow0.out +++ b/tests/bfs/color_st_tw_ow0.out @@ -1,6 +1,7 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m -[01;34mrainbow/[0m[01;34mow[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mchardev_link[0m @@ -13,8 +14,14 @@ [01;34mrainbow/[0m[40;32msticky_ow[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ +[01;34mrainbow/ow[0m diff --git a/tests/bfs/color_star.sh b/tests/bfs/color_star.sh index 3ada4fd..6d5312e 100644 --- a/tests/bfs/color_star.sh +++ b/tests/bfs/color_star.sh @@ -1,2 +1,2 @@ # Regression test: don't segfault on LS_COLORS="*" -LS_COLORS="*" bfs_diff rainbow -color +! LS_COLORS="*" invoke_bfs rainbow -color diff --git a/tests/bfs/color_su0_sg.out b/tests/bfs/color_su0_sg.out index 8b8c8b8..d13b6b6 100644 --- a/tests/bfs/color_su0_sg.out +++ b/tests/bfs/color_su0_sg.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -12,9 +14,14 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz [01;34mrainbow/[0msuid +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_su0_sg0.out b/tests/bfs/color_su0_sg0.out index 0cd5f9a..77fba58 100644 --- a/tests/bfs/color_su0_sg0.out +++ b/tests/bfs/color_su0_sg0.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -10,11 +12,16 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 [01;34mrainbow/[0msgid -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz [01;34mrainbow/[0msugid [01;34mrainbow/[0msuid +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/color_su_sg0.out b/tests/bfs/color_su_sg0.out index a9e8c5d..8fab046 100644 --- a/tests/bfs/color_su_sg0.out +++ b/tests/bfs/color_su_sg0.out @@ -1,3 +1,5 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' [01;34mrainbow[0m [01;34mrainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;35msocket[0m @@ -12,9 +14,14 @@ [01;34mrainbow/[0m[37;44msticky[0m [01;34mrainbow/[0mfile.dat [01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ [01;34mrainbow/[0mmh1 [01;34mrainbow/[0mmh2 [01;34mrainbow/[0msgid -[01;34mrainbow/[0mstar.gz -[01;34mrainbow/[0mstar.tar -[01;34mrainbow/[0mstar.tar.gz +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/comma_incomplete.sh b/tests/bfs/comma_incomplete.sh index 07cf505..bd60168 100644 --- a/tests/bfs/comma_incomplete.sh +++ b/tests/bfs/comma_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -print , +! invoke_bfs -print , diff --git a/tests/bfs/deep_strict.sh b/tests/bfs/deep_strict.sh index e057310..22453c0 100644 --- a/tests/bfs/deep_strict.sh +++ b/tests/bfs/deep_strict.sh @@ -1,5 +1,3 @@ -closefrom 4 - # Not even enough fds to keep the root open -ulimit -n 7 +ulimit -n $((NOPENFD + 4)) bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \; diff --git a/tests/bfs/exclude_exclude.sh b/tests/bfs/exclude_exclude.sh index c687623..739342f 100644 --- a/tests/bfs/exclude_exclude.sh +++ b/tests/bfs/exclude_exclude.sh @@ -1 +1 @@ -fail invoke_bfs basic -exclude -exclude -name foo +! invoke_bfs basic -exclude -exclude -name foo diff --git a/tests/bfs/exclude_print.sh b/tests/bfs/exclude_print.sh index 52ff0fd..dc89e1d 100644 --- a/tests/bfs/exclude_print.sh +++ b/tests/bfs/exclude_print.sh @@ -1 +1 @@ -fail invoke_bfs basic -exclude -print +! invoke_bfs basic -exclude -print diff --git a/tests/bfs/exec_flush_fprint.sh b/tests/bfs/exec_flush_fprint.sh index bf6b62f..a862773 100644 --- a/tests/bfs/exec_flush_fprint.sh +++ b/tests/bfs/exec_flush_fprint.sh @@ -1,3 +1,2 @@ # Even non-stdstreams should be flushed -clean_scratch -bfs_diff basic/a -fprint scratch/foo -exec cat scratch/foo \; +bfs_diff basic/a -fprint "$OUT.f" -exec cat "$OUT.f" \; diff --git a/tests/bfs/exec_flush_fprint_fail.sh b/tests/bfs/exec_flush_fprint_fail.sh index 5da944a..cd38e41 100644 --- a/tests/bfs/exec_flush_fprint_fail.sh +++ b/tests/bfs/exec_flush_fprint_fail.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full -fail invoke_bfs basic/a -fprint /dev/full -exec true \; +test -e /dev/full || skip +! invoke_bfs basic/a -fprint /dev/full -exec true \; diff --git a/tests/bfs/execdir_plus.sh b/tests/bfs/execdir_plus.sh index 9ae7764..6f24bdc 100644 --- a/tests/bfs/execdir_plus.sh +++ b/tests/bfs/execdir_plus.sh @@ -1,7 +1,4 @@ tree=$(invoke_bfs -D tree 2>&1 -quit) +[[ "$tree" == *"-S dfs"* ]] && skip -if [[ "$tree" == *"-S dfs"* ]]; then - skip -fi - -bfs_diff basic -execdir "$TESTS/sort-args.sh" {} + +bfs_diff -j1 basic -execdir "$TESTS/sort-args.sh" {} + diff --git a/tests/bfs/execdir_plus_nonexistent.sh b/tests/bfs/execdir_plus_nonexistent.sh index 8436953..ed7ed56 100644 --- a/tests/bfs/execdir_plus_nonexistent.sh +++ b/tests/bfs/execdir_plus_nonexistent.sh @@ -1,5 +1,2 @@ -stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} + 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 - -bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print -(($? == EX_BFS)) +bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/bfs/fprint_error_stderr.sh b/tests/bfs/fprint_error_stderr.sh index 427808f..2cc4037 100644 --- a/tests/bfs/fprint_error_stderr.sh +++ b/tests/bfs/fprint_error_stderr.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full -fail invoke_bfs basic -maxdepth 0 -fprint /dev/full 2>/dev/full +test -e /dev/full || skip +! invoke_bfs basic -maxdepth 0 -fprint /dev/full 2>/dev/full diff --git a/tests/bfs/fprint_error_stdout.sh b/tests/bfs/fprint_error_stdout.sh index fbdc1d0..42a7b36 100644 --- a/tests/bfs/fprint_error_stdout.sh +++ b/tests/bfs/fprint_error_stdout.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full -fail invoke_bfs basic -maxdepth 0 -fprint /dev/full >/dev/full +test -e /dev/full || skip +! invoke_bfs basic -maxdepth 0 -fprint /dev/full >/dev/full diff --git a/tests/bfs/help.sh b/tests/bfs/help.sh index 5e5c684..5029c7e 100644 --- a/tests/bfs/help.sh +++ b/tests/bfs/help.sh @@ -1,6 +1,4 @@ -invoke_bfs -help | grep -E '\{...?\}' && return 1 -invoke_bfs -D help | grep -E '\{...?\}' && return 1 -invoke_bfs -S help | grep -E '\{...?\}' && return 1 -invoke_bfs -regextype help | grep -E '\{...?\}' && return 1 - -return 0 +! invoke_bfs -help | grep -E '\{...?\}' || fail +! invoke_bfs -D help | grep -E '\{...?\}' || fail +! invoke_bfs -S help | grep -E '\{...?\}' || fail +! invoke_bfs -regextype help | grep -E '\{...?\}' || fail diff --git a/tests/bfs/high_byte.sh b/tests/bfs/high_byte.sh index 222f24b..c76199f 100644 --- a/tests/bfs/high_byte.sh +++ b/tests/bfs/high_byte.sh @@ -1 +1 @@ -fail invoke_bfs -$'\xFF' +! invoke_bfs -$'\xFF' diff --git a/tests/bfs/j0.sh b/tests/bfs/j0.sh new file mode 100644 index 0000000..97a7c5c --- /dev/null +++ b/tests/bfs/j0.sh @@ -0,0 +1 @@ +! invoke_bfs -j0 basic diff --git a/tests/bfs/j1.out b/tests/bfs/j1.out new file mode 100644 index 0000000..a7ccfe4 --- /dev/null +++ b/tests/bfs/j1.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/j1.sh b/tests/bfs/j1.sh new file mode 100644 index 0000000..972ac1b --- /dev/null +++ b/tests/bfs/j1.sh @@ -0,0 +1 @@ +bfs_diff -j1 basic diff --git a/tests/bfs/j64.out b/tests/bfs/j64.out new file mode 100644 index 0000000..a7ccfe4 --- /dev/null +++ b/tests/bfs/j64.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/j64.sh b/tests/bfs/j64.sh new file mode 100644 index 0000000..c56788f --- /dev/null +++ b/tests/bfs/j64.sh @@ -0,0 +1 @@ +bfs_diff -j64 basic diff --git a/tests/bfs/j_negative.sh b/tests/bfs/j_negative.sh new file mode 100644 index 0000000..809c98c --- /dev/null +++ b/tests/bfs/j_negative.sh @@ -0,0 +1 @@ +! invoke_bfs -j-1 basic diff --git a/tests/bfs/limit.out b/tests/bfs/limit.out new file mode 100644 index 0000000..ea94276 --- /dev/null +++ b/tests/bfs/limit.out @@ -0,0 +1,4 @@ +basic/a +basic/b +basic/c/d +basic/e/f diff --git a/tests/bfs/limit.sh b/tests/bfs/limit.sh new file mode 100644 index 0000000..84b605f --- /dev/null +++ b/tests/bfs/limit.sh @@ -0,0 +1 @@ +bfs_diff -s basic -type f -print -limit 4 diff --git a/tests/bfs/limit_0.sh b/tests/bfs/limit_0.sh new file mode 100644 index 0000000..3ce26de --- /dev/null +++ b/tests/bfs/limit_0.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit 0 diff --git a/tests/bfs/limit_implicit_print.sh b/tests/bfs/limit_implicit_print.sh new file mode 100644 index 0000000..cdb059d --- /dev/null +++ b/tests/bfs/limit_implicit_print.sh @@ -0,0 +1 @@ +! invoke_bfs basic -type f -limit 1 diff --git a/tests/bfs/limit_incomplete.sh b/tests/bfs/limit_incomplete.sh new file mode 100644 index 0000000..2d1e842 --- /dev/null +++ b/tests/bfs/limit_incomplete.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit diff --git a/tests/bfs/limit_one.sh b/tests/bfs/limit_one.sh new file mode 100644 index 0000000..3f8181c --- /dev/null +++ b/tests/bfs/limit_one.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit one diff --git a/tests/bfs/links_empty.sh b/tests/bfs/links_empty.sh index 34c7c25..42cf6e5 100644 --- a/tests/bfs/links_empty.sh +++ b/tests/bfs/links_empty.sh @@ -1 +1 @@ -fail invoke_bfs links -links '' +! invoke_bfs links -links '' diff --git a/tests/bfs/links_invalid.sh b/tests/bfs/links_invalid.sh index ff69fa6..4d139c9 100644 --- a/tests/bfs/links_invalid.sh +++ b/tests/bfs/links_invalid.sh @@ -1 +1 @@ -fail invoke_bfs links -links ASDF +! invoke_bfs links -links ASDF diff --git a/tests/bfs/links_leading_space.sh b/tests/bfs/links_leading_space.sh new file mode 100644 index 0000000..15957af --- /dev/null +++ b/tests/bfs/links_leading_space.sh @@ -0,0 +1 @@ +! invoke_bfs links -links ' 1' diff --git a/tests/bfs/links_negative.sh b/tests/bfs/links_negative.sh index b5d9c58..e664b99 100644 --- a/tests/bfs/links_negative.sh +++ b/tests/bfs/links_negative.sh @@ -1 +1 @@ -fail invoke_bfs links -links +-1 +! invoke_bfs links -links +-1 diff --git a/tests/bfs/links_noarg.sh b/tests/bfs/links_noarg.sh index 5dede5f..5c948dc 100644 --- a/tests/bfs/links_noarg.sh +++ b/tests/bfs/links_noarg.sh @@ -1 +1 @@ -fail invoke_bfs links -links +! invoke_bfs links -links diff --git a/tests/bfs/newerma_nonexistent.sh b/tests/bfs/newerma_nonexistent.sh index 7f3695f..cdedb4a 100644 --- a/tests/bfs/newerma_nonexistent.sh +++ b/tests/bfs/newerma_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs times -newerma basic/nonexistent +! invoke_bfs times -newerma basic/nonexistent diff --git a/tests/bfs/newermq.sh b/tests/bfs/newermq.sh index 2a22586..2f705dc 100644 --- a/tests/bfs/newermq.sh +++ b/tests/bfs/newermq.sh @@ -1 +1 @@ -fail invoke_bfs times -newermq times/a +! invoke_bfs times -newermq times/a diff --git a/tests/bfs/newermt_invalid.sh b/tests/bfs/newermt_invalid.sh index 61d2485..98efece 100644 --- a/tests/bfs/newermt_invalid.sh +++ b/tests/bfs/newermt_invalid.sh @@ -1 +1 @@ -fail invoke_bfs times -newermt not_a_date_time +! invoke_bfs times -newermt not_a_date_time diff --git a/tests/bfs/newerqm.sh b/tests/bfs/newerqm.sh index da84350..c0cff98 100644 --- a/tests/bfs/newerqm.sh +++ b/tests/bfs/newerqm.sh @@ -1 +1 @@ -fail invoke_bfs times -newerqm times/a +! invoke_bfs times -newerqm times/a diff --git a/tests/bfs/nocolor.out b/tests/bfs/nocolor.out index b53fe03..d51d24d 100644 --- a/tests/bfs/nocolor.out +++ b/tests/bfs/nocolor.out @@ -1,20 +1,27 @@ rainbow +rainbow/[1m +rainbow/[1m/[0m rainbow/broken rainbow/chardev_link rainbow/exec.sh rainbow/file.dat rainbow/file.txt rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 rainbow/ow rainbow/pipe rainbow/sgid rainbow/socket -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz rainbow/sticky rainbow/sticky_ow rainbow/sugid rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/nocolor_env.out b/tests/bfs/nocolor_env.out new file mode 100644 index 0000000..d51d24d --- /dev/null +++ b/tests/bfs/nocolor_env.out @@ -0,0 +1,27 @@ +rainbow +rainbow/[1m +rainbow/[1m/[0m +rainbow/broken +rainbow/chardev_link +rainbow/exec.sh +rainbow/file.dat +rainbow/file.txt +rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/mh1 +rainbow/mh2 +rainbow/ow +rainbow/pipe +rainbow/sgid +rainbow/socket +rainbow/sticky +rainbow/sticky_ow +rainbow/sugid +rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/nocolor_env.sh b/tests/bfs/nocolor_env.sh new file mode 100644 index 0000000..d1c2afb --- /dev/null +++ b/tests/bfs/nocolor_env.sh @@ -0,0 +1,3 @@ +NO_COLOR=1 bfs_pty rainbow >"$OUT" +sort_output +diff_output diff --git a/tests/bfs/nocolor_env_empty.out b/tests/bfs/nocolor_env_empty.out new file mode 100644 index 0000000..a439814 --- /dev/null +++ b/tests/bfs/nocolor_env_empty.out @@ -0,0 +1,27 @@ +[01;34m$'rainbow/\e[1m'[0m +[01;34m$'rainbow/\e[1m/'[0m$'\e[0m' +[01;34mrainbow[0m +[01;34mrainbow/[0m[01;32mexec.sh[0m +[01;34mrainbow/[0m[01;35msocket[0m +[01;34mrainbow/[0m[01;36mbroken[0m +[01;34mrainbow/[0m[01;36mchardev_link[0m +[01;34mrainbow/[0m[01;36mlink.txt[0m +[01;34mrainbow/[0m[30;42msticky_ow[0m +[01;34mrainbow/[0m[30;43msgid[0m +[01;34mrainbow/[0m[33mpipe[0m +[01;34mrainbow/[0m[34;42mow[0m +[01;34mrainbow/[0m[37;41msugid[0m +[01;34mrainbow/[0m[37;41msuid[0m +[01;34mrainbow/[0m[37;44msticky[0m +[01;34mrainbow/[0mfile.dat +[01;34mrainbow/[0mfile.txt +[01;34mrainbow/[0mlower.gz +[01;34mrainbow/[0mlower.tar +[01;34mrainbow/[0mlower.tar.gz +[01;34mrainbow/[0mlu.tar.GZ +[01;34mrainbow/[0mmh1 +[01;34mrainbow/[0mmh2 +[01;34mrainbow/[0mul.TAR.gz +[01;34mrainbow/[0mupper.GZ +[01;34mrainbow/[0mupper.TAR +[01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/nocolor_env_empty.sh b/tests/bfs/nocolor_env_empty.sh new file mode 100644 index 0000000..1edfb1d --- /dev/null +++ b/tests/bfs/nocolor_env_empty.sh @@ -0,0 +1,3 @@ +NO_COLOR= bfs_pty rainbow >"$OUT" +sort_output +diff_output diff --git a/tests/bfs/or_incomplete.sh b/tests/bfs/or_incomplete.sh index c941b95..4af31b6 100644 --- a/tests/bfs/or_incomplete.sh +++ b/tests/bfs/or_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -print -o +! invoke_bfs -print -o diff --git a/tests/bfs/perm_symbolic_double_comma.sh b/tests/bfs/perm_symbolic_double_comma.sh index 66db0ac..48f9d4b 100644 --- a/tests/bfs/perm_symbolic_double_comma.sh +++ b/tests/bfs/perm_symbolic_double_comma.sh @@ -1 +1 @@ -fail invoke_bfs perms -perm a+r,,u+w +! invoke_bfs perms -perm a+r,,u+w diff --git a/tests/bfs/perm_symbolic_missing_action.sh b/tests/bfs/perm_symbolic_missing_action.sh index 3b18721..28446ab 100644 --- a/tests/bfs/perm_symbolic_missing_action.sh +++ b/tests/bfs/perm_symbolic_missing_action.sh @@ -1 +1 @@ -fail invoke_bfs perms -perm a +! invoke_bfs perms -perm a diff --git a/tests/bfs/perm_symbolic_trailing_comma.sh b/tests/bfs/perm_symbolic_trailing_comma.sh index c52ebe6..01bbc16 100644 --- a/tests/bfs/perm_symbolic_trailing_comma.sh +++ b/tests/bfs/perm_symbolic_trailing_comma.sh @@ -1 +1 @@ -fail invoke_bfs perms -perm a+r, +! invoke_bfs perms -perm a+r, diff --git a/tests/bfs/printf_color.out b/tests/bfs/printf_color.out index d9cd1a4..77d21c3 100644 --- a/tests/bfs/printf_color.out +++ b/tests/bfs/printf_color.out @@ -1,5 +1,8 @@ -[01;34m.[0m [01;34m.[0m [01;34mrainbow[0m [01;34m./[0m[01;34mrainbow[0m [01;34mrainbow[0m +[01;34m.[0m [01;34m$'./rainbow/\e[1m'[0m $'\e[0m' [01;34m$'./rainbow/\e[1m/'[0m$'\e[0m' [01;34m$'rainbow/\e[1m/'[0m$'\e[0m' +[01;34m.[0m [01;34m.[0m [01;34m.[0m [01;34m.[0m +[01;34m.[0m [01;34m.[0m [01;34mrainbow[0m [01;34m./rainbow[0m [01;34mrainbow[0m [01;34m.[0m [01;34m./rainbow[0m [01;32mexec.sh[0m [01;34m./rainbow/[0m[01;32mexec.sh[0m [01;34mrainbow/[0m[01;32mexec.sh[0m +[01;34m.[0m [01;34m./rainbow[0m [01;34m$'\e[1m'[0m [01;34m$'./rainbow/\e[1m'[0m [01;34m$'rainbow/\e[1m'[0m [01;34m.[0m [01;34m./rainbow[0m [01;35msocket[0m [01;34m./rainbow/[0m[01;35msocket[0m [01;34mrainbow/[0m[01;35msocket[0m [01;34m.[0m [01;34m./rainbow[0m [01;36mbroken[0m [01;34m./rainbow/[0m[01;36mbroken[0m [01;34mrainbow/[0m[01;36mbroken[0m nowhere [01;34m.[0m [01;34m./rainbow[0m [01;36mchardev_link[0m [01;34m./rainbow/[0m[01;36mchardev_link[0m [01;34mrainbow/[0m[01;36mchardev_link[0m [01;34m/dev/[0m[01;33mnull[0m @@ -13,8 +16,13 @@ [01;34m.[0m [01;34m./rainbow[0m [37;44msticky[0m [01;34m./rainbow/[0m[37;44msticky[0m [01;34mrainbow/[0m[37;44msticky[0m [01;34m.[0m [01;34m./rainbow[0m file.dat [01;34m./rainbow/[0mfile.dat [01;34mrainbow/[0mfile.dat [01;34m.[0m [01;34m./rainbow[0m file.txt [01;34m./rainbow/[0mfile.txt [01;34mrainbow/[0mfile.txt +[01;34m.[0m [01;34m./rainbow[0m lower.gz [01;34m./rainbow/[0mlower.gz [01;34mrainbow/[0mlower.gz +[01;34m.[0m [01;34m./rainbow[0m lower.tar [01;34m./rainbow/[0mlower.tar [01;34mrainbow/[0mlower.tar +[01;34m.[0m [01;34m./rainbow[0m lower.tar.gz [01;34m./rainbow/[0mlower.tar.gz [01;34mrainbow/[0mlower.tar.gz +[01;34m.[0m [01;34m./rainbow[0m lu.tar.GZ [01;34m./rainbow/[0mlu.tar.GZ [01;34mrainbow/[0mlu.tar.GZ [01;34m.[0m [01;34m./rainbow[0m mh1 [01;34m./rainbow/[0mmh1 [01;34mrainbow/[0mmh1 [01;34m.[0m [01;34m./rainbow[0m mh2 [01;34m./rainbow/[0mmh2 [01;34mrainbow/[0mmh2 -[01;34m.[0m [01;34m./rainbow[0m star.gz [01;34m./rainbow/[0mstar.gz [01;34mrainbow/[0mstar.gz -[01;34m.[0m [01;34m./rainbow[0m star.tar [01;34m./rainbow/[0mstar.tar [01;34mrainbow/[0mstar.tar -[01;34m.[0m [01;34m./rainbow[0m star.tar.gz [01;34m./rainbow/[0mstar.tar.gz [01;34mrainbow/[0mstar.tar.gz +[01;34m.[0m [01;34m./rainbow[0m ul.TAR.gz [01;34m./rainbow/[0mul.TAR.gz [01;34mrainbow/[0mul.TAR.gz +[01;34m.[0m [01;34m./rainbow[0m upper.GZ [01;34m./rainbow/[0mupper.GZ [01;34mrainbow/[0mupper.GZ +[01;34m.[0m [01;34m./rainbow[0m upper.TAR [01;34m./rainbow/[0mupper.TAR [01;34mrainbow/[0mupper.TAR +[01;34m.[0m [01;34m./rainbow[0m upper.TAR.GZ [01;34m./rainbow/[0mupper.TAR.GZ [01;34mrainbow/[0mupper.TAR.GZ diff --git a/tests/bfs/printf_color.sh b/tests/bfs/printf_color.sh index 7bb38c2..3641ddb 100644 --- a/tests/bfs/printf_color.sh +++ b/tests/bfs/printf_color.sh @@ -1 +1 @@ -bfs_diff -color -path './rainbow*' -printf '%H %h %f %p %P %l\n' +bfs_diff -color -exclude \( -depth 1 -not -name rainbow \) -printf '%H %h %f %p %P %l\n' diff --git a/tests/bfs/printf_duplicate_flag.sh b/tests/bfs/printf_duplicate_flag.sh index 77650d0..5ff29f1 100644 --- a/tests/bfs/printf_duplicate_flag.sh +++ b/tests/bfs/printf_duplicate_flag.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%--p' +! invoke_bfs basic -printf '%--p' diff --git a/tests/bfs/printf_everything.sh b/tests/bfs/printf_everything.sh index 5f20718..07d574a 100644 --- a/tests/bfs/printf_everything.sh +++ b/tests/bfs/printf_everything.sh @@ -1,14 +1,14 @@ everything=(%{a,b,c,d,D,f,g,G,h,H,i,k,l,m,M,n,p,P,s,S,t,u,U,y,Y}) # Check if we have fstypes -if ! fail invoke_bfs basic -printf '%F' -quit >/dev/null; then +if invoke_bfs basic -printf '%F' -quit >/dev/null; then everything+=(%F) fi everything+=(%{A,C,T}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z}) # Check if we have birth times -if ! fail invoke_bfs basic -printf '%w' -quit >/dev/null; then +if invoke_bfs basic -printf '%w' -quit >/dev/null; then everything+=(%w %{B,W}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z}) fi diff --git a/tests/bfs/printf_incomplete_escape.sh b/tests/bfs/printf_incomplete_escape.sh index 144add5..f560d28 100644 --- a/tests/bfs/printf_incomplete_escape.sh +++ b/tests/bfs/printf_incomplete_escape.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '\' +! invoke_bfs basic -printf '\' diff --git a/tests/bfs/printf_incomplete_format.sh b/tests/bfs/printf_incomplete_format.sh index 347a0f4..92c6afc 100644 --- a/tests/bfs/printf_incomplete_format.sh +++ b/tests/bfs/printf_incomplete_format.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%' +! invoke_bfs basic -printf '%' diff --git a/tests/bfs/printf_invalid_escape.sh b/tests/bfs/printf_invalid_escape.sh index ce12233..4338f9b 100644 --- a/tests/bfs/printf_invalid_escape.sh +++ b/tests/bfs/printf_invalid_escape.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '\!' +! invoke_bfs basic -printf '\!' diff --git a/tests/bfs/printf_invalid_format.sh b/tests/bfs/printf_invalid_format.sh index 1717615..59d63a7 100644 --- a/tests/bfs/printf_invalid_format.sh +++ b/tests/bfs/printf_invalid_format.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%!' +! invoke_bfs basic -printf '%!' diff --git a/tests/bfs/printf_must_be_numeric.sh b/tests/bfs/printf_must_be_numeric.sh index eabb3d6..7c7c3fa 100644 --- a/tests/bfs/printf_must_be_numeric.sh +++ b/tests/bfs/printf_must_be_numeric.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%+p' +! invoke_bfs basic -printf '%+p' diff --git a/tests/bfs/status.sh b/tests/bfs/status.sh new file mode 100644 index 0000000..83e12d3 --- /dev/null +++ b/tests/bfs/status.sh @@ -0,0 +1 @@ +bfs_pty basic -status -print -depth 0 -exec stty cols 123 rows 14 \; >"$OUT" diff --git a/tests/bfs/stderr_fails_loudly.sh b/tests/bfs/stderr_fails_loudly.sh index d8b3861..8572d5a 100644 --- a/tests/bfs/stderr_fails_loudly.sh +++ b/tests/bfs/stderr_fails_loudly.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full -fail invoke_bfs -D all basic -false -fprint /dev/full 2>/dev/full +test -e /dev/full || skip +! invoke_bfs -D all basic -false -fprint /dev/full 2>/dev/full diff --git a/tests/bfs/stderr_fails_silently.sh b/tests/bfs/stderr_fails_silently.sh index 731cb02..a37393d 100644 --- a/tests/bfs/stderr_fails_silently.sh +++ b/tests/bfs/stderr_fails_silently.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip bfs_diff -D all basic 2>/dev/full diff --git a/tests/bfs/unexpected_operator.sh b/tests/bfs/unexpected_operator.sh index b3658f6..2eb0e71 100644 --- a/tests/bfs/unexpected_operator.sh +++ b/tests/bfs/unexpected_operator.sh @@ -1 +1 @@ -fail invoke_bfs \! -o -print +! invoke_bfs \! -o -print diff --git a/tests/bfs/warn_O9.out b/tests/bfs/warn_O9.out new file mode 100644 index 0000000..336a6e8 --- /dev/null +++ b/tests/bfs/warn_O9.out @@ -0,0 +1,19 @@ +. +./a +./b +./c +./c/d +./e +./e/f +./g +./g/h +./i +./j +./j/foo +./k +./k/foo +./k/foo/bar +./l +./l/foo +./l/foo/bar +./l/foo/bar/baz diff --git a/tests/bfs/warn_O9.sh b/tests/bfs/warn_O9.sh new file mode 100644 index 0000000..821789f --- /dev/null +++ b/tests/bfs/warn_O9.sh @@ -0,0 +1,3 @@ +# Regression test: don't crash when warning if -O9 is the last argument +cd basic +bfs_diff -warn -O9 diff --git a/tests/bfs/warn_xdev_mount.out b/tests/bfs/warn_xdev_mount.out new file mode 100644 index 0000000..a7ccfe4 --- /dev/null +++ b/tests/bfs/warn_xdev_mount.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/warn_xdev_mount.sh b/tests/bfs/warn_xdev_mount.sh new file mode 100644 index 0000000..5d395f6 --- /dev/null +++ b/tests/bfs/warn_xdev_mount.sh @@ -0,0 +1,2 @@ +# Regression test: don't crash if -mount is the last option +bfs_diff basic -warn -xdev -mount diff --git a/tests/bfs/xtype_depth.sh b/tests/bfs/xtype_depth.sh index cd478af..02c8173 100644 --- a/tests/bfs/xtype_depth.sh +++ b/tests/bfs/xtype_depth.sh @@ -1,2 +1,2 @@ # Make sure -xtype is considered side-effecting for facts_when_impure -fail invoke_bfs loops -xtype l -depth 100 +! invoke_bfs loops -xtype l -depth 100 diff --git a/tests/bfstd.c b/tests/bfstd.c new file mode 100644 index 0000000..07b68b0 --- /dev/null +++ b/tests/bfstd.c @@ -0,0 +1,93 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "bfstd.h" +#include "diag.h" +#include <errno.h> +#include <langinfo.h> +#include <stdlib.h> +#include <string.h> + +/** Check the result of xdirname()/xbasename(). */ +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)); + 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)); + ret &= bfs_check(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); + free(xbase); + + return ret; +} + +/** Check the result of wordesc(). */ +static bool check_wordesc(const char *str, const char *exp, enum wesc_flags flags) { + char buf[256]; + char *end = buf + sizeof(buf); + char *esc = wordesc(buf, end, str, flags); + + return bfs_check(esc != end) + && bfs_check(strcmp(buf, exp) == 0, "wordesc('%s') == '%s' (!= '%s')", str, buf, exp); +} + +bool check_bfstd(void) { + bool ret = true; + + ret &= bfs_check(asciilen("") == 0); + ret &= bfs_check(asciilen("@") == 1); + ret &= bfs_check(asciilen("@@") == 2); + ret &= bfs_check(asciilen("\xFF@") == 0); + ret &= bfs_check(asciilen("@\xFF") == 1); + ret &= bfs_check(asciilen("@@@@@@@@") == 8); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@") == 16); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@@@@@@@@@") == 24); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@a\xFF@@@@@@@") == 15); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@\xFF@@@@@@@") == 16); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@a\xFF@@@@@@") == 17); + ret &= bfs_check(asciilen("@@@@@@@\xFF@@@@@@a\xFF@@@@@@@") == 7); + ret &= bfs_check(asciilen("@@@@@@@@\xFF@@@@@a\xFF@@@@@@@") == 8); + ret &= bfs_check(asciilen("@@@@@@@@@\xFF@@@@a\xFF@@@@@@@") == 9); + + // From man 3p basename + ret &= check_base_dir("usr", ".", "usr"); + ret &= check_base_dir("usr/", ".", "usr"); + ret &= check_base_dir("", ".", "."); + ret &= check_base_dir("/", "/", "/"); + // check_base_dir("//", "/" or "//", "/" or "//"); + ret &= check_base_dir("///", "/", "/"); + ret &= check_base_dir("/usr/", "/", "usr"); + ret &= check_base_dir("/usr/lib", "/usr", "lib"); + ret &= check_base_dir("//usr//lib//", "//usr", "lib"); + ret &= check_base_dir("/home//dwc//test", "/home//dwc", "test"); + + ret &= check_wordesc("", "\"\"", WESC_SHELL); + ret &= check_wordesc("word", "word", WESC_SHELL); + ret &= check_wordesc("two words", "\"two words\"", WESC_SHELL); + ret &= check_wordesc("word's", "\"word's\"", WESC_SHELL); + ret &= check_wordesc("\"word\"", "'\"word\"'", WESC_SHELL); + ret &= check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL); + ret &= check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("~user", "\"~user\"", WESC_SHELL); + + const char *charmap = nl_langinfo(CODESET); + if (strcmp(charmap, "UTF-8") == 0) { + ret &= check_wordesc("\xF0", "$'\\xF0'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xCB\x9Cuser", "\xCB\x9Cuser", WESC_SHELL); + } + + ret &= bfs_check(xstrwidth("Hello world") == 11); + ret &= bfs_check(xstrwidth("Hello\1world") == 10); + + return ret; +} diff --git a/tests/bit.c b/tests/bit.c new file mode 100644 index 0000000..674d1b2 --- /dev/null +++ b/tests/bit.c @@ -0,0 +1,141 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "bit.h" +#include "diag.h" +#include <limits.h> +#include <stdint.h> +#include <string.h> + +bfs_static_assert(UMAX_WIDTH(0x1) == 1); +bfs_static_assert(UMAX_WIDTH(0x3) == 2); +bfs_static_assert(UMAX_WIDTH(0x7) == 3); +bfs_static_assert(UMAX_WIDTH(0xF) == 4); +bfs_static_assert(UMAX_WIDTH(0xFF) == 8); +bfs_static_assert(UMAX_WIDTH(0xFFF) == 12); +bfs_static_assert(UMAX_WIDTH(0xFFFF) == 16); + +#define UWIDTH_MAX(n) (2 * ((UINTMAX_C(1) << ((n) - 1)) - 1) + 1) +#define IWIDTH_MAX(n) UWIDTH_MAX((n) - 1) +#define IWIDTH_MIN(n) (-(intmax_t)IWIDTH_MAX(n) - 1) + +bfs_static_assert(UCHAR_MAX == UWIDTH_MAX(UCHAR_WIDTH)); +bfs_static_assert(SCHAR_MIN == IWIDTH_MIN(SCHAR_WIDTH)); +bfs_static_assert(SCHAR_MAX == IWIDTH_MAX(SCHAR_WIDTH)); + +bfs_static_assert(USHRT_MAX == UWIDTH_MAX(USHRT_WIDTH)); +bfs_static_assert(SHRT_MIN == IWIDTH_MIN(SHRT_WIDTH)); +bfs_static_assert(SHRT_MAX == IWIDTH_MAX(SHRT_WIDTH)); + +bfs_static_assert(UINT_MAX == UWIDTH_MAX(UINT_WIDTH)); +bfs_static_assert(INT_MIN == IWIDTH_MIN(INT_WIDTH)); +bfs_static_assert(INT_MAX == IWIDTH_MAX(INT_WIDTH)); + +bfs_static_assert(ULONG_MAX == UWIDTH_MAX(ULONG_WIDTH)); +bfs_static_assert(LONG_MIN == IWIDTH_MIN(LONG_WIDTH)); +bfs_static_assert(LONG_MAX == IWIDTH_MAX(LONG_WIDTH)); + +bfs_static_assert(ULLONG_MAX == UWIDTH_MAX(ULLONG_WIDTH)); +bfs_static_assert(LLONG_MIN == IWIDTH_MIN(LLONG_WIDTH)); +bfs_static_assert(LLONG_MAX == IWIDTH_MAX(LLONG_WIDTH)); + +bfs_static_assert(SIZE_MAX == UWIDTH_MAX(SIZE_WIDTH)); +bfs_static_assert(PTRDIFF_MIN == IWIDTH_MIN(PTRDIFF_WIDTH)); +bfs_static_assert(PTRDIFF_MAX == IWIDTH_MAX(PTRDIFF_WIDTH)); + +bfs_static_assert(UINTPTR_MAX == UWIDTH_MAX(UINTPTR_WIDTH)); +bfs_static_assert(INTPTR_MIN == IWIDTH_MIN(INTPTR_WIDTH)); +bfs_static_assert(INTPTR_MAX == IWIDTH_MAX(INTPTR_WIDTH)); + +bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); +bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); +bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); + +#define check_eq(a, b) \ + bfs_check((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) + +bool check_bit(void) { + bool ret = true; + + const char *str = "\x1\x2\x3\x4"; + uint32_t word; + memcpy(&word, str, sizeof(word)); + +#if ENDIAN_NATIVE == ENDIAN_LITTLE + ret &= check_eq(word, 0x04030201); +#elif ENDIAN_NATIVE == ENDIAN_BIG + ret &= check_eq(word, 0x01020304); +#else +# warning "Skipping byte order tests on mixed/unknown-endian machine" +#endif + + ret &= check_eq(bswap((uint8_t)0x12), 0x12); + ret &= check_eq(bswap((uint16_t)0x1234), 0x3412); + ret &= check_eq(bswap((uint32_t)0x12345678), 0x78563412); + ret &= check_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412); + + ret &= check_eq(count_ones(0x0), 0); + ret &= check_eq(count_ones(0x1), 1); + ret &= check_eq(count_ones(0x2), 1); + ret &= check_eq(count_ones(0x3), 2); + ret &= check_eq(count_ones(0x137F), 10); + + ret &= check_eq(count_zeros(0), INT_WIDTH); + ret &= check_eq(count_zeros(0L), LONG_WIDTH); + ret &= check_eq(count_zeros(0LL), LLONG_WIDTH); + ret &= check_eq(count_zeros((uint8_t)0), 8); + ret &= check_eq(count_zeros((uint16_t)0), 16); + ret &= check_eq(count_zeros((uint32_t)0), 32); + ret &= check_eq(count_zeros((uint64_t)0), 64); + + ret &= check_eq(rotate_left((uint8_t)0xA1, 4), 0x1A); + ret &= check_eq(rotate_left((uint16_t)0x1234, 12), 0x4123); + ret &= check_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345); + ret &= check_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678); + + ret &= check_eq(rotate_right((uint8_t)0xA1, 4), 0x1A); + ret &= check_eq(rotate_right((uint16_t)0x1234, 12), 0x2341); + ret &= check_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123); + ret &= check_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678); + + for (int i = 0; i < 16; ++i) { + uint16_t n = (uint16_t)1 << i; + for (int j = i; j < 16; ++j) { + uint16_t m = (uint16_t)1 << j; + uint16_t nm = n | m; + ret &= check_eq(count_ones(nm), 1 + (n != m)); + ret &= check_eq(count_zeros(nm), 15 - (n != m)); + ret &= check_eq(leading_zeros(nm), 15 - j); + ret &= check_eq(trailing_zeros(nm), i); + ret &= check_eq(first_leading_one(nm), j + 1); + ret &= check_eq(first_trailing_one(nm), i + 1); + ret &= check_eq(bit_width(nm), j + 1); + ret &= check_eq(bit_floor(nm), m); + if (n == m) { + ret &= check_eq(bit_ceil(nm), m); + ret &= bfs_check(has_single_bit(nm)); + } else { + if (j < 15) { + ret &= check_eq(bit_ceil(nm), (m << 1)); + } + ret &= bfs_check(!has_single_bit(nm)); + } + } + } + + ret &= check_eq(leading_zeros((uint16_t)0), 16); + ret &= check_eq(trailing_zeros((uint16_t)0), 16); + ret &= check_eq(first_leading_one(0), 0); + ret &= check_eq(first_trailing_one(0), 0); + ret &= check_eq(bit_width(0), 0); + ret &= check_eq(bit_floor(0), 0); + ret &= check_eq(bit_ceil(0), 1); + + ret &= bfs_check(!has_single_bit(0)); + ret &= bfs_check(!has_single_bit(UINT32_MAX)); + ret &= bfs_check(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1))); + + return ret; +} diff --git a/tests/bsd/L_acl.out b/tests/bsd/L_acl.out index 1dae00a..dd89800 100644 --- a/tests/bsd/L_acl.out +++ b/tests/bsd/L_acl.out @@ -1,2 +1,2 @@ -scratch/acl -scratch/link +./acl +./link diff --git a/tests/bsd/L_acl.sh b/tests/bsd/L_acl.sh index cf573e4..a3fcbc8 100644 --- a/tests/bsd/L_acl.sh +++ b/tests/bsd/L_acl.sh @@ -1,9 +1,9 @@ -clean_scratch +cd "$TEST" -skip_unless invoke_bfs scratch -quit -acl +invoke_bfs . -quit -acl || skip -"$XTOUCH" scratch/{normal,acl} -skip_unless set_acl scratch/acl -ln -s acl scratch/link +"$XTOUCH" normal acl +set_acl acl || skip +ln -s acl link -bfs_diff -L scratch -acl +bfs_diff -L . -acl diff --git a/tests/bsd/L_xattr.out b/tests/bsd/L_xattr.out index 12fac95..21eb50f 100644 --- a/tests/bsd/L_xattr.out +++ b/tests/bsd/L_xattr.out @@ -1,3 +1,3 @@ -scratch/link -scratch/xattr -scratch/xattr_2 +./link +./xattr +./xattr_2 diff --git a/tests/bsd/L_xattr.sh b/tests/bsd/L_xattr.sh index 7c27e0d..f8b56d8 100644 --- a/tests/bsd/L_xattr.sh +++ b/tests/bsd/L_xattr.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs -bfs_diff -L scratch -xattr +invoke_bfs . -quit -xattr || skip +make_xattrs || skip +bfs_diff -L . -xattr diff --git a/tests/bsd/L_xattrname.out b/tests/bsd/L_xattrname.out index 4dc4836..9e4c172 100644 --- a/tests/bsd/L_xattrname.out +++ b/tests/bsd/L_xattrname.out @@ -1,2 +1,2 @@ -scratch/link -scratch/xattr +./link +./xattr diff --git a/tests/bsd/L_xattrname.sh b/tests/bsd/L_xattrname.sh index 39d6a77..8108d57 100644 --- a/tests/bsd/L_xattrname.sh +++ b/tests/bsd/L_xattrname.sh @@ -1,11 +1,11 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs +invoke_bfs . -quit -xattr || skip +make_xattrs || skip case "$UNAME" in Darwin|FreeBSD) - bfs_diff -L scratch -xattrname bfs_test + bfs_diff -L . -xattrname bfs_test ;; *) - bfs_diff -L scratch -xattrname security.bfs_test + bfs_diff -L . -xattrname security.bfs_test ;; esac diff --git a/tests/bsd/X.sh b/tests/bsd/X.sh index 03d9eee..54000cf 100644 --- a/tests/bsd/X.sh +++ b/tests/bsd/X.sh @@ -1,2 +1 @@ -bfs_diff -X weirdnames -[ $? -eq $EX_BFS ] +! bfs_diff -X weirdnames diff --git a/tests/bsd/acl.out b/tests/bsd/acl.out index ddf8446..92e2f67 100644 --- a/tests/bsd/acl.out +++ b/tests/bsd/acl.out @@ -1 +1 @@ -scratch/acl +./acl diff --git a/tests/bsd/acl.sh b/tests/bsd/acl.sh index 1665684..a13c75f 100644 --- a/tests/bsd/acl.sh +++ b/tests/bsd/acl.sh @@ -1,9 +1,9 @@ -clean_scratch +cd "$TEST" -skip_unless invoke_bfs scratch -quit -acl +invoke_bfs . -quit -acl || skip -"$XTOUCH" scratch/{normal,acl} -skip_unless set_acl scratch/acl -ln -s acl scratch/link +"$XTOUCH" normal acl +set_acl acl || skip +ln -s acl link -bfs_diff scratch -acl +bfs_diff . -acl diff --git a/tests/bsd/exit.sh b/tests/bsd/exit.sh index 524a75f..248349c 100644 --- a/tests/bsd/exit.sh +++ b/tests/bsd/exit.sh @@ -1,11 +1,5 @@ -invoke_bfs basic -name foo -exit 42 -if [ $? -ne 42 ]; then - return 1 -fi +check_exit 42 invoke_bfs basic -name foo -exit 42 -invoke_bfs basic -name qux -exit 42 -if [ $? -ne 0 ]; then - return 1 -fi +check_exit 0 invoke_bfs basic -name qux -exit 42 bfs_diff basic/g -print -name g -exit diff --git a/tests/bsd/f_incomplete.sh b/tests/bsd/f_incomplete.sh index acb63af..0dfb19f 100644 --- a/tests/bsd/f_incomplete.sh +++ b/tests/bsd/f_incomplete.sh @@ -1,2 +1 @@ -fail invoke_bfs -f - +! invoke_bfs -f diff --git a/tests/bsd/flags.out b/tests/bsd/flags.out index 11998ed..3216ff5 100644 --- a/tests/bsd/flags.out +++ b/tests/bsd/flags.out @@ -1 +1 @@ -scratch/bar +./bar diff --git a/tests/bsd/flags.sh b/tests/bsd/flags.sh index ffb1cc2..eb9bc22 100644 --- a/tests/bsd/flags.sh +++ b/tests/bsd/flags.sh @@ -1,8 +1,8 @@ -skip_unless invoke_bfs scratch -quit -flags offline +invoke_bfs . -quit -flags offline || skip -clean_scratch +cd "$TEST" -"$XTOUCH" scratch/{foo,bar} -skip_unless chflags offline scratch/bar +"$XTOUCH" foo bar +chflags offline bar || skip -bfs_diff scratch -flags -offline,nohidden +bfs_diff . -flags -offline,nohidden diff --git a/tests/bsd/mtime_bad_unit.sh b/tests/bsd/mtime_bad_unit.sh index 3921f80..6e2caf1 100644 --- a/tests/bsd/mtime_bad_unit.sh +++ b/tests/bsd/mtime_bad_unit.sh @@ -1 +1 @@ -fail invoke_bfs times -mtime +1q +! invoke_bfs times -mtime +1q diff --git a/tests/bsd/mtime_missing_unit.sh b/tests/bsd/mtime_missing_unit.sh index 3ac4c97..f6b1f93 100644 --- a/tests/bsd/mtime_missing_unit.sh +++ b/tests/bsd/mtime_missing_unit.sh @@ -1 +1 @@ -fail invoke_bfs times -mtime +1w2 +! invoke_bfs times -mtime +1w2 diff --git a/tests/bsd/rm.out b/tests/bsd/rm.out index fb188b9..9c558e3 100644 --- a/tests/bsd/rm.out +++ b/tests/bsd/rm.out @@ -1 +1 @@ -scratch +. diff --git a/tests/bsd/rm.sh b/tests/bsd/rm.sh index 9ee2b0a..595d514 100644 --- a/tests/bsd/rm.sh +++ b/tests/bsd/rm.sh @@ -1,6 +1,4 @@ -clean_scratch -"$XTOUCH" -p scratch/foo/bar/baz - -(cd scratch && invoke_bfs . -rm) - -bfs_diff scratch +cd "$TEST" +"$XTOUCH" -p foo/bar/baz +invoke_bfs . -rm +bfs_diff . diff --git a/tests/bsd/s_quit.out b/tests/bsd/s_quit.out new file mode 100644 index 0000000..5ea492b --- /dev/null +++ b/tests/bsd/s_quit.out @@ -0,0 +1 @@ +basic/j/foo diff --git a/tests/bsd/s_quit.sh b/tests/bsd/s_quit.sh new file mode 100644 index 0000000..6bd55ab --- /dev/null +++ b/tests/bsd/s_quit.sh @@ -0,0 +1,4 @@ +# Regression test: bfs -S ids -s -name foo -quit would not actually quit, +# ending up in a confused state and erroring/crashing + +bfs_diff -s basic -name foo -print -quit diff --git a/tests/bsd/sparse.out b/tests/bsd/sparse.out new file mode 100644 index 0000000..52dcf49 --- /dev/null +++ b/tests/bsd/sparse.out @@ -0,0 +1 @@ +mnt/sparse diff --git a/tests/bsd/sparse.sh b/tests/bsd/sparse.sh new file mode 100644 index 0000000..7fcdeed --- /dev/null +++ b/tests/bsd/sparse.sh @@ -0,0 +1,12 @@ +test "$UNAME" = "Linux" || skip + +cd "$TEST" +mkdir mnt + +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt + +truncate -s 1M mnt/sparse +dd if=/dev/zero of=mnt/dense bs=1M count=1 + +bfs_diff mnt -type f -sparse diff --git a/tests/bsd/type_w.out b/tests/bsd/type_w.out new file mode 100644 index 0000000..a20a4f3 --- /dev/null +++ b/tests/bsd/type_w.out @@ -0,0 +1,34 @@ +1: -rw-r--r-- mnt/lower/bar +1: -rw-r--r-- mnt/lower/baz +1: -rw-r--r-- mnt/lower/foo +1: -rw-r--r-- mnt/upper/baz/qux +1: -rw-r--r-- mnt/upper/foo +1: drwxr-xr-x mnt/lower +1: drwxr-xr-x mnt/upper +1: drwxr-xr-x mnt/upper/baz +2: w--------- mnt/upper/bar +3: -rw-r--r-- mnt/lower/bar +3: -rw-r--r-- mnt/lower/baz +3: -rw-r--r-- mnt/lower/foo +3: -rw-r--r-- mnt/upper/baz/qux +3: -rw-r--r-- mnt/upper/foo +3: drwxr-xr-x mnt/lower +3: drwxr-xr-x mnt/upper +3: drwxr-xr-x mnt/upper/baz +3: w--------- mnt/upper/bar +4: -rw-r--r-- mnt/lower/bar +4: -rw-r--r-- mnt/lower/baz +4: -rw-r--r-- mnt/lower/foo +4: -rw-r--r-- mnt/upper/baz/qux +4: drwxr-xr-x mnt/lower +4: drwxr-xr-x mnt/upper +4: drwxr-xr-x mnt/upper/baz +5: w--------- mnt/upper/bar +6: -rw-r--r-- mnt/lower/bar +6: -rw-r--r-- mnt/lower/baz +6: -rw-r--r-- mnt/lower/foo +6: -rw-r--r-- mnt/upper/baz/qux +6: drwxr-xr-x mnt/lower +6: drwxr-xr-x mnt/upper +6: drwxr-xr-x mnt/upper/baz +6: w--------- mnt/upper/bar diff --git a/tests/bsd/type_w.sh b/tests/bsd/type_w.sh new file mode 100644 index 0000000..3aa50d5 --- /dev/null +++ b/tests/bsd/type_w.sh @@ -0,0 +1,56 @@ +# Only ffs supports whiteouts on FreeBSD +command -v mdconfig &>/dev/null || skip +command -v newfs &>/dev/null || skip + +cd "$TEST" + +# Create a ramdisk +if command -v truncate &>/dev/null; then + truncate -s1M img +else + dd if=/dev/zero of=img bs=1k count=1k +fi +md=$(bfs_sudo mdconfig img) || skip +defer bfs_sudo mdconfig -du "$md" + +# Make an ffs filesystem +bfs_sudo newfs -n "/dev/$md" >&2 || skip +mkdir mnt + +# Mount it +bfs_sudo mount "/dev/$md" mnt || skip +defer bfs_sudo umount mnt + +# Make it owned by us +bfs_sudo chown "$(id -u):$(id -g)" mnt +"$XTOUCH" -p mnt/{lower/{foo,bar,baz},upper/{bar,baz/qux}} + +# Mount a union filesystem within it +bfs_sudo mount -t unionfs -o below mnt/{lower,upper} +defer bfs_sudo umount mnt/upper + +# Create a whiteout +rm mnt/upper/bar + +# FreeBSD find doesn't have -printf, so munge -ls output +munge_ls() { + sed -En 's|.*([-drwx]{10}).*(mnt/.*)|'"$1"': \1 \2|p' +} + +# Do a few tests in one +{ + # Normally, we shouldn't see the whiteouts + invoke_bfs mnt -ls | munge_ls 1 + # -type w adds whiteouts to the output + invoke_bfs mnt -type w -ls | munge_ls 2 + # So this is not the same as test 1 + invoke_bfs mnt \( -type w -or -not -type w \) -ls | munge_ls 3 + # Unmount the unionfs + pop_defer + # Now repeat the same tests + invoke_bfs mnt -ls | munge_ls 4 + invoke_bfs mnt -type w -ls | munge_ls 5 + invoke_bfs mnt \( -type w -or -not -type w \) -ls | munge_ls 6 +} >"$OUT" +sort_output +diff_output diff --git a/tests/bsd/xattr.out b/tests/bsd/xattr.out index 109e7c9..0afed35 100644 --- a/tests/bsd/xattr.out +++ b/tests/bsd/xattr.out @@ -1,3 +1,3 @@ -scratch/xattr -scratch/xattr_2 -scratch/xattr_link +./xattr +./xattr_2 +./xattr_link diff --git a/tests/bsd/xattr.sh b/tests/bsd/xattr.sh index 727c220..68f729a 100644 --- a/tests/bsd/xattr.sh +++ b/tests/bsd/xattr.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs -bfs_diff scratch -xattr +invoke_bfs . -quit -xattr || skip +make_xattrs || skip +bfs_diff . -xattr diff --git a/tests/bsd/xattrname.out b/tests/bsd/xattrname.out index 0285ac1..ef732bd 100644 --- a/tests/bsd/xattrname.out +++ b/tests/bsd/xattrname.out @@ -1,2 +1,2 @@ -scratch/xattr -scratch/xattr_link +./xattr +./xattr_link diff --git a/tests/bsd/xattrname.sh b/tests/bsd/xattrname.sh index 6a0fe7e..38b111a 100644 --- a/tests/bsd/xattrname.sh +++ b/tests/bsd/xattrname.sh @@ -1,11 +1,11 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs +invoke_bfs . -quit -xattr || skip +make_xattrs || skip case "$UNAME" in Darwin|FreeBSD) - bfs_diff scratch -xattrname bfs_test + bfs_diff . -xattrname bfs_test ;; *) - bfs_diff scratch -xattrname security.bfs_test + bfs_diff . -xattrname security.bfs_test ;; esac diff --git a/tests/color.sh b/tests/color.sh new file mode 100644 index 0000000..805d2b8 --- /dev/null +++ b/tests/color.sh @@ -0,0 +1,37 @@ +#!/hint/bash + +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD + +## Colored output + +# Common escape sequences +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' + +# Check if we should color output to the given fd +color_fd() { + [ -z "${NO_COLOR:-}" ] && [ -t "$1" ] +} + +# Cache the color status for std{out,err} +color_fd 1 && COLOR_STDOUT=1 || COLOR_STDOUT=0 +color_fd 2 && COLOR_STDERR=1 || COLOR_STDERR=0 + +# Save this in case the tests unset PATH +SED=$(command -v sed) + +# Filter out escape sequences if necessary +color() { + if color_fd 1; then + "$@" + else + "$@" | "$SED" $'s/\e\\[[^m]*m//g' + fi +} diff --git a/tests/common/L_ilname.sh b/tests/common/L_ilname.sh index cfb15a8..e0495ed 100644 --- a/tests/common/L_ilname.sh +++ b/tests/common/L_ilname.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -ilname PATTERN +invoke_bfs -quit -ilname PATTERN || skip bfs_diff -L links -ilname '[AQ]' diff --git a/tests/common/L_ls.sh b/tests/common/L_ls.sh index ced16c6..7ee2b44 100644 --- a/tests/common/L_ls.sh +++ b/tests/common/L_ls.sh @@ -1,2 +1 @@ -clean_scratch -invoke_bfs -L rainbow -ls >scratch/L_ls.out +invoke_bfs -L rainbow -ls >"$OUT" diff --git a/tests/common/L_mount.out b/tests/common/L_mount.out index 2e80082..788579d 100644 --- a/tests/common/L_mount.out +++ b/tests/common/L_mount.out @@ -1,5 +1,5 @@ -scratch -scratch/foo -scratch/foo/bar -scratch/foo/qux -scratch/mnt +. +./foo +./foo/bar +./foo/qux +./mnt diff --git a/tests/common/L_mount.sh b/tests/common/L_mount.sh index dad7e00..fd8042a 100644 --- a/tests/common/L_mount.sh +++ b/tests/common/L_mount.sh @@ -1,15 +1,13 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -ln -s ../mnt scratch/foo/bar -"$XTOUCH" scratch/mnt/baz -ln -s ../mnt/baz scratch/foo/qux +cd "$TEST" +mkdir foo mnt -bfs_diff -L scratch -mount -ret=$? +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -sudo umount scratch/mnt -return $ret +ln -s ../mnt foo/bar +"$XTOUCH" mnt/baz +ln -s ../mnt/baz foo/qux + +bfs_diff -L . -mount diff --git a/tests/common/delete.out b/tests/common/delete.out index fb188b9..9c558e3 100644 --- a/tests/common/delete.out +++ b/tests/common/delete.out @@ -1 +1 @@ -scratch +. diff --git a/tests/common/delete.sh b/tests/common/delete.sh index 89cf2a2..638f307 100644 --- a/tests/common/delete.sh +++ b/tests/common/delete.sh @@ -1,7 +1,4 @@ -clean_scratch -"$XTOUCH" -p scratch/foo/bar/baz - -# Don't try to delete '.' -(cd scratch && invoke_bfs . -delete) - -bfs_diff scratch +cd "$TEST" +"$XTOUCH" -p foo/bar/baz +invoke_bfs . -delete +bfs_diff . diff --git a/tests/common/delete_error.out b/tests/common/delete_error.out new file mode 100644 index 0000000..b6b6505 --- /dev/null +++ b/tests/common/delete_error.out @@ -0,0 +1,8 @@ +. +. +./baz +./baz +./baz/qux +./baz/qux +./foo +./foo/bar diff --git a/tests/common/delete_error.sh b/tests/common/delete_error.sh new file mode 100644 index 0000000..e6327f3 --- /dev/null +++ b/tests/common/delete_error.sh @@ -0,0 +1,9 @@ +cd "$TEST" + +"$XTOUCH" -p foo/bar baz/qux +chmod -w foo +defer chmod +w foo + +! invoke_bfs . -print -delete -print >"$OUT" || fail +sort_output +diff_output diff --git a/tests/common/delete_many.out b/tests/common/delete_many.out index fb188b9..9c558e3 100644 --- a/tests/common/delete_many.out +++ b/tests/common/delete_many.out @@ -1 +1 @@ -scratch +. diff --git a/tests/common/delete_many.sh b/tests/common/delete_many.sh index 6274319..48fe4c2 100644 --- a/tests/common/delete_many.sh +++ b/tests/common/delete_many.sh @@ -1,8 +1,8 @@ # Test for https://github.com/tavianator/bfs/issues/67 -clean_scratch -mkdir scratch/foo -"$XTOUCH" scratch/foo/{1..256} +cd "$TEST" +mkdir foo +"$XTOUCH" foo/{1..256} -invoke_bfs scratch/foo -delete -bfs_diff scratch +invoke_bfs foo -delete +bfs_diff . diff --git a/tests/gnu/empty.out b/tests/common/empty.out index a0f4b76..a0f4b76 100644 --- a/tests/gnu/empty.out +++ b/tests/common/empty.out diff --git a/tests/gnu/empty.sh b/tests/common/empty.sh index 95ee988..95ee988 100644 --- a/tests/gnu/empty.sh +++ b/tests/common/empty.sh diff --git a/tests/common/empty_error.out b/tests/common/empty_error.out new file mode 100644 index 0000000..da45e23 --- /dev/null +++ b/tests/common/empty_error.out @@ -0,0 +1,3 @@ +./bar +./baz +./qux diff --git a/tests/common/empty_error.sh b/tests/common/empty_error.sh new file mode 100644 index 0000000..7c8049c --- /dev/null +++ b/tests/common/empty_error.sh @@ -0,0 +1,7 @@ +cd "$TEST" + +"$XTOUCH" -p foo/ bar/ baz qux +chmod -r foo baz +defer chmod +r foo baz + +! bfs_diff . -empty diff --git a/tests/common/empty_special.out b/tests/common/empty_special.out new file mode 100644 index 0000000..fa35478 --- /dev/null +++ b/tests/common/empty_special.out @@ -0,0 +1,20 @@ +rainbow/[1m/[0m +rainbow/exec.sh +rainbow/file.dat +rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/mh1 +rainbow/mh2 +rainbow/ow +rainbow/sgid +rainbow/sticky +rainbow/sticky_ow +rainbow/sugid +rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/gnu/empty_special.sh b/tests/common/empty_special.sh index 31e9d2e..31e9d2e 100644 --- a/tests/gnu/empty_special.sh +++ b/tests/common/empty_special.sh diff --git a/tests/common/execdir_nonexistent.sh b/tests/common/execdir_nonexistent.sh index 5d116e5..0ec013c 100644 --- a/tests/common/execdir_nonexistent.sh +++ b/tests/common/execdir_nonexistent.sh @@ -1,5 +1,2 @@ -stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 - -bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print -(($? == EX_BFS)) +bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/common/execdir_ulimit.out b/tests/common/execdir_ulimit.out index 7f53982..bf52c09 100644 --- a/tests/common/execdir_ulimit.out +++ b/tests/common/execdir_ulimit.out @@ -1,3 +1,4 @@ +./. ./0 ./1 ./2 @@ -30,7 +31,6 @@ ./q ./r ./s -./scratch ./t ./u ./v diff --git a/tests/common/execdir_ulimit.sh b/tests/common/execdir_ulimit.sh index 8bd9edd..122c282 100644 --- a/tests/common/execdir_ulimit.sh +++ b/tests/common/execdir_ulimit.sh @@ -1,7 +1,6 @@ -clean_scratch -mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z -mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C +cd "$TEST" +mkdir -p a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z +mkdir -p a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C -closefrom 4 -ulimit -n 13 -bfs_diff scratch -execdir echo {} \; +ulimit -n $((NOPENFD + 10)) +bfs_diff . -execdir echo {} \; diff --git a/tests/common/ilname.sh b/tests/common/ilname.sh index 7ab0793..fc7e9e4 100644 --- a/tests/common/ilname.sh +++ b/tests/common/ilname.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -ilname PATTERN +invoke_bfs -quit -ilname PATTERN || skip bfs_diff links -ilname '[AQ]' diff --git a/tests/common/iname.sh b/tests/common/iname.sh index 8fcc443..c25a646 100644 --- a/tests/common/iname.sh +++ b/tests/common/iname.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -iname PATTERN +invoke_bfs -quit -iname PATTERN || skip bfs_diff basic -iname '*F*' diff --git a/tests/common/inum_bind_mount.out b/tests/common/inum_bind_mount.out index a520de3..ede8749 100644 --- a/tests/common/inum_bind_mount.out +++ b/tests/common/inum_bind_mount.out @@ -1,2 +1,2 @@ -scratch/bar -scratch/foo +./bar +./foo diff --git a/tests/common/inum_bind_mount.sh b/tests/common/inum_bind_mount.sh index e35ed4e..892713e 100644 --- a/tests/common/inum_bind_mount.sh +++ b/tests/common/inum_bind_mount.sh @@ -1,12 +1,9 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$UNAME" = "Linux" || skip -clean_scratch -"$XTOUCH" scratch/{foo,bar} -sudo mount --bind scratch/{foo,bar} +cd "$TEST" +"$XTOUCH" foo bar baz -bfs_diff scratch -inum "$(inum scratch/bar)" -ret=$? +bfs_sudo mount --bind foo bar || skip +defer bfs_sudo umount bar -sudo umount scratch/bar -return $ret +bfs_diff . -inum "$(inum bar)" diff --git a/tests/common/inum_mount.out b/tests/common/inum_mount.out index 99c7511..99fa01e 100644 --- a/tests/common/inum_mount.out +++ b/tests/common/inum_mount.out @@ -1 +1 @@ -scratch/mnt +./mnt diff --git a/tests/common/inum_mount.sh b/tests/common/inum_mount.sh index f9f4e2b..7facf57 100644 --- a/tests/common/inum_mount.sh +++ b/tests/common/inum_mount.sh @@ -1,12 +1,9 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt +cd "$TEST" +mkdir foo mnt -bfs_diff scratch -inum "$(inum scratch/mnt)" -ret=$? +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -sudo umount scratch/mnt -return $ret +bfs_diff . -inum "$(inum mnt)" diff --git a/tests/common/ipath.sh b/tests/common/ipath.sh index 9e6f8c5..7d05f31 100644 --- a/tests/common/ipath.sh +++ b/tests/common/ipath.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -ipath PATTERN +invoke_bfs -quit -ipath PATTERN || skip bfs_diff basic -ipath 'basic/*F*' diff --git a/tests/common/ls.sh b/tests/common/ls.sh index 85ca39c..bc50d90 100644 --- a/tests/common/ls.sh +++ b/tests/common/ls.sh @@ -1,2 +1 @@ -clean_scratch -invoke_bfs rainbow -ls >scratch/ls.out +invoke_bfs rainbow -ls >"$OUT" diff --git a/tests/common/maxdepth_incomplete.sh b/tests/common/maxdepth_incomplete.sh index 536dcf5..0bcb461 100644 --- a/tests/common/maxdepth_incomplete.sh +++ b/tests/common/maxdepth_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs basic -maxdepth +! invoke_bfs basic -maxdepth diff --git a/tests/common/mindepth_incomplete.sh b/tests/common/mindepth_incomplete.sh index 19a3b21..6f55a42 100644 --- a/tests/common/mindepth_incomplete.sh +++ b/tests/common/mindepth_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs basic -mindepth +! invoke_bfs basic -mindepth diff --git a/tests/common/mount.out b/tests/common/mount.out index f7839fb..6253434 100644 --- a/tests/common/mount.out +++ b/tests/common/mount.out @@ -1,4 +1,4 @@ -scratch -scratch/foo -scratch/foo/bar -scratch/mnt +. +./foo +./foo/bar +./mnt diff --git a/tests/common/mount.sh b/tests/common/mount.sh index 2732a68..c9abde5 100644 --- a/tests/common/mount.sh +++ b/tests/common/mount.sh @@ -1,13 +1,11 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -"$XTOUCH" scratch/foo/bar scratch/mnt/baz +cd "$TEST" +mkdir foo mnt -bfs_diff scratch -mount -ret=$? +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -sudo umount scratch/mnt -return $ret +"$XTOUCH" foo/bar mnt/baz + +bfs_diff . -mount diff --git a/tests/common/newermt.sh b/tests/common/newermt.sh index 3c5be68..e816b29 100644 --- a/tests/common/newermt.sh +++ b/tests/common/newermt.sh @@ -1 +1,3 @@ -bfs_diff times -newermt 1991-12-14T00:01 +bfs_diff times -newermt 1991-12-14T00:01 \ + -newermt "1991-12-14 01:01+01:00" \ + -newermt "19911213 20:31:00-0330" diff --git a/tests/common/samefile_wordesc.sh b/tests/common/samefile_wordesc.sh new file mode 100644 index 0000000..b5d158f --- /dev/null +++ b/tests/common/samefile_wordesc.sh @@ -0,0 +1,4 @@ +# Regression test: don't abort on incomplete UTF-8 sequences +export LC_ALL=$(locale -a | grep -Ei 'utf-?8$' | head -n1) +test -n "$LC_ALL" || skip +! invoke_bfs -samefile $'\xFA\xFA' diff --git a/tests/find-color.sh b/tests/find-color.sh index ecdd5af..47de2a2 100755 --- a/tests/find-color.sh +++ b/tests/find-color.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 Tavian Barnes <tavianator@tavianator.com> # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD set -e diff --git a/tests/getopts.sh b/tests/getopts.sh new file mode 100644 index 0000000..b987e49 --- /dev/null +++ b/tests/getopts.sh @@ -0,0 +1,179 @@ +#!/hint/bash + +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD + +## Argument parsing + +if command -v nproc &>/dev/null; then + JOBS=$(nproc) +else + JOBS=1 +fi +MAKE= +PATTERNS=() +SUDO=() +STOP=0 +CLEAN=1 +UPDATE=0 +VERBOSE_COMMANDS=0 +VERBOSE_ERRORS=0 +VERBOSE_SKIPPED=0 +VERBOSE_TESTS=0 + +# Print usage information +usage() { + local pad=$(printf "%*s" ${#0} "") + color cat <<EOF +Usage: ${GRN}$0${RST} + [${BLU}-j${RST}${BLD}N${RST}] [${BLU}--make${RST}=${BLD}MAKE${RST}] [${BLU}--bfs${RST}=${BLD}path/to/bfs${RST}] [${BLU}--sudo${RST}[=${BLD}COMMAND${RST}]] + [${BLU}--stop${RST}] [${BLU}--no-clean${RST}] [${BLU}--update${RST}] [${BLU}--verbose${RST}[=${BLD}LEVEL${RST}]] [${BLU}--help${RST}] + [${BLU}--posix${RST}] [${BLU}--bsd${RST}] [${BLU}--gnu${RST}] [${BLU}--all${RST}] [${BLD}TEST${RST} [${BLD}TEST${RST} ...]] + + ${BLU}-j${RST}${BLD}N${RST} + Run ${BLD}N${RST} tests in parallel (default: ${BLD}$JOBS${RST}) + + ${BLU}--make${RST}=${BLD}MAKE${RST} + Use the jobserver from ${BLD}MAKE${RST}, e.g. ${BLU}--make${RST}=${BLD}"make -j$JOBS"${RST} + + ${BLU}--bfs${RST}=${BLD}path/to/bfs${RST} + Set the path to the bfs executable to test (default: ${BLD}./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 +} + +# Parse the command line +parse_args() { + for arg; do + case "$arg" in + -j?*) + JOBS="${arg#-j}" + ;; + --make=*) + MAKE="${arg#*=}" + ;; + --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 + ;; + -*) + color printf "${RED}error:${RST} Unrecognized option '%s'.\n\n" "$arg" >&2 + usage >&2 + exit 1 + ;; + *) + PATTERNS+=("$arg") + ;; + esac + done + + read -a MAKE <<<"$MAKE" + + # Try to resolve the path to $BFS before we cd, while also supporting + # --bfs="./bin/bfs -S ids" + read -a BFS <<<"${BFS:-$BIN/find2fd}" + BFS[0]=$(_realpath "$(command -v "${BFS[0]}")") + + if ((${#PATTERNS[@]} == 0)); then + PATTERNS=("*") + fi + + TEST_CASES=() + ALL_TESTS=($(cd "$TESTS" && quote {posix,common,bsd,gnu,bfs}/*.sh)) + for TEST in "${ALL_TESTS[@]}"; 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 + color printf "${RED}error:${RST} No tests matched" >&2 + color printf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 + color printf ".\n\n" >&2 + usage >&2 + exit 1 + fi +} diff --git a/tests/gnu/L_delete.out b/tests/gnu/L_delete.out index ed0e9a1..7ed5f0d 100644 --- a/tests/gnu/L_delete.out +++ b/tests/gnu/L_delete.out @@ -1,2 +1,2 @@ -scratch -scratch/foo +. +./foo diff --git a/tests/gnu/L_delete.sh b/tests/gnu/L_delete.sh index 6ec167c..0559c49 100644 --- a/tests/gnu/L_delete.sh +++ b/tests/gnu/L_delete.sh @@ -1,9 +1,8 @@ -clean_scratch -mkdir scratch/foo -mkdir scratch/bar -ln -s ../foo scratch/bar/baz +cd "$TEST" +mkdir foo bar +ln -s ../foo bar/baz # Don't try to rmdir() a symlink -invoke_bfs -L scratch/bar -delete || return 1 +invoke_bfs -L bar -delete -bfs_diff scratch +bfs_diff . diff --git a/tests/gnu/L_loops_continue.sh b/tests/gnu/L_loops_continue.sh index 0244137..55aeb33 100644 --- a/tests/gnu/L_loops_continue.sh +++ b/tests/gnu/L_loops_continue.sh @@ -1,2 +1 @@ -bfs_diff -L loops -[ $? -eq $EX_BFS ] +! bfs_diff -L loops diff --git a/tests/gnu/daystart.sh b/tests/gnu/daystart.sh index 9799bca..9c3be1a 100644 --- a/tests/gnu/daystart.sh +++ b/tests/gnu/daystart.sh @@ -1 +1 @@ -bfs_diff basic -daystart -mtime 0 +TZ=WAT-1 bfs_diff basic -daystart -mtime 0 diff --git a/tests/gnu/daystart_twice.sh b/tests/gnu/daystart_twice.sh index 21b2c0f..edbf18d 100644 --- a/tests/gnu/daystart_twice.sh +++ b/tests/gnu/daystart_twice.sh @@ -1 +1 @@ -bfs_diff basic -daystart -daystart -mtime 0 +TZ=WAT-1 bfs_diff basic -daystart -daystart -mtime 0 diff --git a/tests/gnu/empty_special.out b/tests/gnu/empty_special.out deleted file mode 100644 index 3927f2b..0000000 --- a/tests/gnu/empty_special.out +++ /dev/null @@ -1,14 +0,0 @@ -rainbow/exec.sh -rainbow/file.dat -rainbow/file.txt -rainbow/mh1 -rainbow/mh2 -rainbow/ow -rainbow/sgid -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz -rainbow/sticky -rainbow/sticky_ow -rainbow/sugid -rainbow/suid diff --git a/tests/gnu/exec_flush_fail.sh b/tests/gnu/exec_flush_fail.sh index 4772a14..5505f7a 100644 --- a/tests/gnu/exec_flush_fail.sh +++ b/tests/gnu/exec_flush_fail.sh @@ -1,3 +1,3 @@ # Failure to flush streams before exec should be caught -skip_unless test -e /dev/full -fail invoke_bfs basic -print0 -exec true \; >/dev/full +test -e /dev/full || skip +! invoke_bfs basic -print0 -exec true \; >/dev/full diff --git a/tests/gnu/exec_nothing.sh b/tests/gnu/exec_nothing.sh index 9d613e8..443aa0d 100644 --- a/tests/gnu/exec_nothing.sh +++ b/tests/gnu/exec_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't segfault on missing command -fail invoke_bfs basic -exec \; +! invoke_bfs basic -exec \; diff --git a/tests/gnu/exec_plus_flush_fail.sh b/tests/gnu/exec_plus_flush_fail.sh index 5c74fd8..53a50e5 100644 --- a/tests/gnu/exec_plus_flush_fail.sh +++ b/tests/gnu/exec_plus_flush_fail.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full -fail invoke_bfs basic/a -print0 -exec echo found {} + >/dev/full +test -e /dev/full || skip +! invoke_bfs basic/a -print0 -exec echo found {} + >/dev/full diff --git a/tests/gnu/execdir_path_dot.sh b/tests/gnu/execdir_path_dot.sh new file mode 100644 index 0000000..632dbb4 --- /dev/null +++ b/tests/gnu/execdir_path_dot.sh @@ -0,0 +1 @@ +! PATH=".:$PATH" invoke_bfs basic -execdir echo {} + diff --git a/tests/gnu/execdir_path_empty.sh b/tests/gnu/execdir_path_empty.sh new file mode 100644 index 0000000..eda6b1c --- /dev/null +++ b/tests/gnu/execdir_path_empty.sh @@ -0,0 +1 @@ +! PATH=":$PATH" invoke_bfs basic -execdir echo {} + diff --git a/tests/gnu/execdir_path_relative.sh b/tests/gnu/execdir_path_relative.sh new file mode 100644 index 0000000..69899ad --- /dev/null +++ b/tests/gnu/execdir_path_relative.sh @@ -0,0 +1 @@ +! PATH="foo:$PATH" invoke_bfs basic -execdir echo {} + diff --git a/tests/gnu/execdir_ulimit.out b/tests/gnu/execdir_ulimit.out new file mode 100644 index 0000000..6749f7d --- /dev/null +++ b/tests/gnu/execdir_ulimit.out @@ -0,0 +1,16 @@ +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE diff --git a/tests/gnu/execdir_ulimit.sh b/tests/gnu/execdir_ulimit.sh new file mode 100644 index 0000000..e14e716 --- /dev/null +++ b/tests/gnu/execdir_ulimit.sh @@ -0,0 +1,2 @@ +ulimit -Sn 64 +bfs_diff deep -type f -execdir bash -c 'printf "%d %s\n" $(ulimit -Sn) "$1"' bash {} \; diff --git a/tests/gnu/files0_from_empty.sh b/tests/gnu/files0_from_empty.sh index bd4fbf4..85eee8f 100644 --- a/tests/gnu/files0_from_empty.sh +++ b/tests/gnu/files0_from_empty.sh @@ -1 +1 @@ -printf "\0" | fail invoke_bfs -files0-from - +! printf "\0" | invoke_bfs -files0-from - diff --git a/tests/gnu/files0_from_error.sh b/tests/gnu/files0_from_error.sh index ab27ea2..1515d0b 100644 --- a/tests/gnu/files0_from_error.sh +++ b/tests/gnu/files0_from_error.sh @@ -1 +1 @@ -fail invoke_bfs -files0-from basic +! invoke_bfs -files0-from basic diff --git a/tests/gnu/files0_from_file.sh b/tests/gnu/files0_from_file.sh index 089a20e..81435a0 100644 --- a/tests/gnu/files0_from_file.sh +++ b/tests/gnu/files0_from_file.sh @@ -1,4 +1,4 @@ -clean_scratch +FILE="$TMP/$TEST.in" cd weirdnames -invoke_bfs -mindepth 1 -fprintf ../scratch/files0.in "%P\0" -bfs_diff -files0-from ../scratch/files0.in +invoke_bfs -mindepth 1 -fprintf "$FILE" "%P\0" +bfs_diff -files0-from "$FILE" diff --git a/tests/gnu/files0_from_none.out b/tests/gnu/files0_from_none.out new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/gnu/files0_from_none.out diff --git a/tests/gnu/files0_from_none.sh b/tests/gnu/files0_from_none.sh index c6e5b97..1633163 100644 --- a/tests/gnu/files0_from_none.sh +++ b/tests/gnu/files0_from_none.sh @@ -1 +1 @@ -printf "" | fail invoke_bfs -files0-from - +printf "" | bfs_diff -files0-from - diff --git a/tests/gnu/files0_from_nothing.sh b/tests/gnu/files0_from_nothing.sh index 5fdae60..fee50a8 100644 --- a/tests/gnu/files0_from_nothing.sh +++ b/tests/gnu/files0_from_nothing.sh @@ -1 +1 @@ -fail invoke_bfs -files0-from basic/nonexistent +! invoke_bfs -files0-from basic/nonexistent diff --git a/tests/gnu/files0_from_nowhere.sh b/tests/gnu/files0_from_nowhere.sh index 2337613..68eea4b 100644 --- a/tests/gnu/files0_from_nowhere.sh +++ b/tests/gnu/files0_from_nowhere.sh @@ -1 +1 @@ -fail invoke_bfs -files0-from +! invoke_bfs -files0-from diff --git a/tests/gnu/files0_from_ok.sh b/tests/gnu/files0_from_ok.sh index 5387e5c..8e145ce 100644 --- a/tests/gnu/files0_from_ok.sh +++ b/tests/gnu/files0_from_ok.sh @@ -1 +1 @@ -printf "basic\0" | fail invoke_bfs -files0-from - -ok echo {} \; +! printf "basic\0" | invoke_bfs -files0-from - -ok echo {} \; diff --git a/tests/gnu/fls.sh b/tests/gnu/fls.sh index a86fa20..d2ff794 100644 --- a/tests/gnu/fls.sh +++ b/tests/gnu/fls.sh @@ -1,2 +1 @@ -clean_scratch -invoke_bfs rainbow -fls scratch/fls.out +invoke_bfs rainbow -fls "$OUT" diff --git a/tests/gnu/fls_nonexistent.sh b/tests/gnu/fls_nonexistent.sh index 4756834..2854569 100644 --- a/tests/gnu/fls_nonexistent.sh +++ b/tests/gnu/fls_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs rainbow -fls scratch/nonexistent/path +! invoke_bfs rainbow -fls nonexistent/path diff --git a/tests/gnu/fprint0_nonexistent.sh b/tests/gnu/fprint0_nonexistent.sh index d8e0f30..4906081 100644 --- a/tests/gnu/fprint0_nonexistent.sh +++ b/tests/gnu/fprint0_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprint0 scratch/nonexistent/path +! invoke_bfs basic -fprint0 nonexistent/path diff --git a/tests/gnu/fprint_duplicate.sh b/tests/gnu/fprint_duplicate.sh index 5275502..8533b05 100644 --- a/tests/gnu/fprint_duplicate.sh +++ b/tests/gnu/fprint_duplicate.sh @@ -1,7 +1,7 @@ -"$XTOUCH" -p scratch/foo.out -ln scratch/foo.out scratch/foo.hard -ln -s foo.out scratch/foo.soft +"$XTOUCH" -p "$TEST/foo.out" +ln "$TEST/foo.out" "$TEST/foo.hard" +ln -s foo.out "$TEST/foo.soft" -invoke_bfs basic -fprint scratch/foo.out -fprint scratch/foo.hard -fprint scratch/foo.soft -sort scratch/foo.out >"$OUT" +invoke_bfs basic -fprint "$TEST/foo.out" -fprint "$TEST/foo.hard" -fprint "$TEST/foo.soft" +sort "$TEST/foo.out" >"$OUT" diff_output diff --git a/tests/gnu/fprint_error.sh b/tests/gnu/fprint_error.sh index e7f2394..7617034 100644 --- a/tests/gnu/fprint_error.sh +++ b/tests/gnu/fprint_error.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full -fail invoke_bfs basic -maxdepth 0 -fprint /dev/full +test -e /dev/full || skip +! invoke_bfs basic -maxdepth 0 -fprint /dev/full diff --git a/tests/gnu/fprint_noarg.sh b/tests/gnu/fprint_noarg.sh index bf772f3..8511649 100644 --- a/tests/gnu/fprint_noarg.sh +++ b/tests/gnu/fprint_noarg.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprint +! invoke_bfs basic -fprint diff --git a/tests/gnu/fprint_noerror.sh b/tests/gnu/fprint_noerror.sh index 142e935..f13a62b 100644 --- a/tests/gnu/fprint_noerror.sh +++ b/tests/gnu/fprint_noerror.sh @@ -1,3 +1,3 @@ # Regression test: /dev/full should not fail until actually written to -skip_unless test -e /dev/full +test -e /dev/full || skip invoke_bfs basic -false -fprint /dev/full diff --git a/tests/gnu/fprint_nonexistent.sh b/tests/gnu/fprint_nonexistent.sh index b6dac8a..2a403a2 100644 --- a/tests/gnu/fprint_nonexistent.sh +++ b/tests/gnu/fprint_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprint scratch/nonexistent/path +! invoke_bfs basic -fprint nonexistent/path diff --git a/tests/gnu/fprintf_nofile.sh b/tests/gnu/fprintf_nofile.sh index c2c48a5..4e79002 100644 --- a/tests/gnu/fprintf_nofile.sh +++ b/tests/gnu/fprintf_nofile.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprintf +! invoke_bfs basic -fprintf diff --git a/tests/gnu/fprintf_noformat.sh b/tests/gnu/fprintf_noformat.sh index 0d285c3..fd97f4c 100644 --- a/tests/gnu/fprintf_noformat.sh +++ b/tests/gnu/fprintf_noformat.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprintf /dev/null +! invoke_bfs basic -fprintf /dev/null diff --git a/tests/gnu/fprintf_nonexistent.sh b/tests/gnu/fprintf_nonexistent.sh index 6ed141a..b1eea10 100644 --- a/tests/gnu/fprintf_nonexistent.sh +++ b/tests/gnu/fprintf_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprintf scratch/nonexistent/path '%p\n' +! invoke_bfs basic -fprintf nonexistent/path '%p\n' diff --git a/tests/gnu/fstype.sh b/tests/gnu/fstype.sh index 939438e..05645c3 100644 --- a/tests/gnu/fstype.sh +++ b/tests/gnu/fstype.sh @@ -1,3 +1,2 @@ -fstype=$(invoke_bfs basic -maxdepth 0 -printf '%F\n') -skip_if test $? -ne 0 +fstype=$(invoke_bfs basic -maxdepth 0 -printf '%F\n') || skip bfs_diff basic -fstype "$fstype" diff --git a/tests/gnu/fstype_stacked.out b/tests/gnu/fstype_stacked.out new file mode 100644 index 0000000..c1e0e6c --- /dev/null +++ b/tests/gnu/fstype_stacked.out @@ -0,0 +1 @@ +mnt diff --git a/tests/gnu/fstype_stacked.sh b/tests/gnu/fstype_stacked.sh new file mode 100644 index 0000000..a9739bb --- /dev/null +++ b/tests/gnu/fstype_stacked.sh @@ -0,0 +1,12 @@ +test "$UNAME" = "Linux" || skip + +cd "$TEST" +mkdir mnt + +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt + +bfs_sudo mount -t ramfs ramfs mnt || skip +defer bfs_sudo umount mnt + +bfs_diff mnt -fstype ramfs -print -o -printf '%p: %F\n' diff --git a/tests/gnu/fstype_umount.out b/tests/gnu/fstype_umount.out new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/gnu/fstype_umount.out diff --git a/tests/gnu/fstype_umount.sh b/tests/gnu/fstype_umount.sh new file mode 100644 index 0000000..81c195f --- /dev/null +++ b/tests/gnu/fstype_umount.sh @@ -0,0 +1,12 @@ +test "$UNAME" = "Linux" || skip + +cd "$TEST" + +mkdir tmp +bfs_sudo mount -t tmpfs tmpfs tmp || skip +defer bfs_sudo umount -R tmp + +mkdir tmp/ram +bfs_sudo mount -t ramfs ramfs tmp/ram || skip + +bfs_diff tmp -path tmp -exec "${SUDO[@]}" umount tmp/ram \; , -fstype ramfs -print diff --git a/tests/gnu/gid_plus.sh b/tests/gnu/gid_plus.sh index 8ad493b..ccba0e6 100644 --- a/tests/gnu/gid_plus.sh +++ b/tests/gnu/gid_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -g)" -eq 0 +test "$(id -g)" -eq 0 && skip bfs_diff basic -gid +0 diff --git a/tests/gnu/gid_plus_plus.sh b/tests/gnu/gid_plus_plus.sh index 7982633..ec7ae86 100644 --- a/tests/gnu/gid_plus_plus.sh +++ b/tests/gnu/gid_plus_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -g)" -eq 0 +test "$(id -g)" -eq 0 && skip bfs_diff basic -gid ++0 diff --git a/tests/gnu/ignore_readdir_race.sh b/tests/gnu/ignore_readdir_race.sh index 6586bcc..75165f6 100644 --- a/tests/gnu/ignore_readdir_race.sh +++ b/tests/gnu/ignore_readdir_race.sh @@ -1,5 +1,5 @@ -clean_scratch -"$XTOUCH" scratch/{foo,bar} +cd "$TEST" +"$XTOUCH" foo bar # -links 1 forces a stat() call, which will fail for the second file -invoke_bfs scratch -mindepth 1 -ignore_readdir_race -links 1 -exec "$TESTS/remove-sibling.sh" {} \; +invoke_bfs . -mindepth 1 -ignore_readdir_race -links 1 -exec "$TESTS/remove-sibling.sh" {} \; diff --git a/tests/gnu/ignore_readdir_race_notdir.sh b/tests/gnu/ignore_readdir_race_notdir.sh index 5b8b56d..12e9fe6 100644 --- a/tests/gnu/ignore_readdir_race_notdir.sh +++ b/tests/gnu/ignore_readdir_race_notdir.sh @@ -1,5 +1,7 @@ # Check -ignore_readdir_race handling when a directory is replaced with a file -clean_scratch -"$XTOUCH" -p scratch/foo/bar +cd "$TEST" +mkdir foo -invoke_bfs scratch -mindepth 1 -ignore_readdir_race -execdir rm -r {} \; -execdir "$XTOUCH" {} \; +invoke_bfs . -mindepth 1 -ignore_readdir_race \ + -type d -execdir rmdir {} \; \ + -execdir "$XTOUCH" {} \; diff --git a/tests/gnu/ignore_readdir_race_root.sh b/tests/gnu/ignore_readdir_race_root.sh index bad7391..dc41e7f 100644 --- a/tests/gnu/ignore_readdir_race_root.sh +++ b/tests/gnu/ignore_readdir_race_root.sh @@ -1,2 +1,2 @@ # Make sure -ignore_readdir_race doesn't suppress ENOENT at the root -fail invoke_bfs basic/nonexistent -ignore_readdir_race +! invoke_bfs basic/nonexistent -ignore_readdir_race diff --git a/tests/gnu/inum_automount.out b/tests/gnu/inum_automount.out index 7b53ae3..3378e2d 100644 --- a/tests/gnu/inum_automount.out +++ b/tests/gnu/inum_automount.out @@ -1 +1 @@ -scratch/automnt +./automnt diff --git a/tests/gnu/inum_automount.sh b/tests/gnu/inum_automount.sh index 648ea05..86b23e1 100644 --- a/tests/gnu/inum_automount.sh +++ b/tests/gnu/inum_automount.sh @@ -1,17 +1,14 @@ # bfs shouldn't trigger automounts unless it descends into them -skip_unless test "$SUDO" -skip_unless command -v systemd-mount &>/dev/null +command -v systemd-mount &>/dev/null || skip -clean_scratch -mkdir scratch/{foo,automnt} -skip_unless sudo systemd-mount -A -o bind basic scratch/automnt +cd "$TEST" +mkdir foo automnt -before=$(inum scratch/automnt) -bfs_diff scratch -inum "$before" -prune -ret=$? -after=$(inum scratch/automnt) +bfs_sudo systemd-mount -A -o bind "$TMP/basic" automnt || skip +defer bfs_sudo systemd-umount automnt -sudo systemd-umount scratch/automnt - -((ret == 0 && before == after)) +before=$(inum automnt) +bfs_diff . -inum "$before" -prune +after=$(inum automnt) +((before == after)) diff --git a/tests/gnu/iwholename.sh b/tests/gnu/iwholename.sh index 67e9630..0b2d038 100644 --- a/tests/gnu/iwholename.sh +++ b/tests/gnu/iwholename.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -iwholename PATTERN +invoke_bfs -quit -iwholename PATTERN || skip bfs_diff basic -iwholename 'basic/*F*' diff --git a/tests/posix/newer_link.out b/tests/gnu/newer_link.out index d2dcdd1..d2dcdd1 100644 --- a/tests/posix/newer_link.out +++ b/tests/gnu/newer_link.out diff --git a/tests/posix/newer_link.sh b/tests/gnu/newer_link.sh index 685ac78..685ac78 100644 --- a/tests/posix/newer_link.sh +++ b/tests/gnu/newer_link.sh diff --git a/tests/gnu/ok_nothing.sh b/tests/gnu/ok_nothing.sh index 439687b..52c3547 100644 --- a/tests/gnu/ok_nothing.sh +++ b/tests/gnu/ok_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't segfault on missing command -fail invoke_bfs basic -ok \; +! invoke_bfs basic -ok \; diff --git a/tests/gnu/okdir_path_dot.sh b/tests/gnu/okdir_path_dot.sh new file mode 100644 index 0000000..5b40e27 --- /dev/null +++ b/tests/gnu/okdir_path_dot.sh @@ -0,0 +1 @@ +! PATH=".:$PATH" invoke_bfs basic -okdir echo {} \; diff --git a/tests/gnu/okdir_path_empty.sh b/tests/gnu/okdir_path_empty.sh new file mode 100644 index 0000000..2669ee8 --- /dev/null +++ b/tests/gnu/okdir_path_empty.sh @@ -0,0 +1 @@ +! PATH=":$PATH" invoke_bfs basic -okdir echo {} \; diff --git a/tests/gnu/okdir_path_relative.sh b/tests/gnu/okdir_path_relative.sh new file mode 100644 index 0000000..05100a1 --- /dev/null +++ b/tests/gnu/okdir_path_relative.sh @@ -0,0 +1 @@ +! PATH="foo:$PATH" invoke_bfs basic -okdir echo {} \; diff --git a/tests/gnu/print_error.sh b/tests/gnu/print_error.sh index 9fd5af5..bc79637 100644 --- a/tests/gnu/print_error.sh +++ b/tests/gnu/print_error.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full -fail invoke_bfs basic -maxdepth 0 >/dev/full +test -e /dev/full || skip +! invoke_bfs basic -maxdepth 0 >/dev/full diff --git a/tests/gnu/printf_Y_error.out b/tests/gnu/printf_Y_error.out index 410a9b5..1dd554e 100644 --- a/tests/gnu/printf_Y_error.out +++ b/tests/gnu/printf_Y_error.out @@ -1,3 +1,3 @@ -(scratch) () d d -(scratch/bar) (foo/bar) l ? -(scratch/foo) () d d +(.) () d d +(./bar) (foo/bar) l ? +(./foo) () d d diff --git a/tests/gnu/printf_Y_error.sh b/tests/gnu/printf_Y_error.sh index 6487711..d3130ce 100644 --- a/tests/gnu/printf_Y_error.sh +++ b/tests/gnu/printf_Y_error.sh @@ -1,12 +1,8 @@ -clean_scratch -mkdir scratch/foo -chmod -x scratch/foo -ln -s foo/bar scratch/bar +cd "$TEST" +mkdir foo +ln -s foo/bar bar -bfs_diff scratch -printf '(%p) (%l) %y %Y\n' -ret=$? +chmod -x foo +defer chmod +x foo -chmod +x scratch/foo -clean_scratch - -[ $ret -eq $EX_BFS ] +! bfs_diff . -printf '(%p) (%l) %y %Y\n' diff --git a/tests/gnu/printf_times.sh b/tests/gnu/printf_times.sh index d952620..e4f5155 100644 --- a/tests/gnu/printf_times.sh +++ b/tests/gnu/printf_times.sh @@ -1 +1 @@ -bfs_diff times -type f -printf '%p | %a %AY-%Am-%Ad %AH:%AI:%AS %T@ | %t %TY-%Tm-%Td %TH:%TI:%TS %T@\n' +bfs_diff times -type f -printf '%p | %a %AY-%Am-%Ad %AH:%AI:%AS %A@ | %t %TY-%Tm-%Td %TH:%TI:%TS %T@\n' diff --git a/tests/gnu/printf_u_g_ulimit.sh b/tests/gnu/printf_u_g_ulimit.sh index a84ee29..c621b9b 100644 --- a/tests/gnu/printf_u_g_ulimit.sh +++ b/tests/gnu/printf_u_g_ulimit.sh @@ -1,3 +1,2 @@ -closefrom 4 -ulimit -n 16 +ulimit -n $((NOPENFD + 13)) [ "$(invoke_bfs deep -printf '%u %g\n' | uniq)" = "$(id -un) $(id -gn)" ] diff --git a/tests/gnu/regex_error.sh b/tests/gnu/regex_error.sh index 9bd4c8d..4af933f 100644 --- a/tests/gnu/regex_error.sh +++ b/tests/gnu/regex_error.sh @@ -1 +1 @@ -fail invoke_bfs basic -regex '[' +! invoke_bfs basic -regex '[' diff --git a/tests/gnu/regex_invalid_utf8.out b/tests/gnu/regex_invalid_utf8.out index 03f3f58..a133b1a 100644 --- a/tests/gnu/regex_invalid_utf8.out +++ b/tests/gnu/regex_invalid_utf8.out @@ -1 +1 @@ -scratch/â„ +./â„ diff --git a/tests/gnu/regex_invalid_utf8.sh b/tests/gnu/regex_invalid_utf8.sh index edb4b1e..7006dcd 100644 --- a/tests/gnu/regex_invalid_utf8.sh +++ b/tests/gnu/regex_invalid_utf8.sh @@ -1,8 +1,8 @@ -clean_scratch +cd "$TEST" # Incomplete UTF-8 sequences -skip_unless touch scratch/$'\xC3' -skip_unless touch scratch/$'\xE2\x84' -skip_unless touch scratch/$'\xF0\x9F\x92' +touch $'\xC3' || skip +touch $'\xE2\x84' || skip +touch $'\xF0\x9F\x92' || skip -bfs_diff scratch -regex 'scratch/..' +bfs_diff . -regex '\./..' diff --git a/tests/gnu/regextype_emacs.sh b/tests/gnu/regextype_emacs.sh index d0f68cc..3cc388c 100644 --- a/tests/gnu/regextype_emacs.sh +++ b/tests/gnu/regextype_emacs.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs -regextype emacs -quit +invoke_bfs -regextype emacs -quit || skip bfs_diff basic -regextype emacs -regex '.*/\(f+o?o?\|bar\)' diff --git a/tests/gnu/regextype_grep.sh b/tests/gnu/regextype_grep.sh index 0136700..0830667 100644 --- a/tests/gnu/regextype_grep.sh +++ b/tests/gnu/regextype_grep.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs -regextype grep -quit +invoke_bfs -regextype grep -quit || skip bfs_diff basic -regextype grep -regex '.*/f\+o\?o\?' diff --git a/tests/gnu/uid_plus.sh b/tests/gnu/uid_plus.sh index fc4bce3..22b2c8e 100644 --- a/tests/gnu/uid_plus.sh +++ b/tests/gnu/uid_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -u)" -eq 0 +test "$(id -u)" -eq 0 && skip bfs_diff basic -uid +0 diff --git a/tests/gnu/uid_plus_plus.sh b/tests/gnu/uid_plus_plus.sh index 5d5e086..e021888 100644 --- a/tests/gnu/uid_plus_plus.sh +++ b/tests/gnu/uid_plus_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -u)" -eq 0 +test "$(id -u)" -eq 0 && skip bfs_diff basic -uid ++0 diff --git a/tests/gnu/used.out b/tests/gnu/used.out new file mode 100644 index 0000000..647621b --- /dev/null +++ b/tests/gnu/used.out @@ -0,0 +1,4 @@ +-used +7: ./nextyear +-used 1: ./tomorrow +-used 2: ./dayafter +-used 7: ./nextweek diff --git a/tests/gnu/used.sh b/tests/gnu/used.sh new file mode 100644 index 0000000..5e5d4e9 --- /dev/null +++ b/tests/gnu/used.sh @@ -0,0 +1,40 @@ +iso8601() { + printf '%04d-%02d-%02dT%02d:%02d:%02d\n' "$@" +} + +cd "$TEST" + +now=$(date '+%Y-%m-%dT%H:%M:%S') + +# Parse the current date +[[ "$now" =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})$ ]] || fail +# Treat leading zeros as decimal, not octal +YYYY=$((10#${BASH_REMATCH[1]})) +MM=$((10#${BASH_REMATCH[2]})) +DD=$((10#${BASH_REMATCH[3]})) +hh=$((10#${BASH_REMATCH[4]})) +mm=$((10#${BASH_REMATCH[5]})) +ss=$((10#${BASH_REMATCH[6]})) + +# -used is always false if atime < ctime +yesterday=$(iso8601 $YYYY $MM $((DD - 1)) $hh $mm $ss) +"$XTOUCH" -at "$yesterday" yesterday + +# -used rounds up +tomorrow=$(iso8601 $YYYY $MM $DD $((hh + 1)) $mm $ss) +"$XTOUCH" -at "$tomorrow" tomorrow + +dayafter=$(iso8601 $YYYY $MM $((DD + 1)) $((hh + 1)) $mm $ss) +"$XTOUCH" -at "$dayafter" dayafter + +nextweek=$(iso8601 $YYYY $MM $((DD + 6)) $((hh + 1)) $mm $ss) +"$XTOUCH" -at "$nextweek" nextweek + +nextyear=$(iso8601 $((YYYY + 1)) $MM $DD $hh $mm $ss) +"$XTOUCH" -at "$nextyear" nextyear + +bfs_diff -mindepth 1 \ + -a -used 1 -printf '-used 1: %p\n' \ + -o -used 2 -printf '-used 2: %p\n' \ + -o -used 7 -printf '-used 7: %p\n' \ + -o -used +7 -printf '-used +7: %p\n' diff --git a/tests/gnu/xtype_bind_mount.out b/tests/gnu/xtype_bind_mount.out index 16804ea..d18d706 100644 --- a/tests/gnu/xtype_bind_mount.out +++ b/tests/gnu/xtype_bind_mount.out @@ -1,2 +1,2 @@ -scratch/link -scratch/null +./link +./null diff --git a/tests/gnu/xtype_bind_mount.sh b/tests/gnu/xtype_bind_mount.sh index 264b6f8..35fb3f5 100644 --- a/tests/gnu/xtype_bind_mount.sh +++ b/tests/gnu/xtype_bind_mount.sh @@ -1,13 +1,10 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$UNAME" = "Linux" || skip -clean_scratch -"$XTOUCH" scratch/{file,null} -sudo mount --bind /dev/null scratch/null -ln -s /dev/null scratch/link +cd "$TEST" +"$XTOUCH" file null +ln -s /dev/null link -bfs_diff -L scratch -type c -ret=$? +bfs_sudo mount --bind /dev/null null || skip +defer bfs_sudo umount null -sudo umount scratch/null -return $ret +bfs_diff . -xtype c diff --git a/tests/ioq.c b/tests/ioq.c new file mode 100644 index 0000000..ef5ee3b --- /dev/null +++ b/tests/ioq.c @@ -0,0 +1,77 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "ioq.h" +#include "bfstd.h" +#include "diag.h" +#include "dir.h" +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> + +/** + * Test for blocking within ioq_slot_push(). + * + * struct ioqq only supports non-blocking reads; if a write encounters a full + * slot, it must block until someone pops from that slot: + * + * Reader Writer + * ────────────────────────── ───────────────────────── + * tail: 0 → 1 + * slots[0]: empty → full + * tail: 1 → 0 + * slots[1]: empty → full + * tail: 0 → 1 + * slots[0]: full → full* (IOQ_BLOCKED) + * ioq_slot_wait() ... + * head: 0 → 1 + * slots[0]: full* → empty + * ioq_slot_wake() + * ... + * slots[0]: empty → full + * + * To reproduce this unlikely scenario, we must fill up the ready queue, then + * call ioq_cancel() which pushes an additional sentinel IOQ_STOP operation. + */ +static void check_ioq_push_block(void) { + // Must be a power of two to fill the entire queue + const size_t depth = 2; + + struct ioq *ioq = ioq_create(depth, 1); + bfs_verify(ioq, "ioq_create(): %s", xstrerror(errno)); + + // 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)); + + int ret = ioq_opendir(ioq, dir, AT_FDCWD, ".", 0, NULL); + bfs_verify(ret == 0, "ioq_opendir(): %s", xstrerror(errno)); + } + bfs_verify(ioq_capacity(ioq) == 0); + + // Now cancel the queue, pushing an additional IOQ_STOP message + ioq_cancel(ioq); + + // Drain the queue + for (size_t i = 0; i < depth; ++i) { + struct ioq_ent *ent = ioq_pop(ioq, true); + bfs_verify(ent && ent->op == IOQ_OPENDIR); + + if (ent->result >= 0) { + bfs_closedir(ent->opendir.dir); + } + free(ent->opendir.dir); + ioq_free(ioq, ent); + } + bfs_verify(!ioq_pop(ioq, true)); + + ioq_destroy(ioq); +} + +bool check_ioq(void) { + check_ioq_push_block(); + return true; +} diff --git a/tests/ls-color.sh b/tests/ls-color.sh index c82a58d..b9a0402 100755 --- a/tests/ls-color.sh +++ b/tests/ls-color.sh @@ -1,36 +1,50 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 Tavian Barnes <tavianator@tavianator.com> # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD # Prints the "ground truth" coloring of a path using ls set -e +parse_ls_colors() { + for key; do + local -n var="$key" + if [[ "$LS_COLORS" =~ (^|:)$key=(([^:]|\\:)*) ]]; then + var="${BASH_REMATCH[2]}" + # Interpret escapes + var=$(printf "$var" | sed $'s/\^\[/\033/g; s/\\\\:/:/g') + fi + done +} + +re_escape() { + # https://stackoverflow.com/a/29613573/502399 + sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$1" +} + +rs=0 +lc=$'\033[' +rc=m +ec= +no= + +parse_ls_colors rs lc rc ec no +: "${ec:=$lc$rs$rc}" + +strip="(($(re_escape "$lc$no$rc"))?($(re_escape "$ec")|$(re_escape "$lc$rc")))+" + +ls_color() { + # Strip the leading reset sequence from the ls output + ls -1d --color "$@" | sed -E "s/^$strip([a-z].*)$strip/\4/; s/^$strip//" +} + L= if [ "$1" = "-L" ]; then L="$1" shift fi -function ls_color() { - # Strip the leading reset sequence from the ls output - ls -1d --color "$@" | sed $'s/^\033\\[0m//' -} - DIR="${1%/*}" if [ "$DIR" = "$1" ]; then ls_color "$1" diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..429772b --- /dev/null +++ b/tests/main.c @@ -0,0 +1,125 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +/** + * Entry point for unit tests. + */ + +#include "prelude.h" +#include "tests.h" +#include "bfstd.h" +#include "color.h" +#include <errno.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +/** + * Test context. + */ +struct test_ctx { + /** Number of command line arguments. */ + int argc; + /** The arguments themselves. */ + char **argv; + + /** Parsed colors. */ + struct colors *colors; + /** Colorized output stream. */ + CFILE *cout; + + /** Eventual exit status. */ + int ret; +}; + +/** Initialize the test context. */ +static int test_init(struct test_ctx *ctx, int argc, char **argv) { + ctx->argc = argc; + ctx->argv = argv; + + ctx->colors = parse_colors(); + ctx->cout = cfwrap(stdout, ctx->colors, false); + if (!ctx->cout) { + ctx->ret = EXIT_FAILURE; + return -1; + } + + ctx->ret = EXIT_SUCCESS; + return 0; +} + +/** Finalize the test context. */ +static int test_fini(struct test_ctx *ctx) { + if (ctx->cout) { + cfclose(ctx->cout); + } + + free_colors(ctx->colors); + + return ctx->ret; +} + +/** Check if a test case is enabled for this run. */ +static bool should_run(const struct test_ctx *ctx, const char *test) { + // Run all tests by default + if (ctx->argc < 2) { + return true; + } + + // With args, run only specified tests + for (int i = 1; i < ctx->argc; ++i) { + if (strcmp(test, ctx->argv[i]) == 0) { + return true; + } + } + + return false; +} + +/** Run a test if it's enabled. */ +static void run_test(struct test_ctx *ctx, const char *test, test_fn *fn) { + if (should_run(ctx, test)) { + if (fn()) { + cfprintf(ctx->cout, "${grn}[PASS]${rs} ${bld}%s${rs}\n", test); + } else { + cfprintf(ctx->cout, "${red}[FAIL]${rs} ${bld}%s${rs}\n", test); + ctx->ret = EXIT_FAILURE; + } + } +} + +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")) { + setlocale(LC_ALL, ""); + } + + // Run tests in UTC + if (setenv("TZ", "UTC0", true) != 0) { + perror("setenv()"); + return EXIT_FAILURE; + } + tzset(); + + struct test_ctx ctx; + if (test_init(&ctx, argc, argv) != 0) { + goto done; + } + + run_test(&ctx, "alloc", check_alloc); + run_test(&ctx, "bfstd", check_bfstd); + run_test(&ctx, "bit", check_bit); + run_test(&ctx, "ioq", check_ioq); + run_test(&ctx, "trie", check_trie); + run_test(&ctx, "xspawn", check_xspawn); + run_test(&ctx, "xtime", check_xtime); + +done: + return test_fini(&ctx); +} diff --git a/tests/mksock.c b/tests/mksock.c index d1776b3..5786cb6 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -1,29 +1,16 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019 Tavian Barnes <tavianator@tavianator.com> * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD /** * There's no standard Unix utility that creates a socket file, so this small * program does the job. */ +#include "bfstd.h" #include <errno.h> -#include <libgen.h> #include <stdio.h> -#include <string.h> #include <stdlib.h> +#include <string.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> @@ -32,7 +19,7 @@ * Print an error message. */ static void errmsg(const char *cmd, const char *path) { - fprintf(stderr, "%s: '%s': %s.\n", cmd, path, strerror(errno)); + fprintf(stderr, "%s: '%s': %s.\n", cmd, path, xstrerror(errno)); } /** @@ -41,18 +28,13 @@ static void errmsg(const char *cmd, const char *path) { * file name is not. */ static int chdir_parent(const char *path) { - char *copy = strdup(path); - if (!copy) { + char *dir = xdirname(path); + if (!dir) { return -1; } - const char *dir = dirname(copy); int ret = chdir(dir); - - int error = errno; - free(copy); - errno = error; - + free(dir); return ret; } @@ -66,22 +48,21 @@ static int init_sun(struct sockaddr_un *sock, const char *path) { return -1; } - char *copy = strdup(path); - if (!copy) { + char *base = xbasename(path); + if (!base) { return -1; } - const char *base = basename(copy); len = strlen(base); if (len >= sizeof(sock->sun_path)) { - free(copy); + free(base); errno = ENAMETOOLONG; return -1; } sock->sun_family = AF_UNIX; memcpy(sock->sun_path, base, len + 1); - free(copy); + free(base); return 0; } @@ -119,7 +100,7 @@ int main(int argc, char *argv[]) { ret = EXIT_FAILURE; } - if (close(fd) != 0) { + if (xclose(fd) != 0) { errmsg(cmd, path); ret = EXIT_FAILURE; } diff --git a/tests/posix/L_loops.sh b/tests/posix/L_loops.sh index f737cea..01b7efc 100644 --- a/tests/posix/L_loops.sh +++ b/tests/posix/L_loops.sh @@ -1,4 +1,4 @@ # POSIX says it's okay to either stop or keep going on seeing a filesystem # loop, as long as a diagnostic is printed -errors=$(invoke_bfs -L loops 2>&1 >/dev/null) -[ -n "$errors" ] +invoke_bfs -L loops >/dev/null 2>"$OUT" && fail +test -s "$OUT" diff --git a/tests/posix/L_xdev.out b/tests/posix/L_xdev.out index 2e80082..788579d 100644 --- a/tests/posix/L_xdev.out +++ b/tests/posix/L_xdev.out @@ -1,5 +1,5 @@ -scratch -scratch/foo -scratch/foo/bar -scratch/foo/qux -scratch/mnt +. +./foo +./foo/bar +./foo/qux +./mnt diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh index ddbadd8..82d8605 100644 --- a/tests/posix/L_xdev.sh +++ b/tests/posix/L_xdev.sh @@ -1,15 +1,13 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -ln -s ../mnt scratch/foo/bar -"$XTOUCH" scratch/mnt/baz -ln -s ../mnt/baz scratch/foo/qux +cd "$TEST" +mkdir foo mnt -bfs_diff -L scratch -xdev -ret=$? +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -sudo umount scratch/mnt -return $ret +ln -s ../mnt foo/bar +"$XTOUCH" mnt/baz +ln -s ../mnt/baz foo/qux + +bfs_diff -L . -xdev diff --git a/tests/posix/closed_stderr.sh b/tests/posix/closed_stderr.sh deleted file mode 100644 index cc746ef..0000000 --- a/tests/posix/closed_stderr.sh +++ /dev/null @@ -1 +0,0 @@ -fail invoke_bfs basic >&- 2>&- diff --git a/tests/posix/closed_stdout.sh b/tests/posix/closed_stdout.sh deleted file mode 100644 index 446bf56..0000000 --- a/tests/posix/closed_stdout.sh +++ /dev/null @@ -1 +0,0 @@ -fail invoke_bfs basic >&- diff --git a/tests/posix/deep.sh b/tests/posix/deep.sh index 3d1cd60..36a88c0 100644 --- a/tests/posix/deep.sh +++ b/tests/posix/deep.sh @@ -1,4 +1,2 @@ -closefrom 4 - -ulimit -n 16 +ulimit -n $((NOPENFD + 13)) bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \; diff --git a/tests/posix/depth_error.out b/tests/posix/depth_error.out index ed0e9a1..7ed5f0d 100644 --- a/tests/posix/depth_error.out +++ b/tests/posix/depth_error.out @@ -1,2 +1,2 @@ -scratch -scratch/foo +. +./foo diff --git a/tests/posix/depth_error.sh b/tests/posix/depth_error.sh index f770210..db414ba 100644 --- a/tests/posix/depth_error.sh +++ b/tests/posix/depth_error.sh @@ -1,11 +1,7 @@ -clean_scratch -"$XTOUCH" -p scratch/foo/bar -chmod a-r scratch/foo +cd "$TEST" +"$XTOUCH" -p foo/bar -bfs_diff scratch -depth -ret=$? +chmod a-r foo +defer chmod +r foo -chmod +r scratch/foo -clean_scratch - -[ $ret -eq $EX_BFS ] +! bfs_diff . -depth diff --git a/tests/posix/exec_nonexistent.sh b/tests/posix/exec_nonexistent.sh index b4e08e0..a9ff052 100644 --- a/tests/posix/exec_nonexistent.sh +++ b/tests/posix/exec_nonexistent.sh @@ -1,8 +1,4 @@ # Failure to execute the command should lead to an error message and # non-zero exit status. See https://unix.stackexchange.com/q/704522/56202 - -stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 - -bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print -(($? == EX_BFS)) +bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/posix/exec_plus_nonexistent.sh b/tests/posix/exec_plus_nonexistent.sh index f96099e..24582a3 100644 --- a/tests/posix/exec_plus_nonexistent.sh +++ b/tests/posix/exec_plus_nonexistent.sh @@ -1,5 +1,2 @@ -stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} + 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 - -bfs_diff basic -exec "$TESTS/nonexistent" {} + -print -(($? == EX_BFS)) +bfs_diff basic -exec "$TESTS/nonexistent" {} + -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/posix/exec_plus_nothing.sh b/tests/posix/exec_plus_nothing.sh index ef01968..347722d 100644 --- a/tests/posix/exec_plus_nothing.sh +++ b/tests/posix/exec_plus_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't look OOB for {} + -fail invoke_bfs basic -exec + +! invoke_bfs basic -exec + diff --git a/tests/posix/exec_plus_status.sh b/tests/posix/exec_plus_status.sh index ea9e5ef..a814c4e 100644 --- a/tests/posix/exec_plus_status.sh +++ b/tests/posix/exec_plus_status.sh @@ -1,4 +1,3 @@ # -exec ... {} + should always return true, but if the command fails, bfs # should exit with a non-zero status -bfs_diff basic -exec false {} + -print -(($? == EX_BFS)) +! bfs_diff basic -exec false {} + -print diff --git a/tests/posix/exec_ulimit.out b/tests/posix/exec_ulimit.out new file mode 100644 index 0000000..144169e --- /dev/null +++ b/tests/posix/exec_ulimit.out @@ -0,0 +1,16 @@ +64 deep/0/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/1/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/2/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/3/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/4/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/5/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/6/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/7/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/8/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/9/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/A/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/B/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/C/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/D/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/E/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/F/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE diff --git a/tests/posix/exec_ulimit.sh b/tests/posix/exec_ulimit.sh new file mode 100644 index 0000000..655fbec --- /dev/null +++ b/tests/posix/exec_ulimit.sh @@ -0,0 +1,2 @@ +ulimit -Sn 64 +bfs_diff deep -type f -exec bash -c 'printf "%d %s\n" $(ulimit -Sn) "${1:0:6}/.../${1##*/}"' bash {} \; diff --git a/tests/posix/extra_paren.sh b/tests/posix/extra_paren.sh index cd8e8f8..d15022f 100644 --- a/tests/posix/extra_paren.sh +++ b/tests/posix/extra_paren.sh @@ -1 +1 @@ -fail invoke_bfs basic -print \) +! invoke_bfs basic -print \) diff --git a/tests/posix/incomplete.sh b/tests/posix/incomplete.sh index 07b1b61..bca5a13 100644 --- a/tests/posix/incomplete.sh +++ b/tests/posix/incomplete.sh @@ -1 +1 @@ -fail invoke_bfs basic \( +! invoke_bfs basic \( diff --git a/tests/posix/missing_paren.sh b/tests/posix/missing_paren.sh index ac8dd60..d906fbe 100644 --- a/tests/posix/missing_paren.sh +++ b/tests/posix/missing_paren.sh @@ -1 +1 @@ -fail invoke_bfs basic \( -print +! invoke_bfs basic \( -print diff --git a/tests/posix/name_bracket.sh b/tests/posix/name_bracket.sh index 84a417f..e2f943d 100644 --- a/tests/posix/name_bracket.sh +++ b/tests/posix/name_bracket.sh @@ -1,5 +1,9 @@ -# fnmatch() is broken on macOS -skip_if test "$UNAME" = "Darwin" +# fnmatch() is broken on some platforms +case "$UNAME" in + Darwin|NetBSD) + skip + ;; +esac # An unclosed [ should be matched literally bfs_diff weirdnames -name '[' diff --git a/tests/posix/newer_broken.out b/tests/posix/newer_broken.out new file mode 100644 index 0000000..d2dcdd1 --- /dev/null +++ b/tests/posix/newer_broken.out @@ -0,0 +1 @@ +times diff --git a/tests/posix/newer_broken.sh b/tests/posix/newer_broken.sh new file mode 100644 index 0000000..dccaa73 --- /dev/null +++ b/tests/posix/newer_broken.sh @@ -0,0 +1,4 @@ +ln -s nowhere "$TEST/broken" +"$XTOUCH" -h -t "1991-12-14 00:03" "$TEST/broken" + +bfs_diff times -newer "$TEST/broken" diff --git a/tests/posix/newer_nonexistent.sh b/tests/posix/newer_nonexistent.sh index 789cadf..5f2da4b 100644 --- a/tests/posix/newer_nonexistent.sh +++ b/tests/posix/newer_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs times -newer times/nonexistent +! invoke_bfs times -newer times/nonexistent diff --git a/tests/posix/nogroup_ulimit.sh b/tests/posix/nogroup_ulimit.sh index 8f758c4..a39dd1f 100644 --- a/tests/posix/nogroup_ulimit.sh +++ b/tests/posix/nogroup_ulimit.sh @@ -1,8 +1,2 @@ -closefrom 4 -ulimit -n 16 - -# -mindepth 18, but POSIX -path="*/*/*/*/*/*" -path="$path/$path/$path" -bfs_diff deep -path "deep/$path" -nogroup - +ulimit -n $((NOPENFD + 13)) +bfs_diff deep -type f -nogroup diff --git a/tests/posix/nouser_ulimit.sh b/tests/posix/nouser_ulimit.sh index 2777589..a94b8c5 100644 --- a/tests/posix/nouser_ulimit.sh +++ b/tests/posix/nouser_ulimit.sh @@ -1,7 +1,2 @@ -closefrom 4 -ulimit -n 16 - -# -mindepth 18, but POSIX -path="*/*/*/*/*/*" -path="$path/$path/$path" -bfs_diff deep -path "deep/$path" -nouser +ulimit -n $((NOPENFD + 13)) +bfs_diff deep -type f -nouser diff --git a/tests/posix/ok_plus_nothing.sh b/tests/posix/ok_plus_nothing.sh index 7cb7de5..77c7644 100644 --- a/tests/posix/ok_plus_nothing.sh +++ b/tests/posix/ok_plus_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't look OOB for {} + -fail invoke_bfs basic -ok + +! invoke_bfs basic -ok + diff --git a/tests/posix/overlayfs.out b/tests/posix/overlayfs.out new file mode 100644 index 0000000..b472b56 --- /dev/null +++ b/tests/posix/overlayfs.out @@ -0,0 +1,5 @@ +merged +merged/bar +merged/baz +merged/baz/qux +merged/foo diff --git a/tests/posix/overlayfs.sh b/tests/posix/overlayfs.sh new file mode 100644 index 0000000..21ef22f --- /dev/null +++ b/tests/posix/overlayfs.sh @@ -0,0 +1,11 @@ +test "$UNAME" = "Linux" || skip + +cd "$TEST" +"$XTOUCH" -p lower/{foo,bar,baz} upper/{bar,baz/qux} + +mkdir -p work merged +bfs_sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged || skip +defer bfs_sudo rm -rf work +defer bfs_sudo umount merged + +bfs_diff merged diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh new file mode 100644 index 0000000..82fcd17 --- /dev/null +++ b/tests/posix/readdir_error.sh @@ -0,0 +1,37 @@ +test "$UNAME" = "Linux" || skip + +cd "$TEST" +mkfifo hang pid wait running + +( + # Create a zombie process + cat hang >/dev/null & + # Write the PID to pid + echo $! >pid + # Don't wait on the zombie process + exec cat wait hang >running +) & + +# Kill the parent cat on exit +defer kill -9 %1 + +# Read the child PID +read -r pid <pid + +# Make sure the parent cat is running before we kill the child, because bash +# will wait() on its children +echo >wait & +read -r _ <running + +# Turn the child into a zombie +kill -9 "$pid" + +# Wait until it's really a zombie +state=R +while [ "$state" != "Z" ]; do + read -r _ _ state _ <"/proc/$pid/stat" +done + +# On Linux, open(/proc/$pid/net) will succeed but readdir() will fail +test -r "/proc/$pid/net" || skip +! invoke_bfs "/proc/$pid/net" >/dev/null diff --git a/tests/posix/type_bind_mount.out b/tests/posix/type_bind_mount.out index 6435159..2f06c47 100644 --- a/tests/posix/type_bind_mount.out +++ b/tests/posix/type_bind_mount.out @@ -1 +1 @@ -scratch/null +./null diff --git a/tests/posix/type_bind_mount.sh b/tests/posix/type_bind_mount.sh index 445f6ef..97b7305 100644 --- a/tests/posix/type_bind_mount.sh +++ b/tests/posix/type_bind_mount.sh @@ -1,12 +1,9 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$UNAME" = "Linux" || skip -clean_scratch -"$XTOUCH" scratch/{file,null} -sudo mount --bind /dev/null scratch/null +cd "$TEST" +"$XTOUCH" file null -bfs_diff scratch -type c -ret=$? +bfs_sudo mount --bind /dev/null null || skip +defer bfs_sudo umount null -sudo umount scratch/null -return $ret +bfs_diff . -type c diff --git a/tests/posix/unionfs.out b/tests/posix/unionfs.out new file mode 100644 index 0000000..28c4ec1 --- /dev/null +++ b/tests/posix/unionfs.out @@ -0,0 +1,10 @@ +. +./lower +./lower/bar +./lower/baz +./lower/foo +./upper +./upper/bar +./upper/baz +./upper/baz/qux +./upper/foo diff --git a/tests/posix/unionfs.sh b/tests/posix/unionfs.sh new file mode 100644 index 0000000..94d3929 --- /dev/null +++ b/tests/posix/unionfs.sh @@ -0,0 +1,9 @@ +[[ "$UNAME" == *BSD* ]] || skip + +cd "$TEST" +"$XTOUCH" -p lower/{foo,bar,baz} upper/{bar,baz/qux} + +bfs_sudo mount -t unionfs -o below lower upper || skip +defer bfs_sudo umount upper + +bfs_diff . diff --git a/tests/posix/xdev.out b/tests/posix/xdev.out index f7839fb..6253434 100644 --- a/tests/posix/xdev.out +++ b/tests/posix/xdev.out @@ -1,4 +1,4 @@ -scratch -scratch/foo -scratch/foo/bar -scratch/mnt +. +./foo +./foo/bar +./mnt diff --git a/tests/posix/xdev.sh b/tests/posix/xdev.sh index 4591940..c59c5c8 100644 --- a/tests/posix/xdev.sh +++ b/tests/posix/xdev.sh @@ -1,13 +1,11 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -"$XTOUCH" scratch/foo/bar scratch/mnt/baz +cd "$TEST" +mkdir foo mnt -bfs_diff scratch -xdev -ret=$? +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -sudo umount scratch/mnt -return $ret +"$XTOUCH" foo/bar mnt/baz + +bfs_diff . -xdev diff --git a/tests/run.sh b/tests/run.sh new file mode 100644 index 0000000..df3bd88 --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,470 @@ +#!/hint/bash + +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD + +## 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 + callers | while read -r line func file; do + if [ "$func" = source ]; then + debug "$file" $line "${RED}error $ret${RST}" >&$DUPERR + break + fi + done +} + +# Source a test +source_test() ( + set -eE + trap debug_err ERR + + if ((${#MAKE[@]})); then + # Close the jobserver pipes + exec {READY_PIPE}<&- {DONE_PIPE}>&- + fi + + cd "$TMP" + source "$@" +) + +# Run a test +run_test() { + if ((VERBOSE_ERRORS)); then + source_test "$1" + else + source_test "$1" 2>"$TMP/$TEST.err" + fi + ret=$? + + if ((${#MAKE[@]})); then + # Write one byte to the done pipe + printf . >&$DONE_PIPE + fi + + case $ret in + 0) + if ((VERBOSE_TESTS)); then + color printf "${BOL}${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" + fi + ;; + *) + if ((!VERBOSE_ERRORS)); then + cat "$TMP/$TEST.err" >&2 + fi + color printf "${BOL}${RED}[FAIL]${RST} ${BLD}%s${RST}\n" "$TEST" + ;; + esac + + return $ret +} + +# Count the tests running in the background +BG=0 + +# Run a test in the background +bg_test() { + run_test "$1" & + ((++BG)) +} + +# Reap a finished background test +reap_test() { + ((BG--)) + + case "$1" in + 0) + ((++passed)) + ;; + $EX_SKIP) + ((++skipped)) + ;; + *) + ((++failed)) + ;; + esac +} + +# 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 + + reap_test $ret +} + +# Wait until we're ready to run another test +wait_ready() { + if ((${#MAKE[@]})); then + # We'd like to parse the output of jobs -n, but we can't run it in a + # subshell or we won't get the right output + jobs -n >"$TMP/jobs" + while read -r job status ret foo; do + case "$status" in + Done) + reap_test 0 + ;; + Exit) + reap_test $ret + ;; + esac + done <"$TMP/jobs" + + # Read one byte from the ready pipe + read -r -N1 -u$READY_PIPE + elif ((BG >= JOBS)); then + wait_test + fi +} + +# Run make as a co-process to use its job control +comake() { + coproc { + # We can't just use std{in,out}, due to + # https://www.gnu.org/software/make/manual/html_node/Parallel-Input.html + exec {DONE_PIPE}<&0 {READY_PIPE}>&1 + exec "${MAKE[@]}" -s \ + -f "$TESTS/tests.mk" \ + DONE=$DONE_PIPE \ + READY=$READY_PIPE \ + "${!TEST_CASES[@]}" \ + </dev/null >/dev/null + } + + # coproc pipes aren't inherited by subshells, so dup them + 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 + fi + + 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 + + if ((${#MAKE[@]})); then + comake + fi + + # Turn off set -e (but turn it back on in run_test) + set +e + + for TEST in "${TEST_CASES[@]}"; do + wait_ready + if ((STOP && failed > 0)); then + break + fi + + percent=$((100 * ran / total)) + color printf "$TEST_FMT" $percent "$TEST" + + mkdir -p "$TMP/$TEST" + OUT="$TMP/$TEST.out" + + bg_test "$TESTS/$TEST.sh" + ((++ran)) + done + + while ((BG > 0)); do + wait_test + done + + printf "${BOL}" + + if ((passed > 0)); then + color printf "${GRN}[PASS]${RST} ${BLD}%3d${RST} / ${BLD}%d${RST}\n" $passed $total + fi + if ((skipped > 0)); then + color printf "${CYN}[SKIP]${RST} ${BLD}%3d${RST} / ${BLD}%d${RST}\n" $skipped $total + fi + if ((failed > 0)); then + color printf "${RED}[FAIL]${RST} ${BLD}%3d${RST} / ${BLD}%d${RST}\n" $failed $total + exit 1 + fi +} + +## Utilities for the tests themselves + +# Default return value for failed tests +EX_FAIL=1 + +# Fail the current test +fail() { + exit $EX_FAIL +} + +# Return value when a test is skipped +EX_SKIP=77 + +# Skip the current test +skip() { + if ((VERBOSE_SKIPPED)); then + caller | { + read -r line file + printf "${BOL}" + debug "$file" $line "" >&$DUPOUT + } + fi + + exit $EX_SKIP +} + +# Run a command and check its exit status +check_exit() { + local expected="$1" + local actual=0 + shift + "$@" || actual=$? + ((actual == expected)) +} + +# Run a command with sudo +bfs_sudo() { + if ((${#SUDO[@]})); then + "${SUDO[@]}" "$@" + else + return 1 + fi +} + +# Get the inode number of a file +inum() { + ls -id "$@" | awk '{ print $1 }' +} + +# Set an ACL on a file +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 +} + +# Print a bfs invocation for --verbose=commands +bfs_verbose() { + if ((VERBOSE_COMMANDS)); then + ( + # Close some fds to make room for the pipe, + # even with extremely low ulimit -n + exec >&- {DUPERR}>&- + exec >&$DUPOUT {DUPOUT}>&- + color bfs_verbose_impl "$@" + ) + fi +} + +bfs_verbose_impl() { + printf "${GRN}%q${RST}" "${BFS[0]}" + if ((${#BFS[@]} > 1)); then + printf " ${GRN}%q${RST}" "${BFS[@]:1}" + fi + + local expr_started=0 color + for arg; do + case "$arg" in + -[A-Z]*|-[dsxf]|-j*) + color="${CYN}" + ;; + \(|!|-[ao]|-and|-or|-not|-exclude) + expr_started=1 + color="${RED}" + ;; + \)|,) + if ((expr_started)); then + color="${RED}" + else + color="${MAG}" + fi + ;; + -?*) + expr_started=1 + color="${BLU}" + ;; + *) + if ((expr_started)); then + color="${BLD}" + else + color="${MAG}" + fi + ;; + esac + printf " ${color}%q${RST}" "$arg" + done + + printf '\n' +} + +# Run the bfs we're testing +invoke_bfs() { + skip + bfs_verbose "$@" + + local ret=0 + # Close the logging fds + "${BFS[@]}" "$@" {DUPOUT}>&- {DUPERR}>&- || 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 + +# Run bfs with a pseudo-terminal attached +bfs_pty() { + test -n "${UNBUFFER:-}" || skip + + bfs_verbose "$@" + + local ret=0 + "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@" </dev/null' bash "${BFS[@]}" "$@" || ret=$? + + if ((ret > 125)); then + exit $ret + else + return $ret + fi +} + +# Create a directory tree with xattrs in scratch +make_xattrs() { + cd "$TEST" + + "$XTOUCH" normal xattr xattr_2 + ln -s xattr link + ln -s normal xattr_link + + case "$UNAME" in + Darwin) + xattr -w bfs_test true xattr \ + && xattr -w bfs_test_2 true xattr_2 \ + && xattr -s -w bfs_test true xattr_link + ;; + FreeBSD) + setextattr user bfs_test true xattr \ + && setextattr user bfs_test_2 true xattr_2 \ + && setextattr -h user bfs_test true 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 xattr \ + && bfs_sudo setfattr -n security.bfs_test_2 xattr_2 \ + && bfs_sudo setfattr -h -n security.bfs_test xattr_link + ;; + esac +} + +## Snapshot testing + +# Return value when a difference is detected +EX_DIFF=20 + +# Detect colored diff support +if diff --color /dev/null /dev/null &>/dev/null; then + DIFF="diff --color" +else + DIFF="diff" +fi + +# Sort the output file +sort_output() { + sort -o "$OUT" "$OUT" +} + +# Diff against the expected output +diff_output() { + local GOLD="$TESTS/$TEST.out" + + if ((UPDATE)); then + cp "$OUT" "$GOLD" + elif ! cmp -s "$GOLD" "$OUT"; then + $DIFF -u "$GOLD" "$OUT" >&$DUPERR + fi +} + +# Run bfs, and diff it against the expected output +bfs_diff() { + local fd + if ! fd=$("${BFS[@]}" "$@"); then + skip + fi + + local ret=0 + eval "$fd" >"$OUT" || ret=$? + sed -i 's|/$||' "$OUT" + sort_output + diff_output || exit $EX_DIFF + + return $ret +} diff --git a/tests/stddirs.sh b/tests/stddirs.sh new file mode 100644 index 0000000..e08e6bf --- /dev/null +++ b/tests/stddirs.sh @@ -0,0 +1,152 @@ +#!/hint/bash + +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD + +## Standard directory trees for tests + +# Creates a simple file+directory structure for tests +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" +} + +# Creates a file+directory structure with various permissions for tests +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" +} + +# Creates a file+directory structure with various symbolic and hard links +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" +} + +# Creates a file+directory structure with symbolic link loops +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" +} + +# Creates a file+directory structure with varying timestamps +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" +} + +# Creates a file+directory structure with various weird file/directory names +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" +} + +# Creates a very deep directory structure for testing PATH_MAX handling +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}" + + # 4 * 4 * 256 == 4096 >= PATH_MAX + local path="$name/$name/$name/$name" + path="$path/$path/$path/$path" + + "$XTOUCH" -p "$1"/{{0..9},A,B,C,D,E,F}/"$path/$name" +} + +# Creates a directory structure with many different types, and therefore colors +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 06644 "$1"/sugid + chmod 04644 "$1"/suid + chmod 02644 "$1"/sgid + 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' +} + +# Create all standard directory trees +make_stddirs() { + TMP=$(mktemp -d "${TMPDIR:-/tmp}"/bfs.XXXXXXXXXX) + + if ((CLEAN)); then + defer clean_stddirs + else + printf "Test files saved to ${BLD}%s${RST}\n" "$TMP" + fi + + chown "$(id -u):$(id -g)" "$TMP" + + make_basic "$TMP/basic" + make_perms "$TMP/perms" + make_links "$TMP/links" + make_loops "$TMP/loops" + make_times "$TMP/times" + make_weirdnames "$TMP/weirdnames" + make_deep "$TMP/deep" + make_rainbow "$TMP/rainbow" +} + +# Clean up temporary directories on exit +clean_stddirs() { + # 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 + + rm -rf "$TMP" +} diff --git a/tests/tests.h b/tests/tests.h new file mode 100644 index 0000000..9078938 --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,73 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +/** + * Unit tests. + */ + +#ifndef BFS_TESTS_H +#define BFS_TESTS_H + +#include "prelude.h" +#include "diag.h" + +/** Unit test function type. */ +typedef bool test_fn(void); + +/** Memory allocation tests. */ +bool check_alloc(void); + +/** Standard library wrapper tests. */ +bool check_bfstd(void); + +/** Bit manipulation tests. */ +bool check_bit(void); + +/** I/O queue tests. */ +bool check_ioq(void); + +/** Trie tests. */ +bool check_trie(void); + +/** Process spawning tests. */ +bool check_xspawn(void); + +/** Time tests. */ +bool check_xtime(void); + +/** Don't ignore the bfs_check() return value. */ +attr(nodiscard) +static inline bool bfs_check(bool ret) { + return ret; +} + +/** + * Check a condition, logging a message on failure but continuing. + */ +#define bfs_check(...) \ + bfs_check(bfs_check_(#__VA_ARGS__, __VA_ARGS__, "", "")) + +#define bfs_check_(str, cond, format, ...) \ + ((cond) ? true : (bfs_diag( \ + sizeof(format) > 1 \ + ? "%.0s" format "%s%s" \ + : "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_pcheck_(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)) + +#endif // BFS_TESTS_H diff --git a/tests/tests.mk b/tests/tests.mk new file mode 100644 index 0000000..035ca79 --- /dev/null +++ b/tests/tests.mk @@ -0,0 +1,13 @@ +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD + +# Makefile that exposes make's job control to tests.sh + +# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to +.OBJDIR: . + +# Turn off implicit rules +.SUFFIXES: + +.DEFAULT:: + bash -c 'printf . >&$(READY) && read -r -N1 -u$(DONE)' diff --git a/tests/tests.sh b/tests/tests.sh index 762687e..3890243 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -1,711 +1,20 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2015-2022 Tavian Barnes <tavianator@tavianator.com> # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD set -euP umask 022 -export LC_ALL=C -export TZ=UTC0 - -export ASAN_OPTIONS="abort_on_error=1" -export LSAN_OPTIONS="abort_on_error=1" -export MSAN_OPTIONS="abort_on_error=1" -export TSAN_OPTIONS="abort_on_error=1" -export UBSAN_OPTIONS="abort_on_error=1" - -export LS_COLORS="" -unset BFS_COLORS - -if [ -t 1 ]; then - BLD=$'\033[01m' - RED=$'\033[01;31m' - GRN=$'\033[01;32m' - YLW=$'\033[01;33m' - BLU=$'\033[01;34m' - MAG=$'\033[01;35m' - CYN=$'\033[01;36m' - RST=$'\033[0m' -else - BLD= - RED= - GRN= - YLW= - BLU= - MAG= - CYN= - RST= -fi - -UNAME=$(uname) - -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 - cat >&2 <<EOF -${RED}error:${RST} Failed to drop capabilities. -EOF - - exit 1 - fi - - cat >&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" -eq 0 ]; then - UNLESS= - if [ "$UNAME" = "Linux" ]; then - UNLESS=" unless ${GRN}capsh${RST} is installed" - fi - - cat >&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} "") - cat <<EOF -Usage: ${GRN}$0${RST} [${BLU}--bfs${RST}=${MAG}path/to/bfs${RST}] [${BLU}--posix${RST}] [${BLU}--bsd${RST}] [${BLU}--gnu${RST}] [${BLU}--all${RST}] [${BLU}--sudo${RST}] - $pad [${BLU}--stop${RST}] [${BLU}--noclean${RST}] [${BLU}--update${RST}] [${BLU}--verbose${RST}[=${BLD}LEVEL${RST}]] [${BLU}--help${RST}] - $pad [${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}--posix${RST}, ${BLU}--bsd${RST}, ${BLU}--gnu${RST}, ${BLU}--all${RST} - Choose which test cases to run (default: ${BLU}--all${RST}) - - ${BLU}--sudo${RST} - Run tests that require root - - ${BLU}--stop${RST} - Stop when the first error occurs - - ${BLU}--noclean${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 - - ${BLD}TEST${RST} - Select individual test cases to run (e.g. ${BLD}posix/basic${RST}) -EOF -} - -DEFAULT=yes -POSIX= -COMMON= -BSD= -GNU= -ALL= -SUDO= -STOP= -CLEAN=yes -UPDATE= -VERBOSE_COMMANDS= -VERBOSE_ERRORS= -VERBOSE_SKIPPED= -VERBOSE_TESTS= -EXPLICIT= - -enabled_tests=() - -for arg; do - case "$arg" in - --bfs=*) - BFS="${arg#*=}" - ;; - --posix) - DEFAULT= - POSIX=yes - ;; - --bsd) - DEFAULT= - POSIX=yes - COMMON=yes - BSD=yes - ;; - --gnu) - DEFAULT= - POSIX=yes - COMMON=yes - GNU=yes - ;; - --all) - DEFAULT= - POSIX=yes - COMMON=yes - BSD=yes - GNU=yes - ALL=yes - ;; - --sudo) - SUDO=yes - ;; - --stop) - STOP=yes - ;; - --noclean) - CLEAN= - ;; - --update) - UPDATE=yes - ;; - --verbose=commands) - VERBOSE_COMMANDS=yes - ;; - --verbose=errors) - VERBOSE_ERRORS=yes - ;; - --verbose=skipped) - VERBOSE_SKIPPED=yes - ;; - --verbose=tests) - VERBOSE_SKIPPED=yes - VERBOSE_TESTS=yes - ;; - --verbose) - VERBOSE_COMMANDS=yes - VERBOSE_ERRORS=yes - VERBOSE_SKIPPED=yes - VERBOSE_TESTS=yes - ;; - --help) - usage - exit 0 - ;; - */*) - EXPLICIT=yes - SUDO=yes - enabled_tests+=("$arg") - ;; - *) - printf "${RED}error:${RST} Unrecognized option '%s'.\n\n" "$arg" >&2 - usage >&2 - exit 1 - ;; - esac -done - -if [ "$DEFAULT" ]; then - POSIX=yes - COMMON=yes - BSD=yes - GNU=yes - ALL=yes -fi - -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/find2fd}" -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 [ ! "$EXPLICIT" ]; then - [ "$POSIX" ] && enabled_tests+=(posix/*.sh) - [ "$COMMON" ] && enabled_tests+=(common/*.sh) - [ "$BSD" ] && enabled_tests+=(bsd/*.sh) - [ "$GNU" ] && enabled_tests+=(gnu/*.sh) - [ "$ALL" ] && enabled_tests+=(bfs/*.sh) - - enabled_tests=("${enabled_tests[@]%.sh}") -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 - trap cleanup EXIT -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}" - - # 4 * 256 - 1 == 1023 - local names="$name/$name/$name/$name" - - for i in {0..9} A B C D E F; do - ( - mkdir "$1/$i" - cd "$1/$i" - - # 4 * 1024 == 4096 == PATH_MAX - for _ in {1..4}; do - mkdir -p "$names" - cd "$names" - done - - "$XTOUCH" "$name" - ) - 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/star".{gz,tar,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 -} -make_rainbow "$TMP/rainbow" - -# 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 - if [ -t 3 ]; then - printf "${GRN}%q${RST} " "${BFS[@]}" >&3 - - local expr_started= - for arg; do - if [[ $arg == -[A-Z]* ]]; then - printf "${CYN}%q${RST} " "$arg" >&3 - elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then - expr_started=yes - printf "${RED}%q${RST} " "$arg" >&3 - elif [[ $expr_started && $arg == [\),] ]]; then - printf "${RED}%q${RST} " "$arg" >&3 - elif [[ $arg == -?* ]]; then - expr_started=yes - printf "${BLU}%q${RST} " "$arg" >&3 - elif [ "$expr_started" ]; then - printf "${BLD}%q${RST} " "$arg" >&3 - else - printf "${MAG}%q${RST} " "$arg" >&3 - fi - done - else - printf '%q ' "${BFS[@]}" "$@" >&3 - fi - printf '\n' >&3 - fi -} - -function invoke_bfs() { - skip - bfs_verbose "$@" - "${BFS[@]}" "$@" -} - -# Expect a command to fail, but not crash -function fail() { - "$@" - local STATUS="$?" - - if ((STATUS > 125)); then - exit "$STATUS" - elif ((STATUS > 0)); then - return 0 - else - return 1 - fi -} - -# Detect colored diff support -if [ -t 2 ] && diff --color=always /dev/null /dev/null 2>/dev/null; then - DIFF="diff --color=always" -else - DIFF="diff" -fi - -# Return value when bfs fails -EX_BFS=10 -# 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 "$@" - - FD=$("${BFS[@]}" "$@") - if [ "$?" -ne 0 ]; then - skip - fi - - eval "$FD" | sed 's|/$||' | sort >"$OUT" - local STATUS="${PIPESTATUS[0]}" - if [ "$STATUS" -ne 0 ]; then - skip - fi - - diff_output || return $EX_DIFF - - if [ "$STATUS" -eq 0 ]; then - return 0 - else - return $EX_BFS - fi -} - -function skip() { - exit $EX_SKIP -} - -function skip_if() { - if "$@"; then - skip - fi -} - -function skip_unless() { - skip_if fail "$@" -} - -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" -ge "$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")" -gt 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 - [ "$SUDO" ] \ - && sudo setfattr -n security.bfs_test scratch/xattr \ - && sudo setfattr -n security.bfs_test_2 scratch/xattr_2 \ - && sudo setfattr -h -n security.bfs_test scratch/xattr_link - ;; - esac -} - -cd "$TMP" -set +e - -BOL='\n' -EOL='\n' - -function update_eol() { - # Put the cursor at the last column, then write a space so the next - # character will wrap - EOL="\\033[${COLUMNS}G " -} - -if [ "$VERBOSE_TESTS" ]; then - BOL='' -elif [ -t 1 ]; 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 - -passed=0 -failed=0 -skipped=0 - -for TEST in "${enabled_tests[@]}"; do - if [[ -t 1 || "$VERBOSE_TESTS" ]]; then - printf "${BOL}${YLW}%s${RST}${EOL}" "$TEST" - else - printf "." - fi - - OUT="$TMP/$TEST.out" - mkdir -p "${OUT%/*}" - - if [ "$VERBOSE_ERRORS" ]; then - (. "$TESTS/$TEST.sh") - else - (. "$TESTS/$TEST.sh") 2>"$TMP/stderr" - fi - status=$? - - if ((status == 0)); then - ((++passed)) - elif ((status == EX_SKIP)); then - ((++skipped)) - if [ "$VERBOSE_SKIPPED" ]; then - printf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" - fi - else - ((++failed)) - [ "$VERBOSE_ERRORS" ] || cat "$TMP/stderr" >&2 - printf "${BOL}${RED}%s failed!${RST}\n" "$TEST" - [ "$STOP" ] && break - fi -done - -printf "${BOL}" - -if ((passed > 0)); then - printf "${GRN}tests passed: %d${RST}\n" "$passed" -fi -if ((skipped > 0)); then - printf "${CYN}tests skipped: %s${RST}\n" "$skipped" -fi -if ((failed > 0)); then - printf "${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 diff --git a/tests/trie.c b/tests/trie.c index 6bc7549..4667322 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,23 +1,10 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes <tavianator@tavianator.com> * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ - -#undef NDEBUG - -#include "../src/trie.h" -#include <assert.h> +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "trie.h" +#include "diag.h" #include <stdlib.h> #include <string.h> @@ -50,14 +37,16 @@ const char *keys[] = { ">>>", }; -const size_t nkeys = sizeof(keys) / sizeof(keys[0]); +const size_t nkeys = countof(keys); + +bool check_trie(void) { + bool ret = true; -int main(void) { struct trie trie; trie_init(&trie); for (size_t i = 0; i < nkeys; ++i) { - assert(!trie_find_str(&trie, keys[i])); + ret &= bfs_check(!trie_find_str(&trie, keys[i])); const char *prefix = NULL; for (size_t j = 0; j < i; ++j) { @@ -70,38 +59,38 @@ int main(void) { struct trie_leaf *leaf = trie_find_prefix(&trie, keys[i]); if (prefix) { - assert(leaf); - assert(strcmp(prefix, leaf->key) == 0); + bfs_verify(leaf); + ret &= bfs_check(strcmp(prefix, leaf->key) == 0); } else { - assert(!leaf); + ret &= bfs_check(!leaf); } leaf = trie_insert_str(&trie, keys[i]); - assert(leaf); - assert(strcmp(keys[i], leaf->key) == 0); - assert(leaf->length == strlen(keys[i]) + 1); + bfs_verify(leaf); + ret &= bfs_check(strcmp(keys[i], leaf->key) == 0); + ret &= bfs_check(leaf->length == strlen(keys[i]) + 1); } { size_t i = 0; - TRIE_FOR_EACH(&trie, leaf) { - assert(leaf == trie_find_str(&trie, keys[i])); - assert(!leaf->prev || leaf->prev->next == leaf); - assert(!leaf->next || leaf->next->prev == leaf); + for_trie (leaf, &trie) { + ret &= bfs_check(leaf == trie_find_str(&trie, keys[i])); + ret &= bfs_check(!leaf->prev || leaf->prev->next == leaf); + ret &= bfs_check(!leaf->next || leaf->next->prev == leaf); ++i; } - assert(i == nkeys); + ret &= bfs_check(i == nkeys); } for (size_t i = 0; i < nkeys; ++i) { struct trie_leaf *leaf = trie_find_str(&trie, keys[i]); - assert(leaf); - assert(strcmp(keys[i], leaf->key) == 0); - assert(leaf->length == strlen(keys[i]) + 1); + bfs_verify(leaf); + ret &= bfs_check(strcmp(keys[i], leaf->key) == 0); + ret &= bfs_check(leaf->length == strlen(keys[i]) + 1); trie_remove(&trie, leaf); leaf = trie_find_str(&trie, keys[i]); - assert(!leaf); + ret &= bfs_check(!leaf); const char *postfix = NULL; for (size_t j = i + 1; j < nkeys; ++j) { @@ -114,35 +103,35 @@ int main(void) { leaf = trie_find_postfix(&trie, keys[i]); if (postfix) { - assert(leaf); - assert(strcmp(postfix, leaf->key) == 0); + bfs_verify(leaf); + ret &= bfs_check(strcmp(postfix, leaf->key) == 0); } else { - assert(!leaf); + ret &= bfs_check(!leaf); } } - TRIE_FOR_EACH(&trie, leaf) { - assert(false); + for_trie (leaf, &trie) { + ret &= bfs_check(false, "trie should be empty"); } // This tests the "jump" node handling on 32-bit platforms size_t longsize = 1 << 20; char *longstr = malloc(longsize); - assert(longstr); + bfs_verify(longstr); memset(longstr, 0xAC, longsize); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr + longsize/2, 0xAB, longsize/2); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + memset(longstr + longsize / 2, 0xAB, longsize / 2); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr, 0xAA, longsize/2); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + memset(longstr, 0xAA, longsize / 2); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); free(longstr); trie_destroy(&trie); - return EXIT_SUCCESS; + return ret; } diff --git a/tests/util.sh b/tests/util.sh new file mode 100644 index 0000000..3969db5 --- /dev/null +++ b/tests/util.sh @@ -0,0 +1,182 @@ +#!/hint/bash + +# Copyright © Tavian Barnes <tavianator@tavianator.com> +# SPDX-License-Identifier: 0BSD + +## Utility functions + +# Portable realpath(1) +_realpath() ( + cd "$(dirname -- "$1")" + echo "$PWD/$(basename -- "$1")" +) + +# Globals +ROOT=$(_realpath "$(dirname -- "$TESTS")") +TESTS="$ROOT/tests" +BIN="$ROOT/bin" +MKSOCK="$BIN/tests/mksock" +XTOUCH="$BIN/tests/xtouch" +UNAME=$(uname) + +# Standardize the environment +stdenv() { + export LC_ALL=C + export TZ=UTC0 + + local SAN_OPTIONS="abort_on_error=1: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 + + 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 + + # Count the inherited FDs + if [ -d /proc/self/fd ]; then + local fds=/proc/self/fd + else + local fds=/dev/fd + fi + # We use ls $fds on purpose, rather than e.g. ($fds/*), to avoid counting + # internal bash fds that are not exposed to spawned processes + NOPENFD=$(ls -1q "$fds/" 2>/dev/null | wc -l) + NOPENFD=$((NOPENFD > 3 ? NOPENFD - 1 : 3)) + + # 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 +} + +# Drop root priviliges or bail +drop_root() { + 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 cat >&2 <<EOF +${RED}error:${RST} Failed to drop capabilities. +EOF + + exit 1 + fi + + color cat >&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 cat >&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 +} + +## Debugging + +# Get the bash call stack +callers() { + local frame=0 + while caller $frame; do + ((++frame)) + done +} + +# Print a message including path, line number, and command +debug() { + local file="$1" + local line="$2" + local msg="$3" + local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || : + file="${file/#*\/tests\//tests/}" + + color printf "${BLD}%s:%d:${RST} %s\n %s\n" "$file" "$line" "$msg" "$cmd" +} + +## Deferred cleanup + +# Quote a command safely for eval +quote() { + printf '%q' "$1" + shift + if (($# > 0)); then + printf ' %q' "$@" + fi +} + +DEFER_LEVEL=-1 + +# Run a command when this (sub)shell exits +defer() { + # Check if the EXIT trap is already set + if ((DEFER_LEVEL != BASH_SUBSHELL)); then + DEFER_LEVEL=$BASH_SUBSHELL + 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") +} + +# Pop a single command from the defer stack and run it +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 + debug "$file" $line "${RED}error $ret${RST}" >&$DUPERR + fi + + return $ret +} + +# Run all deferred commands +pop_defers() { + local ret=0 + + while ((${#DEFER_CMDS[@]} > 0)); do + pop_defer || ret=$? + done + + return $ret +} diff --git a/tests/xspawn.c b/tests/xspawn.c new file mode 100644 index 0000000..785ea48 --- /dev/null +++ b/tests/xspawn.c @@ -0,0 +1,197 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "alloc.h" +#include "bfstd.h" +#include "dstring.h" +#include "xspawn.h" +#include <stdlib.h> +#include <string.h> +#include <sys/wait.h> + +/** Duplicate the current environment. */ +static char **envdup(void) { + extern char **environ; + + char **envp = NULL; + size_t envc = 0; + + for (char **var = environ; ; ++var) { + char *copy = NULL; + if (*var) { + copy = strdup(*var); + if (!copy) { + goto fail; + } + } + + char **dest = RESERVE(char *, &envp, &envc); + if (!dest) { + free(copy); + goto fail; + } + *dest = copy; + + if (!*var) { + break; + } + } + + return envp; + +fail: + for (size_t i = 0; i < envc; ++i) { + free(envp[i]); + } + free(envp); + return NULL; +} + +/** Check that we resolve executables in $PATH correctly. */ +static bool check_use_path(bool use_posix) { + bool ret = true; + + struct bfs_spawn spawn; + ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0); + if (!ret) { + goto out; + } + + spawn.flags |= BFS_SPAWN_USE_PATH; + if (!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); + if (!ret) { + goto destroy; + } + + // Check that $PATH is resolved in the parent's environment + char **envp; + ret &= bfs_pcheck(envp = envdup()); + if (!ret) { + goto destroy; + } + + // Check that $PATH is resolved after the file actions + char *old_path = getenv("PATH"); + dchar *new_path = NULL; + if (old_path) { + ret &= bfs_pcheck(old_path = strdup(old_path)); + if (!ret) { + goto env; + } + new_path = dstrprintf("tests:%s", old_path); + } else { + new_path = dstrdup("tests"); + } + ret &= bfs_check(new_path); + if (!ret) { + goto path; + } + + ret &= bfs_pcheck(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()"); + if (!ret) { + goto unset; + } + + int wstatus; + ret &= bfs_pcheck(xwaitpid(pid, &wstatus, 0) == pid) + && bfs_check(WIFEXITED(wstatus)); + if (ret) { + int wexit = WEXITSTATUS(wstatus); + ret &= bfs_check(wexit == EXIT_SUCCESS, "xspawnee: exit(%d)", wexit); + } + +unset: + if (old_path) { + ret &= bfs_pcheck(setenv("PATH", old_path, true) == 0); + } else { + ret &= bfs_pcheck(unsetenv("PATH") == 0); + } +path: + dstrfree(new_path); + free(old_path); +env: + for (char **var = envp; *var; ++var) { + free(*var); + } + free(envp); +destroy: + ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0); +out: + return ret; +} + +/** Check path resolution of non-existent executables. */ +static bool check_enoent(bool use_posix) { + bool ret = true; + + struct bfs_spawn spawn; + ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0); + if (!ret) { + goto out; + } + + spawn.flags |= BFS_SPAWN_USE_PATH; + if (!use_posix) { + spawn.flags &= ~BFS_SPAWN_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_pcheck(bfs_spawn_destroy(&spawn) == 0); +out: + return ret; +} + +static bool check_resolve(void) { + bool ret = true; + char *exe; + + exe = bfs_spawn_resolve("sh"); + ret &= bfs_pcheck(exe, "bfs_spawn_resolve('sh')"); + free(exe); + + exe = bfs_spawn_resolve("/bin/sh"); + ret &= bfs_pcheck(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); + free(exe); + + ret &= bfs_pcheck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT); + + return ret; +} + +bool check_xspawn(void) { + bool ret = true; + + ret &= check_use_path(true); + ret &= check_use_path(false); + + ret &= check_enoent(true); + ret &= check_enoent(false); + + ret &= check_resolve(); + + return ret; +} diff --git a/tests/xspawnee.c b/tests/xspawnee.c new file mode 100644 index 0000000..b0a76ca --- /dev/null +++ b/tests/xspawnee.c @@ -0,0 +1,17 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include <stdlib.h> +#include <string.h> + +/** Child binary for bfs_spawn() tests. */ +int main(int argc, char *argv[]) { + if (argc >= 2) { + const char *path = getenv("PATH"); + if (!path || strcmp(path, argv[1]) != 0) { + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/tests/xtime.c b/tests/xtime.c new file mode 100644 index 0000000..d9d6c5c --- /dev/null +++ b/tests/xtime.c @@ -0,0 +1,195 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "xtime.h" +#include "bfstd.h" +#include "diag.h" +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <time.h> + +static bool tm_equal(const struct tm *tma, const struct tm *tmb) { + return tma->tm_year == tmb->tm_year + && tma->tm_mon == tmb->tm_mon + && tma->tm_mday == tmb->tm_mday + && tma->tm_hour == tmb->tm_hour + && tma->tm_min == tmb->tm_min + && tma->tm_sec == tmb->tm_sec + && tma->tm_wday == tmb->tm_wday + && tma->tm_yday == tmb->tm_yday + && tma->tm_isdst == tmb->tm_isdst; +} + +/** Check one xgetdate() result. */ +static bool check_one_xgetdate(const char *str, int error, time_t expected) { + struct timespec ts; + int ret = xgetdate(str, &ts); + + if (error) { + return bfs_pcheck(ret == -1 && errno == error, "xgetdate('%s')", str); + } else { + return bfs_pcheck(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); + } +} + +/** xgetdate() tests. */ +static bool check_xgetdate(void) { + bool ret = true; + + ret &= check_one_xgetdate("", EINVAL, 0); + ret &= check_one_xgetdate("????", EINVAL, 0); + ret &= check_one_xgetdate("1991", EINVAL, 0); + ret &= check_one_xgetdate("1991-??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14", 0, 692668800); + ret &= check_one_xgetdate("1991-12-14-", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T10:??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10:11", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T10:11:??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10:11:12", 0, 692705472); + ret &= check_one_xgetdate("1991-12-14T10Z", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T10:11Z", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T10:11:12Z", 0, 692705472); + ret &= check_one_xgetdate("1991-12-14T10:11:12?", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T03-07", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T06:41-03:30", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T03:11:12-07:00", 0, 692705472); + ret &= check_one_xgetdate("19911214 031112-0700", 0, 692705472);; + + return ret; +} + +#define TM_FORMAT "%04d-%02d-%02d %02d:%02d:%02d (%d/7, %d/365%s)" + +#define TM_PRINTF(tm) \ + (1900 + (tm).tm_year), (tm).tm_mon, (tm).tm_mday, \ + (tm).tm_hour, (tm).tm_min, (tm).tm_sec, \ + ((tm).tm_wday + 1), ((tm).tm_yday + 1), \ + ((tm).tm_isdst ? ((tm).tm_isdst < 0 ? ", DST?" : ", DST") : "") + +/** Check one xmktime() result. */ +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)); + return false; + } + + time_t actual; + return bfs_pcheck(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); +} + +/** xmktime() tests. */ +static bool check_xmktime(void) { + bool ret = true; + + for (time_t time = -10; time <= 10; ++time) { + ret &= check_one_xmktime(time); + } + + // Attempt to trigger overflow (but don't test for it, since it's not mandatory) + struct tm tm = { + .tm_year = INT_MAX, + .tm_mon = INT_MAX, + .tm_mday = INT_MAX, + .tm_hour = INT_MAX, + .tm_min = INT_MAX, + .tm_sec = INT_MAX, + .tm_isdst = -1, + }; + time_t time; + xmktime(&tm, &time); + + return ret; +} + +/** Check one xtimegm() result. */ +static bool check_one_xtimegm(const struct tm *tm) { + struct tm tma = *tm, tmb = *tm; + time_t ta, tb; + ta = mktime(&tma); + if (xtimegm(&tmb, &tb) != 0) { + tb = -1; + } + + bool ret = true; + ret &= bfs_check(ta == tb, "%jd != %jd", (intmax_t)ta, (intmax_t)tb); + ret &= bfs_check(ta == -1 || tm_equal(&tma, &tmb)); + + if (!ret) { + bfs_diag("mktime(): " TM_FORMAT, TM_PRINTF(tma)); + bfs_diag("xtimegm(): " TM_FORMAT, TM_PRINTF(tmb)); + bfs_diag("(input): " TM_FORMAT, TM_PRINTF(*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; + time_t time = 123; + + bool ret = true; + ret &= bfs_check(xtimegm(©, &time) == -1 && errno == EOVERFLOW); + ret &= bfs_check(tm_equal(©, tm)); + ret &= bfs_check(time == 123); + + if (!ret) { + bfs_diag("xtimegm(): " TM_FORMAT, TM_PRINTF(copy)); + bfs_diag("(input): " TM_FORMAT, TM_PRINTF(*tm)); + } + + return ret; +} +#endif + +/** xtimegm() tests. */ +static bool check_xtimegm(void) { + bool ret = true; + + struct tm tm = { + .tm_isdst = -1, + }; + + // Check equivalence with mktime() + for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) + for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) + for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) + for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) + for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) + for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { + ret &= check_one_xtimegm(&tm); + } + +#if !BFS_HAS_TIMEGM + // Check integer overflow cases + 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; +} + +bool check_xtime(void) { + bool ret = true; + ret &= check_xgetdate(); + ret &= check_xmktime(); + ret &= check_xtimegm(); + return ret; +} diff --git a/tests/xtimegm.c b/tests/xtimegm.c deleted file mode 100644 index d774b9e..0000000 --- a/tests/xtimegm.c +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes <tavianator@tavianator.com> * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ - -#include "../src/xtime.h" -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> - -static bool tm_equal(const struct tm *tma, const struct tm *tmb) { - if (tma->tm_year != tmb->tm_year) { - return false; - } - if (tma->tm_mon != tmb->tm_mon) { - return false; - } - if (tma->tm_mday != tmb->tm_mday) { - return false; - } - if (tma->tm_hour != tmb->tm_hour) { - return false; - } - if (tma->tm_min != tmb->tm_min) { - return false; - } - if (tma->tm_sec != tmb->tm_sec) { - return false; - } - if (tma->tm_wday != tmb->tm_wday) { - return false; - } - if (tma->tm_yday != tmb->tm_yday) { - return false; - } - if (tma->tm_isdst != tmb->tm_isdst) { - return false; - } - - return true; -} - -static void tm_print(FILE *file, const struct tm *tm) { - fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_wday, tm->tm_yday, - tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); -} - -int main(void) { - if (setenv("TZ", "UTC0", true) != 0) { - perror("setenv()"); - return EXIT_FAILURE; - } - - struct tm tm = { - .tm_isdst = -1, - }; - - for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) - for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) - for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) - for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) - for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) - for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { - struct tm tma = tm, tmb = tm; - time_t ta, tb; - ta = mktime(&tma); - if (xtimegm(&tmb, &tb) != 0) { - tb = -1; - } - - bool fail = false; - if (ta != tb) { - printf("Mismatch: %jd != %jd\n", (intmax_t)ta, (intmax_t)tb); - fail = true; - } - if (ta != -1 && !tm_equal(&tma, &tmb)) { - printf("mktime(): "); - tm_print(stdout, &tma); - printf("xtimegm(): "); - tm_print(stdout, &tmb); - fail = true; - } - if (fail) { - printf("Input: "); - tm_print(stdout, &tm); - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} diff --git a/tests/xtouch.c b/tests/xtouch.c index 9a91ec7..cd41842 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,28 +1,17 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes <tavianator@tavianator.com> * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ - -#include "../src/bfstd.h" -#include "../src/xtime.h" +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "bfstd.h" +#include "sanity.h" +#include "xtime.h" #include <errno.h> #include <fcntl.h> -#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> +#include <sys/types.h> #include <time.h> #include <unistd.h> @@ -49,85 +38,128 @@ struct args { mode_t pmode; }; -/** Compute flags for fstatat()/utimensat(). */ -static int at_flags(const struct args *args) { - if (args->flags & NO_FOLLOW) { - return AT_SYMLINK_NOFOLLOW; - } else { - return 0; +/** Open (and maybe create) a single directory. */ +static int open_dir(const struct args *args, int dfd, const char *path) { + int ret = openat(dfd, path, O_SEARCH | O_DIRECTORY); + + if (ret < 0 && errno == ENOENT && (args->flags & CREATE_PARENTS)) { + if (mkdirat(dfd, path, args->pmode) == 0 || errno == EEXIST) { + ret = openat(dfd, path, O_SEARCH | O_DIRECTORY); + } } + + return ret; } -/** Create any parent directories of the given path. */ -static int mkdirs(const char *path, mode_t mode) { - char *copy = strdup(path); - if (!copy) { +/** Open (and maybe create) the parent directory of the path. */ +static int open_parent(const struct args *args, const char **path) { + size_t max = xbaseoff(*path); + if (max == 0) { + return AT_FDCWD; + } + + char *dir = strndup(*path, max); + if (!dir) { return -1; } - int ret = -1; - char *cur = copy + strspn(copy, "/"); - while (true) { - cur += strcspn(cur, "/"); + // Optimistically try the whole path first + int dfd = open_dir(args, AT_FDCWD, dir); + if (dfd >= 0) { + goto done; + } - char *next = cur + strspn(cur, "/"); - if (!*next) { - ret = 0; - break; + if (errno == ENOENT) { + if (!(args->flags & CREATE_PARENTS)) { + goto err; } + } else if (!errno_is_like(ENAMETOOLONG)) { + goto err; + } - *cur = '\0'; - if (mkdir(copy, mode) != 0 && errno != EEXIST) { - break; + // Open the parents one-at-a-time + dfd = AT_FDCWD; + char *cur = dir; + while (*cur) { + char *next = cur; + next += strcspn(next, "/"); + next += strspn(next, "/"); + + char c = *next; + *next = '\0'; + + int parent = dfd; + dfd = open_dir(args, parent, cur); + if (parent >= 0) { + close_quietly(parent); } - *cur = '/'; + if (dfd < 0) { + goto err; + } + + *next = c; cur = next; } - free(copy); - return ret; +done: + *path += max; +err: + free(dir); + return dfd; +} + +/** Compute flags for fstatat()/utimensat(). */ +static int at_flags(const struct args *args) { + if (args->flags & NO_FOLLOW) { + return AT_SYMLINK_NOFOLLOW; + } else { + return 0; + } } /** Touch one path. */ static int xtouch(const struct args *args, const char *path) { - int ret = utimensat(AT_FDCWD, path, args->times, at_flags(args)); + int dfd = open_parent(args, &path); + if (dfd < 0 && dfd != AT_FDCWD) { + return -1; + } + + int ret = utimensat(dfd, path, args->times, at_flags(args)); if (ret == 0 || errno != ENOENT) { - return ret; + goto done; } if (args->flags & NO_CREATE) { - return 0; - } else if (args->flags & CREATE_PARENTS) { - if (mkdirs(path, args->pmode) != 0) { - return -1; - } + ret = 0; + goto done; } size_t len = strlen(path); if (len > 0 && path[len - 1] == '/') { - if (mkdir(path, args->dmode) != 0) { - return -1; + if (mkdirat(dfd, path, args->dmode) == 0) { + ret = utimensat(dfd, path, args->times, at_flags(args)); } - - return utimensat(AT_FDCWD, path, args->times, at_flags(args)); } else { - int fd = open(path, O_WRONLY | O_CREAT, args->fmode); - if (fd < 0) { - return -1; - } - - if (futimens(fd, args->times) != 0) { - int error = errno; - close(fd); - errno = error; - return -1; + int fd = openat(dfd, path, O_WRONLY | O_CREAT, args->fmode); + if (fd >= 0) { + if (futimens(fd, args->times) == 0) { + ret = xclose(fd); + } else { + close_quietly(fd); + } } + } - return close(fd); +done: + if (dfd >= 0) { + close_quietly(dfd); } + return ret; } int main(int argc, char *argv[]) { + tzset(); + mode_t mask = umask(0); struct args args = { @@ -187,6 +219,8 @@ int main(int argc, char *argv[]) { if (marg) { char *end; long mode = strtol(marg, &end, 8); + // https://github.com/llvm/llvm-project/issues/64946 + sanitize_init(&end); if (*marg && !*end && mode >= 0 && mode < 01000) { args.fmode = args.dmode = mode; } else { @@ -200,14 +234,14 @@ int main(int argc, char *argv[]) { if (rarg) { struct stat buf; if (fstatat(AT_FDCWD, rarg, &buf, at_flags(&args)) != 0) { - fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, strerror(errno)); + fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, xstrerror(errno)); return EXIT_FAILURE; } - times[0] = buf.st_atim; - times[1] = buf.st_mtim; + times[0] = ST_ATIM(buf); + times[1] = ST_MTIM(buf); } else if (darg) { if (xgetdate(darg, ×[0]) != 0) { - fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, strerror(errno)); + fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, xstrerror(errno)); return EXIT_FAILURE; } times[1] = times[0]; @@ -240,7 +274,7 @@ int main(int argc, char *argv[]) { for (; optind < argc; ++optind) { const char *path = argv[optind]; if (xtouch(&args, path) != 0) { - fprintf(stderr, "%s: '%s': %s\n", cmd, path, strerror(errno)); + fprintf(stderr, "%s: '%s': %s\n", cmd, path, xstrerror(errno)); ret = EXIT_FAILURE; } } |