summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/alloc.c78
-rw-r--r--tests/bfs/L_noerror.out11
-rw-r--r--tests/bfs/L_noerror.sh1
-rw-r--r--tests/bfs/color_ca.out4
-rw-r--r--tests/bfs/color_ca.sh10
-rw-r--r--tests/bfs/color_ca_incapable.out27
-rw-r--r--tests/bfs/color_ca_incapable.sh1
-rw-r--r--tests/bfs/color_ext_case_flipflop.out27
-rw-r--r--tests/bfs/color_ext_case_flipflop.sh1
-rw-r--r--tests/bfs/color_ext_case_nul.out27
-rw-r--r--tests/bfs/color_ext_case_nul.sh5
-rw-r--r--tests/bfs/color_ext_case_priority.out27
-rw-r--r--tests/bfs/color_ext_case_priority.sh1
-rw-r--r--tests/bfs/color_ls.out4
-rw-r--r--tests/bfs/color_notdir_slash_error.out0
-rw-r--r--tests/bfs/color_notdir_slash_error.sh2
-rw-r--r--tests/bfs/execdir_path_relative_slash.out19
-rw-r--r--tests/bfs/execdir_path_relative_slash.sh1
-rw-r--r--tests/bfs/files0_from_root.sh2
-rw-r--r--tests/bfs/noerror.out4
-rw-r--r--tests/bfs/noerror.sh1
-rw-r--r--tests/bfs/noerror_nowarn.sh2
-rw-r--r--tests/bfs/noerror_warn.sh2
-rw-r--r--tests/bfs/perm_leading_plus_symbolic.out3
-rw-r--r--tests/bfs/warn_without_noerror.sh2
-rw-r--r--tests/bfstd.c233
-rw-r--r--tests/bit.c203
-rw-r--r--tests/bsd/perm_000_plus.out37
-rw-r--r--tests/bsd/perm_222_plus.out24
-rw-r--r--tests/bsd/perm_644_plus.out33
-rw-r--r--tests/color.sh8
-rw-r--r--tests/common/L_mount.out5
-rw-r--r--tests/common/amin.sh2
-rw-r--r--tests/common/gid.out (renamed from tests/gnu/gid.out)0
-rw-r--r--tests/common/gid.sh (renamed from tests/gnu/gid.sh)0
-rw-r--r--tests/common/gid_invalid_id.sh1
-rw-r--r--tests/common/gid_invalid_name.sh1
-rw-r--r--tests/common/gid_minus.out (renamed from tests/gnu/gid_minus.out)0
-rw-r--r--tests/common/gid_minus.sh (renamed from tests/gnu/gid_minus.sh)0
-rw-r--r--tests/common/gid_minus_plus.out (renamed from tests/gnu/gid_minus_plus.out)0
-rw-r--r--tests/common/gid_minus_plus.sh (renamed from tests/gnu/gid_minus_plus.sh)0
-rw-r--r--tests/common/gid_plus.out (renamed from tests/gnu/gid_plus.out)0
-rw-r--r--tests/common/gid_plus.sh (renamed from tests/gnu/gid_plus.sh)0
-rw-r--r--tests/common/gid_plus_plus.out (renamed from tests/gnu/gid_plus_plus.out)0
-rw-r--r--tests/common/gid_plus_plus.sh (renamed from tests/gnu/gid_plus_plus.sh)0
-rw-r--r--tests/common/iname.sh2
-rw-r--r--tests/common/mmin.sh2
-rw-r--r--tests/common/name_slash.sh1
-rw-r--r--tests/common/name_slashes.sh1
-rw-r--r--tests/common/uid.out (renamed from tests/gnu/uid.out)0
-rw-r--r--tests/common/uid.sh (renamed from tests/gnu/uid.sh)0
-rw-r--r--tests/common/uid_invalid_id.sh1
-rw-r--r--tests/common/uid_invalid_name.sh1
-rw-r--r--tests/common/uid_minus.out (renamed from tests/gnu/uid_minus.out)0
-rw-r--r--tests/common/uid_minus.sh (renamed from tests/gnu/uid_minus.sh)0
-rw-r--r--tests/common/uid_minus_plus.out (renamed from tests/gnu/uid_minus_plus.out)0
-rw-r--r--tests/common/uid_minus_plus.sh (renamed from tests/gnu/uid_minus_plus.sh)0
-rw-r--r--tests/common/uid_plus.out (renamed from tests/gnu/uid_plus.out)0
-rw-r--r--tests/common/uid_plus.sh (renamed from tests/gnu/uid_plus.sh)0
-rw-r--r--tests/common/uid_plus_plus.out (renamed from tests/gnu/uid_plus_plus.out)0
-rw-r--r--tests/common/uid_plus_plus.sh (renamed from tests/gnu/uid_plus_plus.sh)0
-rw-r--r--tests/getopts.sh7
-rw-r--r--tests/gnu/L_printf_types.out17
-rw-r--r--tests/gnu/L_printf_types.sh1
-rw-r--r--tests/gnu/execdir_self.out1
-rw-r--r--tests/gnu/execdir_self.sh9
-rw-r--r--tests/gnu/executable.out23
-rw-r--r--tests/gnu/files0_from_empty.sh2
-rw-r--r--tests/gnu/files0_from_file_file.out2
-rw-r--r--tests/gnu/files0_from_file_file.sh3
-rw-r--r--tests/gnu/files0_from_ok.sh1
-rw-r--r--tests/gnu/files0_from_stdin_ok.sh1
-rw-r--r--tests/gnu/files0_from_stdin_ok_file.out45
-rw-r--r--tests/gnu/files0_from_stdin_ok_file.sh4
-rw-r--r--tests/gnu/files0_from_stdin_stdin.out45
-rw-r--r--tests/gnu/files0_from_stdin_stdin.sh2
-rw-r--r--tests/gnu/fls_overflow.sh4
-rw-r--r--tests/gnu/follow_files0_from.out42
-rw-r--r--tests/gnu/follow_files0_from.sh1
-rw-r--r--tests/gnu/fprint_unreached_error.sh (renamed from tests/gnu/fprint_noerror.sh)0
-rw-r--r--tests/gnu/fstype_btrfs_subvol.out4
-rw-r--r--tests/gnu/fstype_btrfs_subvol.sh25
-rw-r--r--tests/gnu/ignore_readdir_race_rmdir.out2
-rw-r--r--tests/gnu/ignore_readdir_race_rmdir.sh5
-rw-r--r--tests/gnu/not_comma.out34
-rw-r--r--tests/gnu/not_comma.sh2
-rw-r--r--tests/gnu/ok_files0_from_stdin.sh1
-rw-r--r--tests/gnu/ok_flush.sh2
-rw-r--r--tests/gnu/perm_000_slash.out37
-rw-r--r--tests/gnu/perm_222_slash.out24
-rw-r--r--tests/gnu/perm_644_slash.out33
-rw-r--r--tests/gnu/perm_leading_plus_symbolic_slash.out35
-rw-r--r--tests/gnu/perm_symbolic_slash.out31
-rw-r--r--tests/gnu/readable.out24
-rw-r--r--tests/gnu/writable.out24
-rw-r--r--tests/ioq.c11
-rw-r--r--tests/list.c10
-rw-r--r--tests/main.c197
-rw-r--r--tests/mksock.c1
-rw-r--r--tests/posix/L_mount.out2
-rw-r--r--tests/posix/L_mount.sh (renamed from tests/common/L_mount.sh)0
-rw-r--r--tests/posix/atime.out (renamed from tests/common/atime.out)0
-rw-r--r--tests/posix/atime.sh (renamed from tests/common/atime.sh)8
-rw-r--r--tests/posix/exec_return.out18
-rw-r--r--tests/posix/exec_return.sh1
-rw-r--r--tests/posix/exec_sigmask.out1
-rw-r--r--tests/posix/exec_sigmask.sh16
-rw-r--r--tests/posix/exec_substring_plus.out19
-rw-r--r--tests/posix/exec_substring_plus.sh14
-rw-r--r--tests/posix/group_invalid_id.sh1
-rw-r--r--tests/posix/group_invalid_name.sh1
-rw-r--r--tests/posix/group_o_group.out19
-rw-r--r--tests/posix/group_o_group.sh3
-rw-r--r--tests/posix/iname.out (renamed from tests/common/iname.out)0
-rw-r--r--tests/posix/iname.sh1
-rw-r--r--tests/posix/mount.out (renamed from tests/common/mount.out)1
-rw-r--r--tests/posix/mount.sh (renamed from tests/common/mount.sh)0
-rw-r--r--tests/posix/mtime.out (renamed from tests/common/mtime.out)0
-rw-r--r--tests/posix/mtime.sh (renamed from tests/common/mtime.sh)8
-rw-r--r--tests/posix/name_slash.out (renamed from tests/common/name_slash.out)0
-rw-r--r--tests/posix/name_slash.sh1
-rw-r--r--tests/posix/name_slashes.out (renamed from tests/common/name_slashes.out)0
-rw-r--r--tests/posix/name_slashes.sh1
-rw-r--r--tests/posix/perm_000.out2
-rw-r--r--tests/posix/perm_000_minus.out37
-rw-r--r--tests/posix/perm_222.out2
-rw-r--r--tests/posix/perm_222_minus.out6
-rw-r--r--tests/posix/perm_644.out2
-rw-r--r--tests/posix/perm_644_minus.out12
-rw-r--r--tests/posix/perm_leading_plus_symbolic_minus.out7
-rw-r--r--tests/posix/perm_leading_plus_umask.out10
-rw-r--r--tests/posix/perm_leading_plus_umask.sh3
-rw-r--r--tests/posix/perm_symbolic_minus.out12
-rw-r--r--tests/posix/permcopy.out2
-rw-r--r--tests/posix/print0.out (renamed from tests/gnu/print0.out)bin16 -> 16 bytes
-rw-r--r--tests/posix/print0.sh (renamed from tests/gnu/print0.sh)0
-rw-r--r--tests/posix/root_order.out4
-rw-r--r--tests/posix/root_order.sh6
-rw-r--r--tests/posix/user_invalid_id.sh1
-rw-r--r--tests/posix/user_invalid_name.sh1
-rw-r--r--tests/posix/user_o_user.out19
-rw-r--r--tests/posix/user_o_user.sh3
-rw-r--r--tests/ptyx.c252
-rw-r--r--tests/run.sh37
-rw-r--r--tests/sighook.c241
-rw-r--r--tests/stddirs.sh34
-rw-r--r--tests/tests.h57
-rw-r--r--tests/trie.c62
-rw-r--r--tests/util.sh28
-rw-r--r--tests/xspawn.c197
-rw-r--r--tests/xtime.c134
-rw-r--r--tests/xtouch.c13
152 files changed, 2178 insertions, 650 deletions
diff --git a/tests/alloc.c b/tests/alloc.c
index 6c0defd..4aae515 100644
--- a/tests/alloc.c
+++ b/tests/alloc.c
@@ -1,52 +1,78 @@
// 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;
+struct flexible {
+ alignas(64) int foo[8];
+ int bar[];
+};
- // 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);
+/** Check varena_realloc() poisoning for a size combination. */
+static struct flexible *check_varena_realloc(struct varena *varena, struct flexible *flexy, size_t old_count, size_t new_count) {
+ flexy = varena_realloc(varena, flexy, old_count, new_count);
+ bfs_everify(flexy);
- 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));
+ for (size_t i = 0; i < new_count; ++i) {
+ if (i < old_count) {
+ bfs_check(flexy->bar[i] == (int)i);
+ } else {
+ flexy->bar[i] = i;
+ }
+ }
- // 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);
+ return flexy;
+}
- // Make sure we detect allocation size overflows
-#if __GNUC__ && !__clang__
-# pragma GCC diagnostic ignored "-Walloc-size-larger-than="
-#endif
+void check_alloc(void) {
+ // Check aligned allocation
+ void *ptr;
+ bfs_everify((ptr = zalloc(64, 129)));
+ bfs_check((uintptr_t)ptr % 64 == 0);
+ bfs_echeck((ptr = xrealloc(ptr, 64, 129, 65)));
+ bfs_check((uintptr_t)ptr % 64 == 0);
+ free(ptr);
- 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);
+ // Check sizeof_flex()
+ bfs_check(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible));
+ bfs_check(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0);
+
+ // volatile to suppress -Walloc-size-larger-than
+ volatile size_t too_many = SIZE_MAX / sizeof(int) + 1;
+ bfs_check(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX));
+
+ // Make sure we detect allocation size overflows
+ bfs_check(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW);
+ bfs_check(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW);
+ bfs_check(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW);
+ 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));
+ bfs_everify(varena_alloc(&varena, i));
struct arena *arena = &varena.arenas[varena.narenas - 1];
- ret &= bfs_check(arena->size >= sizeof_flex(struct flexible, bar, i));
+ bfs_check(arena->size >= sizeof_flex(struct flexible, bar, i));
}
+ // Check varena_realloc() (un)poisoning
+ struct flexible *flexy = varena_alloc(&varena, 160);
+ bfs_everify(flexy);
+
+ flexy = check_varena_realloc(&varena, flexy, 0, 160);
+ flexy = check_varena_realloc(&varena, flexy, 160, 192);
+ flexy = check_varena_realloc(&varena, flexy, 192, 160);
+ flexy = check_varena_realloc(&varena, flexy, 160, 320);
+ flexy = check_varena_realloc(&varena, flexy, 320, 96);
+
varena_destroy(&varena);
- return ret;
}
diff --git a/tests/bfs/L_noerror.out b/tests/bfs/L_noerror.out
new file mode 100644
index 0000000..a514555
--- /dev/null
+++ b/tests/bfs/L_noerror.out
@@ -0,0 +1,11 @@
+loops
+loops/broken
+loops/deeply
+loops/deeply/nested
+loops/deeply/nested/dir
+loops/file
+loops/notdir
+loops/skip
+loops/skip/dir
+loops/skip/loop
+loops/symlink
diff --git a/tests/bfs/L_noerror.sh b/tests/bfs/L_noerror.sh
new file mode 100644
index 0000000..7db2a4d
--- /dev/null
+++ b/tests/bfs/L_noerror.sh
@@ -0,0 +1 @@
+bfs_diff -L loops -noerror
diff --git a/tests/bfs/color_ca.out b/tests/bfs/color_ca.out
new file mode 100644
index 0000000..bf74202
--- /dev/null
+++ b/tests/bfs/color_ca.out
@@ -0,0 +1,4 @@
+.
+./link
+./capable
+./normal
diff --git a/tests/bfs/color_ca.sh b/tests/bfs/color_ca.sh
new file mode 100644
index 0000000..3aaaaf1
--- /dev/null
+++ b/tests/bfs/color_ca.sh
@@ -0,0 +1,10 @@
+test "$UNAME" = "Linux" || skip
+invoke_bfs . -quit -capable || skip
+
+cd "$TEST"
+
+"$XTOUCH" normal capable
+bfs_sudo setcap all+ep capable || skip
+ln -s capable link
+
+LS_COLORS="ca=30;41:" bfs_diff . -color
diff --git a/tests/bfs/color_ca_incapable.out b/tests/bfs/color_ca_incapable.out
new file mode 100644
index 0000000..a439814
--- /dev/null
+++ b/tests/bfs/color_ca_incapable.out
@@ -0,0 +1,27 @@
+$'rainbow/\e[1m'
+$'rainbow/\e[1m/'$'\e[0m'
+rainbow
+rainbow/exec.sh
+rainbow/socket
+rainbow/broken
+rainbow/chardev_link
+rainbow/link.txt
+rainbow/sticky_ow
+rainbow/sgid
+rainbow/pipe
+rainbow/ow
+rainbow/sugid
+rainbow/suid
+rainbow/sticky
+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/ul.TAR.gz
+rainbow/upper.GZ
+rainbow/upper.TAR
+rainbow/upper.TAR.GZ
diff --git a/tests/bfs/color_ca_incapable.sh b/tests/bfs/color_ca_incapable.sh
new file mode 100644
index 0000000..f46a127
--- /dev/null
+++ b/tests/bfs/color_ca_incapable.sh
@@ -0,0 +1 @@
+LS_COLORS="ca=30;41:" bfs_diff rainbow -color
diff --git a/tests/bfs/color_ext_case_flipflop.out b/tests/bfs/color_ext_case_flipflop.out
new file mode 100644
index 0000000..f4cc53c
--- /dev/null
+++ b/tests/bfs/color_ext_case_flipflop.out
@@ -0,0 +1,27 @@
+$'rainbow/\e[1m'
+$'rainbow/\e[1m/'$'\e[0m'
+rainbow
+rainbow/exec.sh
+rainbow/lower.tar.gz
+rainbow/lu.tar.GZ
+rainbow/ul.TAR.gz
+rainbow/upper.TAR.GZ
+rainbow/socket
+rainbow/broken
+rainbow/chardev_link
+rainbow/link.txt
+rainbow/sticky_ow
+rainbow/sgid
+rainbow/pipe
+rainbow/ow
+rainbow/sugid
+rainbow/suid
+rainbow/sticky
+rainbow/file.dat
+rainbow/file.txt
+rainbow/lower.gz
+rainbow/lower.tar
+rainbow/mh1
+rainbow/mh2
+rainbow/upper.GZ
+rainbow/upper.TAR
diff --git a/tests/bfs/color_ext_case_flipflop.sh b/tests/bfs/color_ext_case_flipflop.sh
new file mode 100644
index 0000000..4d6f615
--- /dev/null
+++ b/tests/bfs/color_ext_case_flipflop.sh
@@ -0,0 +1 @@
+LS_COLORS="*.tar.gz=01;31:*.TAR.GZ=01;32:*.TAR.GZ=01;33:*.tar.gz=01;33:" bfs_diff rainbow -color
diff --git a/tests/bfs/color_ext_case_nul.out b/tests/bfs/color_ext_case_nul.out
new file mode 100644
index 0000000..8ccd9a7
--- /dev/null
+++ b/tests/bfs/color_ext_case_nul.out
@@ -0,0 +1,27 @@
+$'rainbow/\e[1m'
+$'rainbow/\e[1m/'$'\e[0m'
+rainbow
+rainbow/lower.gz
+rainbow/lower.tar.gz
+rainbow/lu.tar.GZ
+rainbow/ul.TAR.gz
+rainbow/upper.GZ
+rainbow/upper.TAR.GZ
+rainbow/exec.sh
+rainbow/socket
+rainbow/broken
+rainbow/chardev_link
+rainbow/link.txt
+rainbow/sticky_ow
+rainbow/sgid
+rainbow/pipe
+rainbow/ow
+rainbow/sugid
+rainbow/suid
+rainbow/sticky
+rainbow/file.dat
+rainbow/file.txt
+rainbow/lower.tar
+rainbow/mh1
+rainbow/mh2
+rainbow/upper.TAR
diff --git a/tests/bfs/color_ext_case_nul.sh b/tests/bfs/color_ext_case_nul.sh
new file mode 100644
index 0000000..68fea1c
--- /dev/null
+++ b/tests/bfs/color_ext_case_nul.sh
@@ -0,0 +1,5 @@
+# Regression test: embedded NUL bytes in an extension caused an assertion
+# failure in the trie implementation
+
+export LS_COLORS='*.gz=01;31:*\0.GZ=01;32:'
+bfs_diff rainbow -color
diff --git a/tests/bfs/color_ext_case_priority.out b/tests/bfs/color_ext_case_priority.out
new file mode 100644
index 0000000..4a6c9a0
--- /dev/null
+++ b/tests/bfs/color_ext_case_priority.out
@@ -0,0 +1,27 @@
+$'rainbow/\e[1m'
+$'rainbow/\e[1m/'$'\e[0m'
+rainbow
+rainbow/lower.tar.gz
+rainbow/exec.sh
+rainbow/upper.TAR.GZ
+rainbow/lower.gz
+rainbow/lu.tar.GZ
+rainbow/ul.TAR.gz
+rainbow/upper.GZ
+rainbow/socket
+rainbow/broken
+rainbow/chardev_link
+rainbow/link.txt
+rainbow/sticky_ow
+rainbow/sgid
+rainbow/pipe
+rainbow/ow
+rainbow/sugid
+rainbow/suid
+rainbow/sticky
+rainbow/file.dat
+rainbow/file.txt
+rainbow/lower.tar
+rainbow/mh1
+rainbow/mh2
+rainbow/upper.TAR
diff --git a/tests/bfs/color_ext_case_priority.sh b/tests/bfs/color_ext_case_priority.sh
new file mode 100644
index 0000000..f178c56
--- /dev/null
+++ b/tests/bfs/color_ext_case_priority.sh
@@ -0,0 +1 @@
+LS_COLORS="*.gz=01;33:*.tar.gz=01;31:*.TAR.GZ=01;32:" bfs_diff rainbow -color
diff --git a/tests/bfs/color_ls.out b/tests/bfs/color_ls.out
index f69eb9c..cc64318 100644
--- a/tests/bfs/color_ls.out
+++ b/tests/bfs/color_ls.out
@@ -8,5 +8,5 @@
foo/bar/nowhere/nothing
foo/bar/baz
foo/bar/baz
-foo/bar/baz//qux
-foo/bar/baz//qux
+foo/bar/baz//qux
+foo/bar/baz//qux
diff --git a/tests/bfs/color_notdir_slash_error.out b/tests/bfs/color_notdir_slash_error.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/bfs/color_notdir_slash_error.out
diff --git a/tests/bfs/color_notdir_slash_error.sh b/tests/bfs/color_notdir_slash_error.sh
new file mode 100644
index 0000000..ca26d50
--- /dev/null
+++ b/tests/bfs/color_notdir_slash_error.sh
@@ -0,0 +1,2 @@
+# Regression test: infinite loop printing the error message for .../notdir/nowhere
+! bfs_diff -color links/notdir/nowhere
diff --git a/tests/bfs/execdir_path_relative_slash.out b/tests/bfs/execdir_path_relative_slash.out
new file mode 100644
index 0000000..62b31f6
--- /dev/null
+++ b/tests/bfs/execdir_path_relative_slash.out
@@ -0,0 +1,19 @@
+./a
+./b
+./bar
+./bar
+./basic
+./baz
+./c
+./d
+./e
+./f
+./foo
+./foo
+./foo
+./g
+./h
+./i
+./j
+./k
+./l
diff --git a/tests/bfs/execdir_path_relative_slash.sh b/tests/bfs/execdir_path_relative_slash.sh
new file mode 100644
index 0000000..fb5a924
--- /dev/null
+++ b/tests/bfs/execdir_path_relative_slash.sh
@@ -0,0 +1 @@
+PATH="foo:$PATH" bfs_diff basic -execdir /bin/sh -c 'printf "%s\\n" "$@"' sh {} +
diff --git a/tests/bfs/files0_from_root.sh b/tests/bfs/files0_from_root.sh
new file mode 100644
index 0000000..6ba5f00
--- /dev/null
+++ b/tests/bfs/files0_from_root.sh
@@ -0,0 +1,2 @@
+printf 'basic\0' >"$TEST/input"
+! invoke_bfs basic -files0-from "$TEST/input"
diff --git a/tests/bfs/noerror.out b/tests/bfs/noerror.out
new file mode 100644
index 0000000..c4f8ce4
--- /dev/null
+++ b/tests/bfs/noerror.out
@@ -0,0 +1,4 @@
+inaccessible
+inaccessible/dir
+inaccessible/file
+inaccessible/link
diff --git a/tests/bfs/noerror.sh b/tests/bfs/noerror.sh
new file mode 100644
index 0000000..e334f8f
--- /dev/null
+++ b/tests/bfs/noerror.sh
@@ -0,0 +1 @@
+bfs_diff inaccessible -noerror
diff --git a/tests/bfs/noerror_nowarn.sh b/tests/bfs/noerror_nowarn.sh
new file mode 100644
index 0000000..26e7e68
--- /dev/null
+++ b/tests/bfs/noerror_nowarn.sh
@@ -0,0 +1,2 @@
+stderr=$(invoke_bfs inaccessible -noerror -nowarn 2>&1 >/dev/null)
+[ -z "$stderr" ]
diff --git a/tests/bfs/noerror_warn.sh b/tests/bfs/noerror_warn.sh
new file mode 100644
index 0000000..ec85f4c
--- /dev/null
+++ b/tests/bfs/noerror_warn.sh
@@ -0,0 +1,2 @@
+stderr=$(invoke_bfs inaccessible -noerror -warn 2>&1 >/dev/null)
+[ -n "$stderr" ]
diff --git a/tests/bfs/perm_leading_plus_symbolic.out b/tests/bfs/perm_leading_plus_symbolic.out
index e69de29..09bc88f 100644
--- a/tests/bfs/perm_leading_plus_symbolic.out
+++ b/tests/bfs/perm_leading_plus_symbolic.out
@@ -0,0 +1,3 @@
+perms
+perms/drwxr-xr-x
+perms/frwxr-xr-x
diff --git a/tests/bfs/warn_without_noerror.sh b/tests/bfs/warn_without_noerror.sh
new file mode 100644
index 0000000..5167309
--- /dev/null
+++ b/tests/bfs/warn_without_noerror.sh
@@ -0,0 +1,2 @@
+# bfs shouldn't print "warning: Suppressed errors" without -noerror
+! invoke_bfs inaccessible -warn 2>&1 >/dev/null | grep warning >&2
diff --git a/tests/bfstd.c b/tests/bfstd.c
index f0f61ce..6e15e2b 100644
--- a/tests/bfstd.c
+++ b/tests/bfstd.c
@@ -1,93 +1,212 @@
// 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 <limits.h>
+#include <stdint.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;
+/** asciilen() test cases. */
+static void check_asciilen(void) {
+ bfs_check(asciilen("") == 0);
+ bfs_check(asciilen("@") == 1);
+ bfs_check(asciilen("@@") == 2);
+ bfs_check(asciilen("\xFF@") == 0);
+ bfs_check(asciilen("@\xFF") == 1);
+ bfs_check(asciilen("@@@@@@@@") == 8);
+ bfs_check(asciilen("@@@@@@@@@@@@@@@@") == 16);
+ bfs_check(asciilen("@@@@@@@@@@@@@@@@@@@@@@@@") == 24);
+ bfs_check(asciilen("@@@@@@@@@@@@@@a\xFF@@@@@@@") == 15);
+ bfs_check(asciilen("@@@@@@@@@@@@@@@@\xFF@@@@@@@") == 16);
+ bfs_check(asciilen("@@@@@@@@@@@@@@@@a\xFF@@@@@@") == 17);
+ bfs_check(asciilen("@@@@@@@\xFF@@@@@@a\xFF@@@@@@@") == 7);
+ bfs_check(asciilen("@@@@@@@@\xFF@@@@@a\xFF@@@@@@@") == 8);
+ bfs_check(asciilen("@@@@@@@@@\xFF@@@@a\xFF@@@@@@@") == 9);
+}
+/** Check the result of xdirname()/xbasename(). */
+static void check_base_dir(const char *path, const char *dir, const char *base) {
char *xdir = xdirname(path);
bfs_everify(xdir, "xdirname()");
- ret &= bfs_check(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir);
+ bfs_check(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir);
free(xdir);
char *xbase = xbasename(path);
bfs_everify(xbase, "xbasename()");
- ret &= bfs_check(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base);
+ bfs_check(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base);
free(xbase);
+}
- return ret;
+/** xdirname()/xbasename() test cases. */
+static void check_basedirs(void) {
+ // From man 3p basename
+ check_base_dir("usr", ".", "usr");
+ check_base_dir("usr/", ".", "usr");
+ check_base_dir("", ".", ".");
+ check_base_dir("/", "/", "/");
+ // check_base_dir("//", "/" or "//", "/" or "//");
+ check_base_dir("///", "/", "/");
+ check_base_dir("/usr/", "/", "usr");
+ check_base_dir("/usr/lib", "/usr", "lib");
+ check_base_dir("//usr//lib//", "//usr", "lib");
+ check_base_dir("/home//dwc//test", "/home//dwc", "test");
}
/** Check the result of wordesc(). */
-static bool check_wordesc(const char *str, const char *exp, enum wesc_flags flags) {
+static void 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);
+ if (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);
+/** wordesc() test cases. */
+static void check_wordescs(void) {
+ check_wordesc("", "\"\"", WESC_SHELL);
+ check_wordesc("word", "word", WESC_SHELL);
+ check_wordesc("two words", "\"two words\"", WESC_SHELL);
+ check_wordesc("word's", "\"word's\"", WESC_SHELL);
+ check_wordesc("\"word\"", "'\"word\"'", WESC_SHELL);
+ check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL);
+ check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY);
+ check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY);
+ 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);
+ check_wordesc("\xF0", "$'\\xF0'", WESC_SHELL | WESC_TTY);
+ check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY);
+ check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY);
+ check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY);
+ check_wordesc("\xCB\x9Cuser", "\xCB\x9Cuser", WESC_SHELL);
}
+}
+
+/** xstrto*() test cases. */
+static void check_strtox(void) {
+ short s;
+ unsigned short us;
+ int i;
+ unsigned int ui;
+ long l;
+ unsigned long ul;
+ long long ll;
+ unsigned long long ull;
+ char *end;
+
+#define check_strtouerr(err, str, end, base) \
+ do { \
+ bfs_echeck(xstrtous(str, end, base, &us) != 0 && errno == err); \
+ bfs_echeck(xstrtoui(str, end, base, &ui) != 0 && errno == err); \
+ bfs_echeck(xstrtoul(str, end, base, &ul) != 0 && errno == err); \
+ bfs_echeck(xstrtoull(str, end, base, &ull) != 0 && errno == err); \
+ } while (0)
+
+ check_strtouerr(ERANGE, "-1", NULL, 0);
+ check_strtouerr(ERANGE, "-0x1", NULL, 0);
- ret &= bfs_check(xstrwidth("Hello world") == 11);
- ret &= bfs_check(xstrwidth("Hello\1world") == 10);
+ check_strtouerr(EINVAL, "-", NULL, 0);
+ check_strtouerr(EINVAL, "-q", NULL, 0);
+ check_strtouerr(EINVAL, "-1q", NULL, 0);
+ check_strtouerr(EINVAL, "-0x", NULL, 0);
+
+#define check_strtoerr(err, str, end, base) \
+ do { \
+ bfs_echeck(xstrtos(str, end, base, &s) != 0 && errno == err); \
+ bfs_echeck(xstrtoi(str, end, base, &i) != 0 && errno == err); \
+ bfs_echeck(xstrtol(str, end, base, &l) != 0 && errno == err); \
+ bfs_echeck(xstrtoll(str, end, base, &ll) != 0 && errno == err); \
+ check_strtouerr(err, str, end, base); \
+ } while (0)
+
+ check_strtoerr(EINVAL, "", NULL, 0);
+ check_strtoerr(EINVAL, "", &end, 0);
+ check_strtoerr(EINVAL, " 1 ", &end, 0);
+ check_strtoerr(EINVAL, " -1", NULL, 0);
+ check_strtoerr(EINVAL, " 123", NULL, 0);
+ check_strtoerr(EINVAL, "123 ", NULL, 0);
+ check_strtoerr(EINVAL, "0789", NULL, 0);
+ check_strtoerr(EINVAL, "789A", NULL, 0);
+ check_strtoerr(EINVAL, "0x", NULL, 0);
+ check_strtoerr(EINVAL, "0x789A", NULL, 10);
+ check_strtoerr(EINVAL, "0x-1", NULL, 0);
+
+#define check_strtotype(type, min, max, fmt, fn, str, base, v, n) \
+ do { \
+ if ((n) >= min && (n) <= max) { \
+ bfs_echeck(fn(str, NULL, base, &v) == 0); \
+ bfs_check(v == (type)(n), "%s('%s') == " fmt " (!= " fmt ")", #fn, str, v, (type)(n)); \
+ } else { \
+ bfs_echeck(fn(str, NULL, base, &v) != 0 && errno == ERANGE); \
+ } \
+ } while (0)
+
+#define check_strtoint(str, base, n) \
+ do { \
+ check_strtotype( signed short, SHRT_MIN, SHRT_MAX, "%d", xstrtos, str, base, s, n); \
+ check_strtotype( signed int, INT_MIN, INT_MAX, "%d", xstrtoi, str, base, i, n); \
+ check_strtotype( signed long, LONG_MIN, LONG_MAX, "%ld", xstrtol, str, base, l, n); \
+ check_strtotype( signed long long, LLONG_MIN, LLONG_MAX, "%lld", xstrtoll, str, base, ll, n); \
+ check_strtotype(unsigned short, 0, USHRT_MAX, "%u", xstrtous, str, base, us, n); \
+ check_strtotype(unsigned int, 0, UINT_MAX, "%u", xstrtoui, str, base, ui, n); \
+ check_strtotype(unsigned long, 0, ULONG_MAX, "%lu", xstrtoul, str, base, ul, n); \
+ check_strtotype(unsigned long long, 0, ULLONG_MAX, "%llu", xstrtoull, str, base, ull, n); \
+ } while (0)
+
+ check_strtoint("123", 0, 123);
+ check_strtoint("+123", 0, 123);
+ check_strtoint("-123", 0, -123);
+
+ check_strtoint("0123", 0, 0123);
+ check_strtoint("0x789A", 0, 0x789A);
+
+ check_strtoint("0123", 10, 123);
+ check_strtoint("0789", 10, 789);
+
+ check_strtoint("123", 16, 0x123);
+
+ check_strtoint("0x7FFF", 0, 0x7FFF);
+ check_strtoint("-0x8000", 0, -0x8000);
+
+ check_strtoint("0x7FFFFFFF", 0, 0x7FFFFFFFL);
+ check_strtoint("-0x80000000", 0, -0x7FFFFFFFL - 1);
+
+ check_strtoint("0x7FFFFFFFFFFFFFFF", 0, 0x7FFFFFFFFFFFFFFFLL);
+ check_strtoint("-0x8000000000000000", 0, -0x7FFFFFFFFFFFFFFFLL - 1);
+
+#define check_strtoend(str, estr, base, n) \
+ do { \
+ bfs_echeck(xstrtoll(str, &end, base, &ll) == 0); \
+ bfs_check(ll == (n), "xstrtoll('%s') == %lld (!= %lld)", str, ll, (long long)(n)); \
+ bfs_check(strcmp(end, estr) == 0, "xstrtoll('%s'): end == '%s' (!= '%s')", str, end, estr); \
+ } while (0)
+
+ check_strtoend("123 ", " ", 0, 123);
+ check_strtoend("0789", "89", 0, 07);
+ check_strtoend("789A", "A", 0, 789);
+ check_strtoend("0xDEFG", "G", 0, 0xDEF);
+}
+
+/** xstrwidth() test cases. */
+static void check_strwidth(void) {
+ bfs_check(xstrwidth("Hello world") == 11);
+ bfs_check(xstrwidth("Hello\1world") == 10);
+}
- return ret;
+void check_bfstd(void) {
+ check_asciilen();
+ check_basedirs();
+ check_wordescs();
+ check_strtox();
+ check_strwidth();
}
diff --git a/tests/bit.c b/tests/bit.c
index 49e167d..09d470b 100644
--- a/tests/bit.c
+++ b/tests/bit.c
@@ -1,141 +1,160 @@
// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD
-#include "prelude.h"
#include "tests.h"
+
+#include "bfs.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);
+// Polyfill C23's one-argument static_assert()
+#if __STDC_VERSION__ < C23
+# undef static_assert
+# define static_assert(...) _Static_assert(__VA_ARGS__, #__VA_ARGS__)
+#endif
+
+static_assert(UMAX_WIDTH(0x1) == 1);
+static_assert(UMAX_WIDTH(0x3) == 2);
+static_assert(UMAX_WIDTH(0x7) == 3);
+static_assert(UMAX_WIDTH(0xF) == 4);
+static_assert(UMAX_WIDTH(0xFF) == 8);
+static_assert(UMAX_WIDTH(0xFFF) == 12);
+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));
+static_assert(UCHAR_MAX == UWIDTH_MAX(UCHAR_WIDTH));
+static_assert(SCHAR_MIN == IWIDTH_MIN(SCHAR_WIDTH));
+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));
+static_assert(USHRT_MAX == UWIDTH_MAX(USHRT_WIDTH));
+static_assert(SHRT_MIN == IWIDTH_MIN(SHRT_WIDTH));
+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));
+static_assert(UINT_MAX == UWIDTH_MAX(UINT_WIDTH));
+static_assert(INT_MIN == IWIDTH_MIN(INT_WIDTH));
+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));
+static_assert(ULONG_MAX == UWIDTH_MAX(ULONG_WIDTH));
+static_assert(LONG_MIN == IWIDTH_MIN(LONG_WIDTH));
+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));
+static_assert(ULLONG_MAX == UWIDTH_MAX(ULLONG_WIDTH));
+static_assert(LLONG_MIN == IWIDTH_MIN(LLONG_WIDTH));
+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));
+static_assert(SIZE_MAX == UWIDTH_MAX(SIZE_WIDTH));
+static_assert(PTRDIFF_MIN == IWIDTH_MIN(PTRDIFF_WIDTH));
+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));
+static_assert(UINTPTR_MAX == UWIDTH_MAX(UINTPTR_WIDTH));
+static_assert(INTPTR_MIN == IWIDTH_MIN(INTPTR_WIDTH));
+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));
+static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH));
+static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH));
+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";
+void check_bit(void) {
+ const char *str = "\x1\x2\x3\x4\x5\x6\x7\x8";
uint32_t word;
memcpy(&word, str, sizeof(word));
#if ENDIAN_NATIVE == ENDIAN_LITTLE
- ret &= check_eq(word, 0x04030201);
+ check_eq(word, 0x04030201);
#elif ENDIAN_NATIVE == ENDIAN_BIG
- ret &= check_eq(word, 0x01020304);
+ 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(0x0U), 0);
- ret &= check_eq(count_ones(0x1U), 1);
- ret &= check_eq(count_ones(0x2U), 1);
- ret &= check_eq(count_ones(0x3U), 2);
- ret &= check_eq(count_ones(0x137FU), 10);
-
- ret &= check_eq(count_zeros(0U), UINT_WIDTH);
- ret &= check_eq(count_zeros(0UL), ULONG_WIDTH);
- ret &= check_eq(count_zeros(0ULL), ULLONG_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);
+ check_eq(bswap((uint8_t)0x12), 0x12);
+ check_eq(bswap((uint16_t)0x1234), 0x3412);
+ check_eq(bswap((uint32_t)0x12345678), 0x78563412);
+ check_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412);
+
+ // Make sure we can bswap() every unsigned type
+ (void)bswap((unsigned char)0);
+ (void)bswap((unsigned short)0);
+ (void)bswap(0U);
+ (void)bswap(0UL);
+ (void)bswap(0ULL);
+
+ check_eq(load8_beu8(str), 0x01);
+ check_eq(load8_leu8(str), 0x01);
+ check_eq(load8_beu16(str), 0x0102);
+ check_eq(load8_leu16(str), 0x0201);
+ check_eq(load8_beu32(str), 0x01020304);
+ check_eq(load8_leu32(str), 0x04030201);
+ check_eq(load8_beu64(str), 0x0102030405060708ULL);
+ check_eq(load8_leu64(str), 0x0807060504030201ULL);
+
+ check_eq(count_ones(0x0U), 0);
+ check_eq(count_ones(0x1U), 1);
+ check_eq(count_ones(0x2U), 1);
+ check_eq(count_ones(0x3U), 2);
+ check_eq(count_ones(0x137FU), 10);
+
+ check_eq(count_zeros(0U), UINT_WIDTH);
+ check_eq(count_zeros(0UL), ULONG_WIDTH);
+ check_eq(count_zeros(0ULL), ULLONG_WIDTH);
+ check_eq(count_zeros((uint8_t)0), 8);
+ check_eq(count_zeros((uint16_t)0), 16);
+ check_eq(count_zeros((uint32_t)0), 32);
+ check_eq(count_zeros((uint64_t)0), 64);
+
+ check_eq(rotate_left((uint8_t)0xA1, 4), 0x1A);
+ check_eq(rotate_left((uint16_t)0x1234, 12), 0x4123);
+ check_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345);
+ check_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678);
+
+ check_eq(rotate_right((uint8_t)0xA1, 4), 0x1A);
+ check_eq(rotate_right((uint16_t)0x1234, 12), 0x2341);
+ check_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123);
+ check_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678);
for (unsigned int i = 0; i < 16; ++i) {
uint16_t n = (uint16_t)1 << i;
for (unsigned 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), 16 - j);
- ret &= check_eq(first_trailing_one(nm), i + 1);
- ret &= check_eq(bit_width(nm), j + 1);
- ret &= check_eq(bit_floor(nm), m);
+ check_eq(count_ones(nm), 1 + (n != m));
+ check_eq(count_zeros(nm), 15 - (n != m));
+ check_eq(leading_zeros(nm), 15 - j);
+ check_eq(trailing_zeros(nm), i);
+ check_eq(first_leading_one(nm), 16 - j);
+ check_eq(first_trailing_one(nm), i + 1);
+ check_eq(bit_width(nm), j + 1);
+ check_eq(bit_floor(nm), m);
if (n == m) {
- ret &= check_eq(bit_ceil(nm), m);
- ret &= bfs_check(has_single_bit(nm));
+ check_eq(bit_ceil(nm), m);
+ bfs_check(has_single_bit(nm));
} else {
if (j < 15) {
- ret &= check_eq(bit_ceil(nm), (m << 1));
+ check_eq(bit_ceil(nm), (m << 1));
}
- ret &= bfs_check(!has_single_bit(nm));
+ 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(0U), 0);
- ret &= check_eq(first_trailing_one(0U), 0);
- ret &= check_eq(bit_width(0U), 0);
- ret &= check_eq(bit_floor(0U), 0);
- ret &= check_eq(bit_ceil(0U), 1);
-
- ret &= bfs_check(!has_single_bit(0U));
- ret &= bfs_check(!has_single_bit(UINT32_MAX));
- ret &= bfs_check(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1)));
-
- return ret;
+ check_eq(leading_zeros((uint16_t)0), 16);
+ check_eq(trailing_zeros((uint16_t)0), 16);
+ check_eq(first_leading_one(0U), 0);
+ check_eq(first_trailing_one(0U), 0);
+ check_eq(bit_width(0U), 0);
+ check_eq(bit_floor(0U), 0);
+ check_eq(bit_ceil(0U), 1);
+
+ bfs_check(!has_single_bit(0U));
+ bfs_check(!has_single_bit(UINT32_MAX));
+ bfs_check(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1)));
}
diff --git a/tests/bsd/perm_000_plus.out b/tests/bsd/perm_000_plus.out
index 42f2fed..e279684 100644
--- a/tests/bsd/perm_000_plus.out
+++ b/tests/bsd/perm_000_plus.out
@@ -1,10 +1,29 @@
perms
-perms/dr-x
-perms/drwx
-perms/f---
-perms/f-w-
-perms/f-wx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f---------
+perms/f--x------
+perms/f--x--x--x
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr--------
+perms/fr--r--r--
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/bsd/perm_222_plus.out b/tests/bsd/perm_222_plus.out
index 5c78ecc..1b6d885 100644
--- a/tests/bsd/perm_222_plus.out
+++ b/tests/bsd/perm_222_plus.out
@@ -1,6 +1,20 @@
perms
-perms/drwx
-perms/f-w-
-perms/f-wx
-perms/frw-
-perms/frwx
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/bsd/perm_644_plus.out b/tests/bsd/perm_644_plus.out
index 774c0ea..eef88ca 100644
--- a/tests/bsd/perm_644_plus.out
+++ b/tests/bsd/perm_644_plus.out
@@ -1,9 +1,26 @@
perms
-perms/dr-x
-perms/drwx
-perms/f-w-
-perms/f-wx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr--------
+perms/fr--r--r--
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/color.sh b/tests/color.sh
index 4f4312e..9e2e0f6 100644
--- a/tests/color.sh
+++ b/tests/color.sh
@@ -46,9 +46,7 @@ show_bar() {
# Name the pipe deterministically based on the ttyname, so that concurrent
# tests.sh runs on the same terminal (e.g. make -jN check) cooperate
- local pipe
- pipe=$(printf '%s' "$TTY" | tr '/' '-')
- pipe="${TMPDIR:-/tmp}/bfs$pipe.bar"
+ local pipe="${TMPDIR:-/tmp}/bfs${TTY//\//-}.bar"
if mkfifo "$pipe" 2>/dev/null; then
# We won the race, create the background process to manage the bar
@@ -78,7 +76,7 @@ hide_bar() {
# The background process that muxes multiple status bars for one TTY
bar_proc() {
# Read from the pipe, write to the TTY
- exec <"$1" >"$TTY"
+ exec <"$1" >/dev/tty
# Delete the pipe when done
defer rm "$1"
@@ -133,7 +131,7 @@ bar_proc() {
# Resize the status bar
resize_bar() {
# Bash gets $LINES from stderr, so if it's redirected use tput instead
- TTY_HEIGHT="${LINES:-$(tput lines 2>"$TTY")}"
+ TTY_HEIGHT="${LINES:-$(tput lines 2>/dev/tty)}"
if ((BAR_HEIGHT == 0)); then
return
diff --git a/tests/common/L_mount.out b/tests/common/L_mount.out
deleted file mode 100644
index 788579d..0000000
--- a/tests/common/L_mount.out
+++ /dev/null
@@ -1,5 +0,0 @@
-.
-./foo
-./foo/bar
-./foo/qux
-./mnt
diff --git a/tests/common/amin.sh b/tests/common/amin.sh
index c4d53fb..92c3531 100644
--- a/tests/common/amin.sh
+++ b/tests/common/amin.sh
@@ -9,7 +9,7 @@ now=$(epoch_time)
"$XTOUCH" -at "@$((now + 60))" in_one_minute
"$XTOUCH" -at "@$((now + 60 * 60))" in_one_hour
-bfs_diff -mindepth 1 \
+bfs_diff . -mindepth 1 \
\( -amin -1 -exec printf -- '-amin -1: %s\n' {} \; -o -true \) \
\( -amin 1 -exec printf -- '-amin 1: %s\n' {} \; -o -true \) \
\( -amin +1 -exec printf -- '-amin +1: %s\n' {} \; -o -true \)
diff --git a/tests/gnu/gid.out b/tests/common/gid.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/gid.out
+++ b/tests/common/gid.out
diff --git a/tests/gnu/gid.sh b/tests/common/gid.sh
index 2707b4a..2707b4a 100644
--- a/tests/gnu/gid.sh
+++ b/tests/common/gid.sh
diff --git a/tests/common/gid_invalid_id.sh b/tests/common/gid_invalid_id.sh
new file mode 100644
index 0000000..74f0055
--- /dev/null
+++ b/tests/common/gid_invalid_id.sh
@@ -0,0 +1 @@
+! invoke_bfs -gid 1eW6f5RM9Qi
diff --git a/tests/common/gid_invalid_name.sh b/tests/common/gid_invalid_name.sh
new file mode 100644
index 0000000..0e2e5f5
--- /dev/null
+++ b/tests/common/gid_invalid_name.sh
@@ -0,0 +1 @@
+! invoke_bfs -gid eW6f5RM9Qi
diff --git a/tests/gnu/gid_minus.out b/tests/common/gid_minus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/gid_minus.out
+++ b/tests/common/gid_minus.out
diff --git a/tests/gnu/gid_minus.sh b/tests/common/gid_minus.sh
index e3822f0..e3822f0 100644
--- a/tests/gnu/gid_minus.sh
+++ b/tests/common/gid_minus.sh
diff --git a/tests/gnu/gid_minus_plus.out b/tests/common/gid_minus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/gid_minus_plus.out
+++ b/tests/common/gid_minus_plus.out
diff --git a/tests/gnu/gid_minus_plus.sh b/tests/common/gid_minus_plus.sh
index 4ff0877..4ff0877 100644
--- a/tests/gnu/gid_minus_plus.sh
+++ b/tests/common/gid_minus_plus.sh
diff --git a/tests/gnu/gid_plus.out b/tests/common/gid_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/gid_plus.out
+++ b/tests/common/gid_plus.out
diff --git a/tests/gnu/gid_plus.sh b/tests/common/gid_plus.sh
index ccba0e6..ccba0e6 100644
--- a/tests/gnu/gid_plus.sh
+++ b/tests/common/gid_plus.sh
diff --git a/tests/gnu/gid_plus_plus.out b/tests/common/gid_plus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/gid_plus_plus.out
+++ b/tests/common/gid_plus_plus.out
diff --git a/tests/gnu/gid_plus_plus.sh b/tests/common/gid_plus_plus.sh
index ec7ae86..ec7ae86 100644
--- a/tests/gnu/gid_plus_plus.sh
+++ b/tests/common/gid_plus_plus.sh
diff --git a/tests/common/iname.sh b/tests/common/iname.sh
deleted file mode 100644
index c25a646..0000000
--- a/tests/common/iname.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-invoke_bfs -quit -iname PATTERN || skip
-bfs_diff basic -iname '*F*'
diff --git a/tests/common/mmin.sh b/tests/common/mmin.sh
index 38e3337..4e1d19c 100644
--- a/tests/common/mmin.sh
+++ b/tests/common/mmin.sh
@@ -9,7 +9,7 @@ now=$(epoch_time)
"$XTOUCH" -mt "@$((now + 60))" in_one_minute
"$XTOUCH" -mt "@$((now + 60 * 60))" in_one_hour
-bfs_diff -mindepth 1 \
+bfs_diff . -mindepth 1 \
\( -mmin -1 -exec printf -- '-mmin -1: %s\n' {} \; -o -true \) \
\( -mmin 1 -exec printf -- '-mmin 1: %s\n' {} \; -o -true \) \
\( -mmin +1 -exec printf -- '-mmin +1: %s\n' {} \; -o -true \)
diff --git a/tests/common/name_slash.sh b/tests/common/name_slash.sh
deleted file mode 100644
index 8d89623..0000000
--- a/tests/common/name_slash.sh
+++ /dev/null
@@ -1 +0,0 @@
-bfs_diff / -maxdepth 0 -name /
diff --git a/tests/common/name_slashes.sh b/tests/common/name_slashes.sh
deleted file mode 100644
index 78d0a84..0000000
--- a/tests/common/name_slashes.sh
+++ /dev/null
@@ -1 +0,0 @@
-bfs_diff /// -maxdepth 0 -name /
diff --git a/tests/gnu/uid.out b/tests/common/uid.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/uid.out
+++ b/tests/common/uid.out
diff --git a/tests/gnu/uid.sh b/tests/common/uid.sh
index fb3cd93..fb3cd93 100644
--- a/tests/gnu/uid.sh
+++ b/tests/common/uid.sh
diff --git a/tests/common/uid_invalid_id.sh b/tests/common/uid_invalid_id.sh
new file mode 100644
index 0000000..f5b952d
--- /dev/null
+++ b/tests/common/uid_invalid_id.sh
@@ -0,0 +1 @@
+! invoke_bfs -uid 1eW6f5RM9Qi
diff --git a/tests/common/uid_invalid_name.sh b/tests/common/uid_invalid_name.sh
new file mode 100644
index 0000000..a2c359f
--- /dev/null
+++ b/tests/common/uid_invalid_name.sh
@@ -0,0 +1 @@
+! invoke_bfs -uid eW6f5RM9Qi
diff --git a/tests/gnu/uid_minus.out b/tests/common/uid_minus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/uid_minus.out
+++ b/tests/common/uid_minus.out
diff --git a/tests/gnu/uid_minus.sh b/tests/common/uid_minus.sh
index 6d371f2..6d371f2 100644
--- a/tests/gnu/uid_minus.sh
+++ b/tests/common/uid_minus.sh
diff --git a/tests/gnu/uid_minus_plus.out b/tests/common/uid_minus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/uid_minus_plus.out
+++ b/tests/common/uid_minus_plus.out
diff --git a/tests/gnu/uid_minus_plus.sh b/tests/common/uid_minus_plus.sh
index e7a0496..e7a0496 100644
--- a/tests/gnu/uid_minus_plus.sh
+++ b/tests/common/uid_minus_plus.sh
diff --git a/tests/gnu/uid_plus.out b/tests/common/uid_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/uid_plus.out
+++ b/tests/common/uid_plus.out
diff --git a/tests/gnu/uid_plus.sh b/tests/common/uid_plus.sh
index 22b2c8e..22b2c8e 100644
--- a/tests/gnu/uid_plus.sh
+++ b/tests/common/uid_plus.sh
diff --git a/tests/gnu/uid_plus_plus.out b/tests/common/uid_plus_plus.out
index a7ccfe4..a7ccfe4 100644
--- a/tests/gnu/uid_plus_plus.out
+++ b/tests/common/uid_plus_plus.out
diff --git a/tests/gnu/uid_plus_plus.sh b/tests/common/uid_plus_plus.sh
index e021888..e021888 100644
--- a/tests/gnu/uid_plus_plus.sh
+++ b/tests/common/uid_plus_plus.sh
diff --git a/tests/getopts.sh b/tests/getopts.sh
index 5214e9f..a16511f 100644
--- a/tests/getopts.sh
+++ b/tests/getopts.sh
@@ -5,11 +5,7 @@
## Argument parsing
-if command -v nproc &>/dev/null; then
- JOBS=$(nproc)
-else
- JOBS=1
-fi
+JOBS=$(_nproc)
MAKE=
PATTERNS=()
SUDO=()
@@ -23,7 +19,6 @@ 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}]]
diff --git a/tests/gnu/L_printf_types.out b/tests/gnu/L_printf_types.out
new file mode 100644
index 0000000..734b15f
--- /dev/null
+++ b/tests/gnu/L_printf_types.out
@@ -0,0 +1,17 @@
+(links) () d d
+(links/broken) (nowhere) l N
+(links/deeply) () d d
+(links/deeply/nested) () d d
+(links/deeply/nested/broken) (nowhere) l N
+(links/deeply/nested/dir) () d d
+(links/deeply/nested/file) () f f
+(links/deeply/nested/link) () f f
+(links/file) () f f
+(links/hardlink) () f f
+(links/notdir) (symlink/file) l N
+(links/skip) () d d
+(links/skip/broken) (nowhere) l N
+(links/skip/dir) () d d
+(links/skip/file) () f f
+(links/skip/link) () f f
+(links/symlink) () f f
diff --git a/tests/gnu/L_printf_types.sh b/tests/gnu/L_printf_types.sh
new file mode 100644
index 0000000..caa9083
--- /dev/null
+++ b/tests/gnu/L_printf_types.sh
@@ -0,0 +1 @@
+bfs_diff -L links -printf '(%p) (%l) %y %Y\n'
diff --git a/tests/gnu/execdir_self.out b/tests/gnu/execdir_self.out
new file mode 100644
index 0000000..3ad0640
--- /dev/null
+++ b/tests/gnu/execdir_self.out
@@ -0,0 +1 @@
+./bar.sh
diff --git a/tests/gnu/execdir_self.sh b/tests/gnu/execdir_self.sh
new file mode 100644
index 0000000..1fc5d04
--- /dev/null
+++ b/tests/gnu/execdir_self.sh
@@ -0,0 +1,9 @@
+cd "$TEST"
+mkdir foo
+cat >foo/bar.sh <<EOF
+#!/bin/sh
+printf '%s\n' "\$@"
+EOF
+chmod +x foo/bar.sh
+
+bfs_diff . -name bar.sh -execdir {} {} \;
diff --git a/tests/gnu/executable.out b/tests/gnu/executable.out
index 08965bf..e256554 100644
--- a/tests/gnu/executable.out
+++ b/tests/gnu/executable.out
@@ -1,6 +1,19 @@
perms
-perms/dr-x
-perms/drwx
-perms/f-wx
-perms/fr-x
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f--x------
+perms/f--x--x--x
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/gnu/files0_from_empty.sh b/tests/gnu/files0_from_empty.sh
index 85eee8f..7b42772 100644
--- a/tests/gnu/files0_from_empty.sh
+++ b/tests/gnu/files0_from_empty.sh
@@ -1 +1 @@
-! printf "\0" | invoke_bfs -files0-from -
+! printf '\0' | invoke_bfs -files0-from -
diff --git a/tests/gnu/files0_from_file_file.out b/tests/gnu/files0_from_file_file.out
new file mode 100644
index 0000000..fb683c7
--- /dev/null
+++ b/tests/gnu/files0_from_file_file.out
@@ -0,0 +1,2 @@
+basic/g
+basic/g/h
diff --git a/tests/gnu/files0_from_file_file.sh b/tests/gnu/files0_from_file_file.sh
new file mode 100644
index 0000000..1119952
--- /dev/null
+++ b/tests/gnu/files0_from_file_file.sh
@@ -0,0 +1,3 @@
+printf 'basic/c\0' >"$TEST/in1"
+printf 'basic/g\0' >"$TEST/in2"
+bfs_diff -files0-from "$TEST/in1" -files0-from "$TEST/in2"
diff --git a/tests/gnu/files0_from_ok.sh b/tests/gnu/files0_from_ok.sh
deleted file mode 100644
index 8e145ce..0000000
--- a/tests/gnu/files0_from_ok.sh
+++ /dev/null
@@ -1 +0,0 @@
-! printf "basic\0" | invoke_bfs -files0-from - -ok echo {} \;
diff --git a/tests/gnu/files0_from_stdin_ok.sh b/tests/gnu/files0_from_stdin_ok.sh
new file mode 100644
index 0000000..0283c8d
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_ok.sh
@@ -0,0 +1 @@
+! printf 'basic\0' | invoke_bfs -files0-from - -ok echo {} \;
diff --git a/tests/gnu/files0_from_stdin_ok_file.out b/tests/gnu/files0_from_stdin_ok_file.out
new file mode 100644
index 0000000..0f6b00d
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_ok_file.out
@@ -0,0 +1,45 @@
+
+
+
+
+
+ /j
+ /j
+!
+!-
+!-/e
+!-/e
+!/d
+!/d
+(
+(-
+(-/c
+(-/c
+(/b
+(/b
+)
+)/g
+)/g
+*
+*/m
+*/m
+,
+,/f
+,/f
+-
+-/a
+-/a
+...
+.../h
+.../h
+/n
+/n
+[
+[/k
+[/k
+\
+\/i
+\/i
+{
+{/l
+{/l
diff --git a/tests/gnu/files0_from_stdin_ok_file.sh b/tests/gnu/files0_from_stdin_ok_file.sh
new file mode 100644
index 0000000..028df0c
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_ok_file.sh
@@ -0,0 +1,4 @@
+FILE="$TMP/$TEST.in"
+cd weirdnames
+invoke_bfs -mindepth 1 -fprintf "$FILE" "%P\0"
+yes | bfs_diff -files0-from - -ok printf '%s\n' {} \; -files0-from "$FILE"
diff --git a/tests/gnu/files0_from_stdin_stdin.out b/tests/gnu/files0_from_stdin_stdin.out
new file mode 100644
index 0000000..0f6b00d
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_stdin.out
@@ -0,0 +1,45 @@
+
+
+
+
+
+ /j
+ /j
+!
+!-
+!-/e
+!-/e
+!/d
+!/d
+(
+(-
+(-/c
+(-/c
+(/b
+(/b
+)
+)/g
+)/g
+*
+*/m
+*/m
+,
+,/f
+,/f
+-
+-/a
+-/a
+...
+.../h
+.../h
+/n
+/n
+[
+[/k
+[/k
+\
+\/i
+\/i
+{
+{/l
+{/l
diff --git a/tests/gnu/files0_from_stdin_stdin.sh b/tests/gnu/files0_from_stdin_stdin.sh
new file mode 100644
index 0000000..8f6368f
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_stdin.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+invoke_bfs -mindepth 1 -printf "%P\0" | bfs_diff -files0-from - -files0-from -
diff --git a/tests/gnu/fls_overflow.sh b/tests/gnu/fls_overflow.sh
new file mode 100644
index 0000000..067bc86
--- /dev/null
+++ b/tests/gnu/fls_overflow.sh
@@ -0,0 +1,4 @@
+# Regression test: times that overflow localtime() should still print
+cd "$TEST"
+"$XTOUCH" -t "@1111111111111111111" overflow || skip
+invoke_bfs . -fls "$OUT"
diff --git a/tests/gnu/follow_files0_from.out b/tests/gnu/follow_files0_from.out
new file mode 100644
index 0000000..c77d546
--- /dev/null
+++ b/tests/gnu/follow_files0_from.out
@@ -0,0 +1,42 @@
+links
+links/broken
+links/broken
+links/deeply
+links/deeply
+links/deeply/nested
+links/deeply/nested
+links/deeply/nested
+links/deeply/nested/broken
+links/deeply/nested/broken
+links/deeply/nested/broken
+links/deeply/nested/broken
+links/deeply/nested/dir
+links/deeply/nested/dir
+links/deeply/nested/dir
+links/deeply/nested/dir
+links/deeply/nested/file
+links/deeply/nested/file
+links/deeply/nested/file
+links/deeply/nested/file
+links/deeply/nested/link
+links/deeply/nested/link
+links/deeply/nested/link
+links/deeply/nested/link
+links/file
+links/file
+links/hardlink
+links/hardlink
+links/notdir
+links/notdir
+links/skip
+links/skip
+links/skip/broken
+links/skip/broken
+links/skip/dir
+links/skip/dir
+links/skip/file
+links/skip/file
+links/skip/link
+links/skip/link
+links/symlink
+links/symlink
diff --git a/tests/gnu/follow_files0_from.sh b/tests/gnu/follow_files0_from.sh
new file mode 100644
index 0000000..8c20f6d
--- /dev/null
+++ b/tests/gnu/follow_files0_from.sh
@@ -0,0 +1 @@
+invoke_bfs links -print0 | bfs_diff -follow -files0-from -
diff --git a/tests/gnu/fprint_noerror.sh b/tests/gnu/fprint_unreached_error.sh
index f13a62b..f13a62b 100644
--- a/tests/gnu/fprint_noerror.sh
+++ b/tests/gnu/fprint_unreached_error.sh
diff --git a/tests/gnu/fstype_btrfs_subvol.out b/tests/gnu/fstype_btrfs_subvol.out
new file mode 100644
index 0000000..8871fb9
--- /dev/null
+++ b/tests/gnu/fstype_btrfs_subvol.out
@@ -0,0 +1,4 @@
+mnt
+mnt/file
+mnt/subvol
+mnt/subvol/file
diff --git a/tests/gnu/fstype_btrfs_subvol.sh b/tests/gnu/fstype_btrfs_subvol.sh
new file mode 100644
index 0000000..71df45c
--- /dev/null
+++ b/tests/gnu/fstype_btrfs_subvol.sh
@@ -0,0 +1,25 @@
+# Test that -fstype works in btrfs subvolumes
+
+command -v btrfs &>/dev/null || skip
+
+cd "$TEST"
+
+# Make a btrfs filesystem image
+truncate -s128M img
+mkfs.btrfs img >&2
+
+# Mount it
+mkdir mnt
+bfs_sudo mount img mnt || skip
+defer bfs_sudo umount mnt
+
+# Make it owned by us
+bfs_sudo chown "$(id -u):$(id -g)" mnt
+
+# Create a subvolume inside it
+btrfs subvolume create mnt/subvol >&2
+
+# Make a file in and outside the subvolume
+"$XTOUCH" mnt/file mnt/subvol/file
+
+bfs_diff mnt -fstype btrfs -print -o -printf '%p %F\n'
diff --git a/tests/gnu/ignore_readdir_race_rmdir.out b/tests/gnu/ignore_readdir_race_rmdir.out
new file mode 100644
index 0000000..ede8749
--- /dev/null
+++ b/tests/gnu/ignore_readdir_race_rmdir.out
@@ -0,0 +1,2 @@
+./bar
+./foo
diff --git a/tests/gnu/ignore_readdir_race_rmdir.sh b/tests/gnu/ignore_readdir_race_rmdir.sh
new file mode 100644
index 0000000..87f36a9
--- /dev/null
+++ b/tests/gnu/ignore_readdir_race_rmdir.sh
@@ -0,0 +1,5 @@
+cd "$TEST"
+"$XTOUCH" -p foo/ bar/
+
+# Check that -ignore_readdir_race suppresses errors from opendir()
+bfs_diff . -ignore_readdir_race -mindepth 1 -print -name foo -exec rmdir {} \;
diff --git a/tests/gnu/not_comma.out b/tests/gnu/not_comma.out
new file mode 100644
index 0000000..b90468e
--- /dev/null
+++ b/tests/gnu/not_comma.out
@@ -0,0 +1,34 @@
+basic
+basic
+basic/a
+basic/a
+basic/b
+basic/b
+basic/c
+basic/c
+basic/c/d
+basic/c/d
+basic/e
+basic/e
+basic/e/f
+basic/g
+basic/g
+basic/g/h
+basic/g/h
+basic/i
+basic/i
+basic/j
+basic/j
+basic/j/foo
+basic/k
+basic/k
+basic/k/foo
+basic/k/foo/bar
+basic/k/foo/bar
+basic/l
+basic/l
+basic/l/foo
+basic/l/foo/bar
+basic/l/foo/bar
+basic/l/foo/bar/baz
+basic/l/foo/bar/baz
diff --git a/tests/gnu/not_comma.sh b/tests/gnu/not_comma.sh
new file mode 100644
index 0000000..04c0195
--- /dev/null
+++ b/tests/gnu/not_comma.sh
@@ -0,0 +1,2 @@
+# Regression test: assertion failure in sink_not_comma()
+bfs_diff basic -not \( -print , -name '*f*' \) -print
diff --git a/tests/gnu/ok_files0_from_stdin.sh b/tests/gnu/ok_files0_from_stdin.sh
new file mode 100644
index 0000000..2c4de7b
--- /dev/null
+++ b/tests/gnu/ok_files0_from_stdin.sh
@@ -0,0 +1 @@
+! printf 'basic\0' | invoke_bfs -ok echo {} \; -files0-from -
diff --git a/tests/gnu/ok_flush.sh b/tests/gnu/ok_flush.sh
index 87c7298..a5dc0d0 100644
--- a/tests/gnu/ok_flush.sh
+++ b/tests/gnu/ok_flush.sh
@@ -1,4 +1,4 @@
# I/O streams should be flushed before -ok prompts
-yes | invoke_bfs basic -printf '%p ? ' -ok echo found \; 2>&1 | tr '\0' ' ' | sed 's/?.*?/?/' >"$OUT"
+yes | invoke_bfs basic -printf '%p ? ' -ok echo found \; 2>&1 | sed 's/?.*?/?/' >"$OUT"
sort_output
diff_output
diff --git a/tests/gnu/perm_000_slash.out b/tests/gnu/perm_000_slash.out
index 42f2fed..e279684 100644
--- a/tests/gnu/perm_000_slash.out
+++ b/tests/gnu/perm_000_slash.out
@@ -1,10 +1,29 @@
perms
-perms/dr-x
-perms/drwx
-perms/f---
-perms/f-w-
-perms/f-wx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f---------
+perms/f--x------
+perms/f--x--x--x
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr--------
+perms/fr--r--r--
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/gnu/perm_222_slash.out b/tests/gnu/perm_222_slash.out
index 5c78ecc..1b6d885 100644
--- a/tests/gnu/perm_222_slash.out
+++ b/tests/gnu/perm_222_slash.out
@@ -1,6 +1,20 @@
perms
-perms/drwx
-perms/f-w-
-perms/f-wx
-perms/frw-
-perms/frwx
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/gnu/perm_644_slash.out b/tests/gnu/perm_644_slash.out
index 774c0ea..eef88ca 100644
--- a/tests/gnu/perm_644_slash.out
+++ b/tests/gnu/perm_644_slash.out
@@ -1,9 +1,26 @@
perms
-perms/dr-x
-perms/drwx
-perms/f-w-
-perms/f-wx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr--------
+perms/fr--r--r--
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/gnu/perm_leading_plus_symbolic_slash.out b/tests/gnu/perm_leading_plus_symbolic_slash.out
index 774c0ea..fcbf49e 100644
--- a/tests/gnu/perm_leading_plus_symbolic_slash.out
+++ b/tests/gnu/perm_leading_plus_symbolic_slash.out
@@ -1,9 +1,28 @@
perms
-perms/dr-x
-perms/drwx
-perms/f-w-
-perms/f-wx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f--x------
+perms/f--x--x--x
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr--------
+perms/fr--r--r--
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/gnu/perm_symbolic_slash.out b/tests/gnu/perm_symbolic_slash.out
index 774c0ea..5a21321 100644
--- a/tests/gnu/perm_symbolic_slash.out
+++ b/tests/gnu/perm_symbolic_slash.out
@@ -1,9 +1,24 @@
perms
-perms/dr-x
-perms/drwx
-perms/f-w-
-perms/f-wx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr--r--r--
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/gnu/readable.out b/tests/gnu/readable.out
index 285aa43..56d1f52 100644
--- a/tests/gnu/readable.out
+++ b/tests/gnu/readable.out
@@ -1,7 +1,19 @@
perms
-perms/dr-x
-perms/drwx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/fr--------
+perms/fr--r--r--
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/gnu/writable.out b/tests/gnu/writable.out
index 5c78ecc..1b6d885 100644
--- a/tests/gnu/writable.out
+++ b/tests/gnu/writable.out
@@ -1,6 +1,20 @@
perms
-perms/drwx
-perms/f-w-
-perms/f-wx
-perms/frw-
-perms/frwx
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/ioq.c b/tests/ioq.c
index 99c98a2..1a0da97 100644
--- a/tests/ioq.c
+++ b/tests/ioq.c
@@ -1,13 +1,12 @@
// 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 "ioq.h"
+
#include <fcntl.h>
#include <stdlib.h>
@@ -50,6 +49,7 @@ static void check_ioq_push_block(void) {
int ret = ioq_opendir(ioq, dir, AT_FDCWD, ".", 0, NULL);
bfs_everify(ret == 0, "ioq_opendir()");
}
+ ioq_submit(ioq);
bfs_verify(ioq_capacity(ioq) == 0);
// Now cancel the queue, pushing an additional IOQ_STOP message
@@ -71,7 +71,6 @@ static void check_ioq_push_block(void) {
ioq_destroy(ioq);
}
-bool check_ioq(void) {
+void check_ioq(void) {
check_ioq_push_block();
- return true;
}
diff --git a/tests/list.c b/tests/list.c
index e14570f..5d0403f 100644
--- a/tests/list.c
+++ b/tests/list.c
@@ -1,10 +1,14 @@
// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD
-#include "prelude.h"
#include "tests.h"
+
+#include "bfs.h"
+#include "diag.h"
#include "list.h"
+#include <stddef.h>
+
struct item {
int n;
struct item *next;
@@ -41,7 +45,7 @@ static bool check_list_items(struct list *list, int *array, size_t size) {
#define ARRAY(...) (int[]){ __VA_ARGS__ }, countof((int[]){ __VA_ARGS__ })
#define EMPTY() NULL, 0
-bool check_list(void) {
+void check_list(void) {
struct list l1;
SLIST_INIT(&l1);
bfs_verify(check_list_items(&l1, EMPTY()));
@@ -92,6 +96,4 @@ bool check_list(void) {
SLIST_APPEND(&l2, &i12);
SLIST_SPLICE(&l1, &l1.head->next, &l2);
bfs_verify(check_list_items(&l1, ARRAY(10, 11, 12, 15, 20)));
-
- return true;
}
diff --git a/tests/main.c b/tests/main.c
index bef2e37..9240e1c 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -5,18 +5,45 @@
* Entry point for unit tests.
*/
-#include "prelude.h"
#include "tests.h"
+
+#include "alloc.h"
#include "bfstd.h"
#include "color.h"
+#include "list.h"
+
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
+#include <sys/wait.h>
#include <time.h>
+#include <unistd.h>
+
+/** Result of the current test. */
+static bool pass;
+
+bool bfs_check_impl(bool result) {
+ pass &= result;
+ return result;
+}
+
+/**
+ * A running test.
+ */
+struct test_proc {
+ /** Linked list links. */
+ struct test_proc *prev, *next;
+
+ /** The PID of this test. */
+ pid_t pid;
+ /** The name of this test. */
+ const char *name;
+};
/**
- * Test context.
+ * Global test context.
*/
struct test_ctx {
/** Number of command line arguments. */
@@ -24,6 +51,17 @@ struct test_ctx {
/** The arguments themselves. */
char **argv;
+ /** Maximum jobs (-j). */
+ int jobs;
+ /** Current jobs. */
+ int running;
+ /** Completed jobs. */
+ int done;
+ /** List of running tests. */
+ struct {
+ struct test_proc *head, *tail;
+ } procs;
+
/** Parsed colors. */
struct colors *colors;
/** Colorized output stream. */
@@ -34,10 +72,15 @@ struct test_ctx {
};
/** Initialize the test context. */
-static int test_init(struct test_ctx *ctx, int argc, char **argv) {
+static int test_init(struct test_ctx *ctx, int jobs, int argc, char **argv) {
ctx->argc = argc;
ctx->argv = argv;
+ ctx->jobs = jobs;
+ ctx->running = 0;
+ ctx->done = 0;
+ LIST_INIT(&ctx->procs);
+
ctx->colors = parse_colors();
ctx->cout = cfwrap(stdout, ctx->colors, false);
if (!ctx->cout) {
@@ -49,26 +92,15 @@ static int test_init(struct test_ctx *ctx, int argc, char **argv) {
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) {
+ if (ctx->argc == 0) {
return true;
}
// With args, run only specified tests
- for (int i = 1; i < ctx->argc; ++i) {
+ for (int i = 0; i < ctx->argc; ++i) {
if (strcmp(test, ctx->argv[i]) == 0) {
return true;
}
@@ -77,16 +109,104 @@ static bool should_run(const struct test_ctx *ctx, const char *test) {
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);
+/** Wait for a test to finish. */
+static void wait_test(struct test_ctx *ctx) {
+ int wstatus;
+ pid_t pid = xwaitpid(0, &wstatus, 0);
+ bfs_everify(pid > 0, "xwaitpid()");
+
+ struct test_proc *proc = NULL;
+ for_list (struct test_proc, i, &ctx->procs) {
+ if (i->pid == pid) {
+ proc = i;
+ break;
+ }
+ }
+
+ bfs_verify(proc, "No test_proc for PID %ju", (intmax_t)pid);
+
+ bool passed = false;
+
+ if (WIFEXITED(wstatus)) {
+ int status = WEXITSTATUS(wstatus);
+ if (status == EXIT_SUCCESS) {
+ cfprintf(ctx->cout, "${grn}[PASS]${rs} ${bld}%s${rs}\n", proc->name);
+ passed = true;
+ } else if (status == EXIT_FAILURE) {
+ cfprintf(ctx->cout, "${red}[FAIL]${rs} ${bld}%s${rs}\n", proc->name);
} else {
- cfprintf(ctx->cout, "${red}[FAIL]${rs} ${bld}%s${rs}\n", test);
- ctx->ret = EXIT_FAILURE;
+ cfprintf(ctx->cout, "${red}[FAIL]${rs} ${bld}%s${rs} (Exit %d)\n", proc->name, status);
+ }
+ } else {
+ const char *str = NULL;
+ if (WIFSIGNALED(wstatus)) {
+ str = strsignal(WTERMSIG(wstatus));
}
+ if (!str) {
+ str = "Unknown";
+ }
+ cfprintf(ctx->cout, "${red}[FAIL]${rs} ${bld}%s${rs} (%s)\n", proc->name, str);
+ }
+
+ if (!passed) {
+ ctx->ret = EXIT_FAILURE;
}
+
+ --ctx->running;
+ ++ctx->done;
+ LIST_REMOVE(&ctx->procs, proc);
+ free(proc);
+}
+
+/** Unit test function type. */
+typedef void test_fn(void);
+
+/** 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)) {
+ return;
+ }
+
+ while (ctx->running >= ctx->jobs) {
+ wait_test(ctx);
+ }
+
+ struct test_proc *proc = ALLOC(struct test_proc);
+ bfs_everify(proc, "alloc()");
+
+ LIST_ITEM_INIT(proc);
+ proc->name = test;
+
+ fflush(NULL);
+ proc->pid = fork();
+ bfs_everify(proc->pid >= 0, "fork()");
+
+ if (proc->pid > 0) {
+ // Parent
+ ++ctx->running;
+ LIST_APPEND(&ctx->procs, proc);
+ return;
+ }
+
+ // Child
+ pass = true;
+ fn();
+ exit(pass ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+/** Finalize the test context. */
+static int test_fini(struct test_ctx *ctx) {
+ while (ctx->running > 0) {
+ wait_test(ctx);
+ }
+
+ if (ctx->cout) {
+ cfclose(ctx->cout);
+ }
+
+ free_colors(ctx->colors);
+
+ return ctx->ret;
}
int main(int argc, char *argv[]) {
@@ -102,8 +222,37 @@ int main(int argc, char *argv[]) {
}
tzset();
+ unsigned int jobs = 0;
+
+ const char *cmd = argc > 0 ? argv[0] : "units";
+ int c;
+ while (c = getopt(argc, argv, ":j:"), c != -1) {
+ switch (c) {
+ case 'j':
+ if (xstrtoui(optarg, NULL, 10, &jobs) != 0) {
+ fprintf(stderr, "%s: Bad job count '%s': %s\n", cmd, optarg, errstr());
+ return EXIT_FAILURE;
+ }
+ break;
+ case ':':
+ fprintf(stderr, "%s: Missing argument to -%c\n", cmd, optopt);
+ return EXIT_FAILURE;
+ case '?':
+ fprintf(stderr, "%s: Unrecognized option -%c\n", cmd, optopt);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (!jobs) {
+ jobs = nproc();
+ }
+
+ if (optind > argc) {
+ optind = argc;
+ }
+
struct test_ctx ctx;
- if (test_init(&ctx, argc, argv) != 0) {
+ if (test_init(&ctx, jobs, argc - optind, argv + optind) != 0) {
goto done;
}
diff --git a/tests/mksock.c b/tests/mksock.c
index 5786cb6..f46df96 100644
--- a/tests/mksock.c
+++ b/tests/mksock.c
@@ -7,6 +7,7 @@
*/
#include "bfstd.h"
+
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/tests/posix/L_mount.out b/tests/posix/L_mount.out
new file mode 100644
index 0000000..7ed5f0d
--- /dev/null
+++ b/tests/posix/L_mount.out
@@ -0,0 +1,2 @@
+.
+./foo
diff --git a/tests/common/L_mount.sh b/tests/posix/L_mount.sh
index fd8042a..fd8042a 100644
--- a/tests/common/L_mount.sh
+++ b/tests/posix/L_mount.sh
diff --git a/tests/common/atime.out b/tests/posix/atime.out
index 5ed206b..5ed206b 100644
--- a/tests/common/atime.out
+++ b/tests/posix/atime.out
diff --git a/tests/common/atime.sh b/tests/posix/atime.sh
index c2e4031..25dfd7e 100644
--- a/tests/common/atime.sh
+++ b/tests/posix/atime.sh
@@ -9,7 +9,7 @@ now=$(epoch_time)
"$XTOUCH" -at "@$((now))" now
"$XTOUCH" -at "@$((now + 60 * 60 * 24))" tomorrow
-bfs_diff -mindepth 1 \
- \( -atime -1 -exec printf -- '-atime -1: %s\n' {} \; -o -true \) \
- \( -atime 1 -exec printf -- '-atime 1: %s\n' {} \; -o -true \) \
- \( -atime +1 -exec printf -- '-atime +1: %s\n' {} \; -o -true \)
+bfs_diff . \! -name . \
+ \( -atime -1 -exec printf -- '-atime -1: %s\n' {} \; -o -prune \) \
+ \( -atime 1 -exec printf -- '-atime 1: %s\n' {} \; -o -prune \) \
+ \( -atime +1 -exec printf -- '-atime +1: %s\n' {} \; -o -prune \)
diff --git a/tests/posix/exec_return.out b/tests/posix/exec_return.out
new file mode 100644
index 0000000..600c93a
--- /dev/null
+++ b/tests/posix/exec_return.out
@@ -0,0 +1,18 @@
+basic
+basic/a
+basic/b
+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/posix/exec_return.sh b/tests/posix/exec_return.sh
new file mode 100644
index 0000000..cfa0f5d
--- /dev/null
+++ b/tests/posix/exec_return.sh
@@ -0,0 +1 @@
+bfs_diff basic -exec test {} = basic/c \; -o -print
diff --git a/tests/posix/exec_sigmask.out b/tests/posix/exec_sigmask.out
new file mode 100644
index 0000000..bb646f3
--- /dev/null
+++ b/tests/posix/exec_sigmask.out
@@ -0,0 +1 @@
+SigBlk: 0000000000000000
diff --git a/tests/posix/exec_sigmask.sh b/tests/posix/exec_sigmask.sh
new file mode 100644
index 0000000..2907458
--- /dev/null
+++ b/tests/posix/exec_sigmask.sh
@@ -0,0 +1,16 @@
+# Regression test: restore the signal mask after fork()
+
+cd "$TEST"
+mkfifo p1 p2
+
+{
+ # Get the PID of `sh`
+ read -r pid <p1
+ # Send SIGTERM -- this will hang forever if signals are blocked
+ kill $pid
+} &
+
+# Write the `sh` PID to p1, then hang reading p2 until we're killed
+! invoke_bfs p1 -exec bash -c 'echo $$ >p1 && read -r _ <p2' bash {} + || fail
+
+_wait
diff --git a/tests/posix/exec_substring_plus.out b/tests/posix/exec_substring_plus.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/exec_substring_plus.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/posix/exec_substring_plus.sh b/tests/posix/exec_substring_plus.sh
new file mode 100644
index 0000000..90309b0
--- /dev/null
+++ b/tests/posix/exec_substring_plus.sh
@@ -0,0 +1,14 @@
+# https://pubs.opengroup.org/onlinepubs/9799919799/utilities/find.html
+#
+# Only a <plus-sign> that immediately follows an argument containing only
+# the two characters "{}" shall punctuate the end of the primary expression.
+# Other uses of the <plus-sign> shall not be treated as special.
+# ...
+# If a utility_name or argument string contains the two characters "{}", but
+# not just the two characters "{}", it is implementation-defined whether
+# find replaces those two characters or uses the string without change.
+
+invoke_bfs basic -exec printf '%s %s %s %s\n' {} {}+ +{} + \; | sed 's/ .*//' >"$OUT"
+sort_output
+diff_output
+
diff --git a/tests/posix/group_invalid_id.sh b/tests/posix/group_invalid_id.sh
new file mode 100644
index 0000000..1a89747
--- /dev/null
+++ b/tests/posix/group_invalid_id.sh
@@ -0,0 +1 @@
+! invoke_bfs -group 1eW6f5RM9Qi
diff --git a/tests/posix/group_invalid_name.sh b/tests/posix/group_invalid_name.sh
new file mode 100644
index 0000000..a08dc72
--- /dev/null
+++ b/tests/posix/group_invalid_name.sh
@@ -0,0 +1 @@
+! invoke_bfs -group eW6f5RM9Qi
diff --git a/tests/posix/group_o_group.out b/tests/posix/group_o_group.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/group_o_group.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/posix/group_o_group.sh b/tests/posix/group_o_group.sh
new file mode 100644
index 0000000..60aefc0
--- /dev/null
+++ b/tests/posix/group_o_group.sh
@@ -0,0 +1,3 @@
+# Regression test for
+# https://github.com/tavianator/bfs/issues/155
+bfs_diff basic -group 0 -o -group "$(id -g)"
diff --git a/tests/common/iname.out b/tests/posix/iname.out
index a9e5d42..a9e5d42 100644
--- a/tests/common/iname.out
+++ b/tests/posix/iname.out
diff --git a/tests/posix/iname.sh b/tests/posix/iname.sh
new file mode 100644
index 0000000..a9297ac
--- /dev/null
+++ b/tests/posix/iname.sh
@@ -0,0 +1 @@
+bfs_diff basic -iname '*F*'
diff --git a/tests/common/mount.out b/tests/posix/mount.out
index 6253434..b0ad937 100644
--- a/tests/common/mount.out
+++ b/tests/posix/mount.out
@@ -1,4 +1,3 @@
.
./foo
./foo/bar
-./mnt
diff --git a/tests/common/mount.sh b/tests/posix/mount.sh
index c9abde5..c9abde5 100644
--- a/tests/common/mount.sh
+++ b/tests/posix/mount.sh
diff --git a/tests/common/mtime.out b/tests/posix/mtime.out
index 91f0114..91f0114 100644
--- a/tests/common/mtime.out
+++ b/tests/posix/mtime.out
diff --git a/tests/common/mtime.sh b/tests/posix/mtime.sh
index 7ba127e..8367631 100644
--- a/tests/common/mtime.sh
+++ b/tests/posix/mtime.sh
@@ -9,7 +9,7 @@ now=$(epoch_time)
"$XTOUCH" -mt "@$((now))" now
"$XTOUCH" -mt "@$((now + 60 * 60 * 24))" tomorrow
-bfs_diff -mindepth 1 \
- \( -mtime -1 -exec printf -- '-mtime -1: %s\n' {} \; -o -true \) \
- \( -mtime 1 -exec printf -- '-mtime 1: %s\n' {} \; -o -true \) \
- \( -mtime +1 -exec printf -- '-mtime +1: %s\n' {} \; -o -true \)
+bfs_diff . \! -name . \
+ \( -mtime -1 -exec printf -- '-mtime -1: %s\n' {} \; -o -prune \) \
+ \( -mtime 1 -exec printf -- '-mtime 1: %s\n' {} \; -o -prune \) \
+ \( -mtime +1 -exec printf -- '-mtime +1: %s\n' {} \; -o -prune \)
diff --git a/tests/common/name_slash.out b/tests/posix/name_slash.out
index b498fd4..b498fd4 100644
--- a/tests/common/name_slash.out
+++ b/tests/posix/name_slash.out
diff --git a/tests/posix/name_slash.sh b/tests/posix/name_slash.sh
new file mode 100644
index 0000000..b42b145
--- /dev/null
+++ b/tests/posix/name_slash.sh
@@ -0,0 +1 @@
+bfs_diff / -prune -name /
diff --git a/tests/common/name_slashes.out b/tests/posix/name_slashes.out
index 187b81f..187b81f 100644
--- a/tests/common/name_slashes.out
+++ b/tests/posix/name_slashes.out
diff --git a/tests/posix/name_slashes.sh b/tests/posix/name_slashes.sh
new file mode 100644
index 0000000..45a39d3
--- /dev/null
+++ b/tests/posix/name_slashes.sh
@@ -0,0 +1 @@
+bfs_diff /// -prune -name /
diff --git a/tests/posix/perm_000.out b/tests/posix/perm_000.out
index b46af62..9df7f46 100644
--- a/tests/posix/perm_000.out
+++ b/tests/posix/perm_000.out
@@ -1 +1 @@
-perms/f---
+perms/f---------
diff --git a/tests/posix/perm_000_minus.out b/tests/posix/perm_000_minus.out
index 42f2fed..e279684 100644
--- a/tests/posix/perm_000_minus.out
+++ b/tests/posix/perm_000_minus.out
@@ -1,10 +1,29 @@
perms
-perms/dr-x
-perms/drwx
-perms/f---
-perms/f-w-
-perms/f-wx
-perms/fr--
-perms/fr-x
-perms/frw-
-perms/frwx
+perms/dr-x------
+perms/dr-xr-xr-x
+perms/drwx------
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f---------
+perms/f--x------
+perms/f--x--x--x
+perms/f-w-------
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx------
+perms/f-wx--x--x
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/fr--------
+perms/fr--r--r--
+perms/fr-x------
+perms/fr-xr-xr-x
+perms/frw-------
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-----
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/posix/perm_222.out b/tests/posix/perm_222.out
index 4876193..bdc5590 100644
--- a/tests/posix/perm_222.out
+++ b/tests/posix/perm_222.out
@@ -1 +1 @@
-perms/f-w-
+perms/f-w--w--w-
diff --git a/tests/posix/perm_222_minus.out b/tests/posix/perm_222_minus.out
index 4876193..342b285 100644
--- a/tests/posix/perm_222_minus.out
+++ b/tests/posix/perm_222_minus.out
@@ -1 +1,5 @@
-perms/f-w-
+perms/drwxrwxrwx
+perms/f-w--w--w-
+perms/f-wx-wx-wx
+perms/frw-rw-rw-
+perms/frwxrwxrwx
diff --git a/tests/posix/perm_644.out b/tests/posix/perm_644.out
index 4598cc1..9f77ce6 100644
--- a/tests/posix/perm_644.out
+++ b/tests/posix/perm_644.out
@@ -1 +1 @@
-perms/frw-
+perms/frw-r--r--
diff --git a/tests/posix/perm_644_minus.out b/tests/posix/perm_644_minus.out
index 9e041c3..84f69f5 100644
--- a/tests/posix/perm_644_minus.out
+++ b/tests/posix/perm_644_minus.out
@@ -1,4 +1,10 @@
perms
-perms/drwx
-perms/frw-
-perms/frwx
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/posix/perm_leading_plus_symbolic_minus.out b/tests/posix/perm_leading_plus_symbolic_minus.out
index e69de29..38d0e1c 100644
--- a/tests/posix/perm_leading_plus_symbolic_minus.out
+++ b/tests/posix/perm_leading_plus_symbolic_minus.out
@@ -0,0 +1,7 @@
+perms
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/posix/perm_leading_plus_umask.out b/tests/posix/perm_leading_plus_umask.out
new file mode 100644
index 0000000..6ed4b7f
--- /dev/null
+++ b/tests/posix/perm_leading_plus_umask.out
@@ -0,0 +1,10 @@
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/f-w--w----
+perms/f-w--w--w-
+perms/f-wx-wx--x
+perms/f-wx-wx-wx
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/posix/perm_leading_plus_umask.sh b/tests/posix/perm_leading_plus_umask.sh
new file mode 100644
index 0000000..948b4ad
--- /dev/null
+++ b/tests/posix/perm_leading_plus_umask.sh
@@ -0,0 +1,3 @@
+# Test for https://www.austingroupbugs.net/view.php?id=1392
+umask 002
+bfs_diff perms -perm -+w
diff --git a/tests/posix/perm_symbolic_minus.out b/tests/posix/perm_symbolic_minus.out
index 9e041c3..84f69f5 100644
--- a/tests/posix/perm_symbolic_minus.out
+++ b/tests/posix/perm_symbolic_minus.out
@@ -1,4 +1,10 @@
perms
-perms/drwx
-perms/frw-
-perms/frwx
+perms/drwxr-xr-x
+perms/drwxrwxr-x
+perms/drwxrwxrwx
+perms/frw-r--r--
+perms/frw-rw-r--
+perms/frw-rw-rw-
+perms/frwxr-xr-x
+perms/frwxrwxr-x
+perms/frwxrwxrwx
diff --git a/tests/posix/permcopy.out b/tests/posix/permcopy.out
index 4598cc1..9f77ce6 100644
--- a/tests/posix/permcopy.out
+++ b/tests/posix/permcopy.out
@@ -1 +1 @@
-perms/frw-
+perms/frw-r--r--
diff --git a/tests/gnu/print0.out b/tests/posix/print0.out
index 1347444..1347444 100644
--- a/tests/gnu/print0.out
+++ b/tests/posix/print0.out
Binary files differ
diff --git a/tests/gnu/print0.sh b/tests/posix/print0.sh
index b916172..b916172 100644
--- a/tests/gnu/print0.sh
+++ b/tests/posix/print0.sh
diff --git a/tests/posix/root_order.out b/tests/posix/root_order.out
new file mode 100644
index 0000000..ea94276
--- /dev/null
+++ b/tests/posix/root_order.out
@@ -0,0 +1,4 @@
+basic/a
+basic/b
+basic/c/d
+basic/e/f
diff --git a/tests/posix/root_order.sh b/tests/posix/root_order.sh
new file mode 100644
index 0000000..86adf20
--- /dev/null
+++ b/tests/posix/root_order.sh
@@ -0,0 +1,6 @@
+# Root paths must be processed in order
+# https://www.austingroupbugs.net/view.php?id=1859
+
+# -size forces a stat(), which we don't want to be async
+invoke_bfs basic/{a,b,c/d,e/f} -size -1000 >"$OUT"
+diff_output
diff --git a/tests/posix/user_invalid_id.sh b/tests/posix/user_invalid_id.sh
new file mode 100644
index 0000000..c378f7e
--- /dev/null
+++ b/tests/posix/user_invalid_id.sh
@@ -0,0 +1 @@
+! invoke_bfs -user 1eW6f5RM9Qi
diff --git a/tests/posix/user_invalid_name.sh b/tests/posix/user_invalid_name.sh
new file mode 100644
index 0000000..bbf3031
--- /dev/null
+++ b/tests/posix/user_invalid_name.sh
@@ -0,0 +1 @@
+! invoke_bfs -user eW6f5RM9Qi
diff --git a/tests/posix/user_o_user.out b/tests/posix/user_o_user.out
new file mode 100644
index 0000000..a7ccfe4
--- /dev/null
+++ b/tests/posix/user_o_user.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/posix/user_o_user.sh b/tests/posix/user_o_user.sh
new file mode 100644
index 0000000..7c143ae
--- /dev/null
+++ b/tests/posix/user_o_user.sh
@@ -0,0 +1,3 @@
+# Regression test for
+# https://github.com/tavianator/bfs/issues/155
+bfs_diff basic -user 0 -o -user "$(id -u)"
diff --git a/tests/ptyx.c b/tests/ptyx.c
new file mode 100644
index 0000000..59292df
--- /dev/null
+++ b/tests/ptyx.c
@@ -0,0 +1,252 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+/**
+ * Execute a command in a pseudo-terminal.
+ *
+ * $ ptyx [-w WIDTH] [-h HEIGHT] [--] COMMAND [ARGS...]
+ */
+
+#include "bfs.h"
+#include "bfstd.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#if __has_include(<stropts.h>)
+# include <stropts.h>
+#endif
+
+#if __sun
+/**
+ * Push a STREAMS module, if it's not already there.
+ *
+ * See https://www.illumos.org/issues/9042.
+ */
+static int i_push(int fd, const char *name) {
+ int ret = ioctl(fd, I_FIND, name);
+ if (ret < 0) {
+ return ret;
+ } else if (ret == 0) {
+ return ioctl(fd, I_PUSH, name);
+ } else {
+ return 0;
+ }
+}
+#endif
+
+int main(int argc, char *argv[]) {
+ const char *cmd = argc > 0 ? argv[0] : "ptyx";
+
+/** Report an error message and exit. */
+#define die(...) die_(__VA_ARGS__, )
+
+#define die_(format, ...) \
+ do { \
+ fprintf(stderr, "%s: " format "%s", cmd, __VA_ARGS__ "\n"); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+/** Report an error code and exit. */
+#define edie(...) edie_(__VA_ARGS__, )
+
+#define edie_(format, ...) \
+ do { \
+ fprintf(stderr, "%s: " format ": %s\n", cmd, __VA_ARGS__ errstr()); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+ unsigned short width = 0;
+ unsigned short height = 0;
+
+ // Parse the command line
+ int c;
+ while (c = getopt(argc, argv, "+:w:h:"), c != -1) {
+ switch (c) {
+ case 'w':
+ if (xstrtous(optarg, NULL, 10, &width) != 0) {
+ edie("Bad width '%s'", optarg);
+ }
+ break;
+ case 'h':
+ if (xstrtous(optarg, NULL, 10, &height) != 0) {
+ edie("Bad height '%s'", optarg);
+ }
+ break;
+ case ':':
+ die("Missing argument to -%c", optopt);
+ case '?':
+ die("Unrecognized option -%c", optopt);
+ }
+ }
+
+ if (optind >= argc) {
+ die("Missing command");
+ }
+ char **args = argv + optind;
+
+ // Create a new pty, and set it up
+ int ptm = posix_openpt(O_RDWR | O_NOCTTY);
+ if (ptm < 0) {
+ edie("posix_openpt()");
+ }
+ if (grantpt(ptm) != 0) {
+ edie("grantpt()");
+ }
+ if (unlockpt(ptm) != 0) {
+ edie("unlockpt()");
+ }
+
+ // Get the subsidiary device path
+ char *name = ptsname(ptm);
+ if (!name) {
+ edie("ptsname()");
+ }
+
+ // Open the subsidiary device
+ int pts = open(name, O_RDWR | O_NOCTTY);
+ if (pts < 0) {
+ edie("%s", name);
+ }
+
+#if __sun
+ // On Solaris/illumos, a pty doesn't behave like a terminal until we
+ // push some STREAMS modules (see ptm(4D), ptem(4M), ldterm(4M)).
+ if (i_push(pts, "ptem") != 0) {
+ die("ioctl(I_PUSH, ptem)");
+ }
+ if (i_push(pts, "ldterm") != 0) {
+ die("ioctl(I_PUSH, ldterm)");
+ }
+#endif
+
+ // A new pty starts at 0x0, which is not very useful. Instead, grab the
+ // default size from the current controlling terminal, if possible.
+ if (!width || !height) {
+ int tty = open_cterm(O_RDONLY | O_CLOEXEC);
+ if (tty >= 0) {
+ struct winsize ws;
+ if (xtcgetwinsize(tty, &ws) != 0) {
+ edie("tcgetwinsize()");
+ }
+ if (!width) {
+ width = ws.ws_col;
+ }
+ if (!height) {
+ height = ws.ws_row;
+ }
+ xclose(tty);
+ }
+ }
+ if (!width) {
+ width = 80;
+ }
+ if (!height) {
+ height = 24;
+ }
+
+ // Update the pty size
+ struct winsize ws;
+ if (xtcgetwinsize(pts, &ws) != 0) {
+ edie("tcgetwinsize()");
+ }
+ ws.ws_col = width;
+ ws.ws_row = height;
+ if (xtcsetwinsize(pts, &ws) != 0) {
+ edie("tcsetwinsize()");
+ }
+
+ // Set custom terminal attributes
+ struct termios attrs;
+ if (tcgetattr(pts, &attrs) != 0) {
+ edie("tcgetattr()");
+ }
+ attrs.c_oflag &= ~OPOST; // Don't convert \n to \r\n
+ if (tcsetattr(pts, TCSANOW, &attrs) != 0) {
+ edie("tcsetattr()");
+ }
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ edie("fork()");
+ } else if (pid == 0) {
+ // Child
+ close(ptm);
+
+ // Make ourselves a session leader so we can have our own
+ // controlling terminal
+ if (setsid() < 0) {
+ edie("setsid()");
+ }
+
+#ifdef TIOCSCTTY
+ // Set the pty as the controlling terminal
+ if (ioctl(pts, TIOCSCTTY, 0) != 0) {
+ edie("ioctl(TIOCSCTTY)");
+ }
+#endif
+
+ // Redirect std{in,out,err} to the pty
+ if (dup2(pts, STDIN_FILENO) < 0
+ || dup2(pts, STDOUT_FILENO) < 0
+ || dup2(pts, STDERR_FILENO) < 0) {
+ edie("dup2()");
+ }
+ if (pts > STDERR_FILENO) {
+ xclose(pts);
+ }
+
+ // Run the requested command
+ execvp(args[0], args);
+ edie("execvp(): %s", args[0]);
+ }
+
+ // Parent
+ xclose(pts);
+
+ // Read output from the pty and copy it to stdout
+ char buf[1024];
+ while (true) {
+ ssize_t len = read(ptm, buf, sizeof(buf));
+ if (len > 0) {
+ if (xwrite(STDOUT_FILENO, buf, len) < 0) {
+ edie("write()");
+ }
+ } else if (len == 0) {
+ break;
+ } else if (errno == EINTR) {
+ continue;
+ } else if (errno == EIO) {
+ // Linux reports EIO rather than EOF when pts is closed
+ break;
+ } else {
+ die("read()");
+ }
+ }
+
+ xclose(ptm);
+
+ int wstatus;
+ if (xwaitpid(pid, &wstatus, 0) < 0) {
+ edie("waitpid()");
+ }
+
+ if (WIFEXITED(wstatus)) {
+ return WEXITSTATUS(wstatus);
+ } else if (WIFSIGNALED(wstatus)) {
+ int sig = WTERMSIG(wstatus);
+ fprintf(stderr, "%s: %s: %s\n", cmd, args[0], strsignal(sig));
+ return 128 + sig;
+ } else {
+ return 128;
+ }
+}
diff --git a/tests/run.sh b/tests/run.sh
index 115a036..3ed2a9c 100644
--- a/tests/run.sh
+++ b/tests/run.sh
@@ -94,19 +94,17 @@ reap_test() {
# Wait for a background test to finish
wait_test() {
- local pid
+ local pid line ret
- while true; do
- wait -n -ppid
+ while :; do
+ line=$((LINENO + 1))
+ _wait -n -ppid
ret=$?
if [ "${pid:-}" ]; then
break
- elif ((ret > 128)); then
- # Interrupted by signal
- continue
else
- debug "${BASH_SOURCE[0]}" $((LINENO - 3)) "${RED}error $ret${RST}" >&$DUPERR
+ debug "${BASH_SOURCE[0]}" $line "${RED}error $ret${RST}" >&$DUPERR
exit 1
fi
done
@@ -120,7 +118,9 @@ wait_ready() {
# 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
+
+ local job status ret rest
+ while read -r job status ret rest; do
case "$status" in
Done)
reap_test 0
@@ -156,7 +156,7 @@ comake() {
exec {READY_PIPE}<&${COPROC[0]} {DONE_PIPE}>&${COPROC[1]}
}
-# Print the current test progess
+# Print the current test progress
progress() {
if [ "${BAR:-}" ]; then
print_bar "$(printf "$@")"
@@ -359,20 +359,12 @@ invoke_bfs() {
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=$?
+ "$PTYX" -w80 -h24 -- "${BFS[@]}" "$@" || ret=$?
if ((ret > 125)); then
exit $ret
@@ -412,8 +404,13 @@ make_xattrs() {
# Get the Unix epoch time in seconds
epoch_time() {
- # https://stackoverflow.com/a/12746260/502399
- awk 'BEGIN { srand(); print srand(); }'
+ if [ "${EPOCHSECONDS:-}" ]; then
+ # Added in bash 5
+ printf '%d' "$EPOCHSECONDS"
+ else
+ # https://stackoverflow.com/a/12746260/502399
+ awk 'BEGIN { srand(); print srand(); }'
+ fi
}
## Snapshot testing
diff --git a/tests/sighook.c b/tests/sighook.c
index c94526e..82e0ae5 100644
--- a/tests/sighook.c
+++ b/tests/sighook.c
@@ -1,15 +1,23 @@
// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD
-#include "prelude.h"
#include "tests.h"
-#include "sighook.h"
+
#include "atomic.h"
+#include "bfstd.h"
+#include "sighook.h"
#include "thread.h"
+#include "xtime.h"
+
+#include <errno.h>
#include <pthread.h>
#include <signal.h>
-#include <sys/time.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+/** Counts SIGALRM deliveries. */
static atomic size_t count = 0;
/** SIGALRM handler. */
@@ -17,81 +25,204 @@ static void alrm_hook(int sig, siginfo_t *info, void *arg) {
fetch_add(&count, 1, relaxed);
}
-/** Swap out an old hook for a new hook. */
-static int swap_hooks(struct sighook **hook) {
- struct sighook *next = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
- if (!bfs_echeck(next, "sighook(SIGALRM)")) {
- return -1;
- }
+/** SH_ONESHOT counter. */
+static atomic size_t shots = 0;
- sigunhook(*hook);
- *hook = next;
- return 0;
+/** SH_ONESHOT hook. */
+static void alrm_oneshot(int sig, siginfo_t *info, void *arg) {
+ fetch_add(&shots, 1, relaxed);
}
-/** Background thread that rapidly (un)registers signal hooks. */
+/** Keeps the background thread alive. */
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+static bool done = false;
+
+/** Background thread that receives signals. */
static void *hook_thread(void *ptr) {
- struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
- if (!bfs_echeck(hook, "sighook(SIGALRM)")) {
- return NULL;
+ mutex_lock(&mutex);
+ while (!done) {
+ cond_wait(&cond, &mutex);
}
+ mutex_unlock(&mutex);
+ return NULL;
+}
- while (load(&count, relaxed) < 1000) {
- if (swap_hooks(&hook) != 0) {
- sigunhook(hook);
- return NULL;
- }
+/** Block a signal in this thread. */
+static int block_signal(int sig, sigset_t *old) {
+ sigset_t set;
+ if (sigemptyset(&set) != 0) {
+ return -1;
+ }
+ if (sigaddset(&set, sig) != 0) {
+ return -1;
}
- sigunhook(hook);
- return &count;
+ errno = pthread_sigmask(SIG_BLOCK, &set, old);
+ if (errno != 0) {
+ return -1;
+ }
+
+ return 0;
}
-bool check_sighook(void) {
- bool ret = true;
+/** Tests for sighook(). */
+static void check_hooks(void) {
+ struct sighook *hook = NULL;
+ struct sighook *oneshot = NULL;
- struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
- ret &= bfs_echeck(hook, "sighook(SIGALRM)");
- if (!ret) {
- goto done;
+ hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
+ if (!bfs_echeck(hook, "sighook(SIGALRM)")) {
+ return;
}
- struct itimerval ival = {
- .it_value = {
- .tv_usec = 100,
- },
- .it_interval = {
- .tv_usec = 100,
- },
- };
- ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0);
- if (!ret) {
+ // Create a background thread to receive SIGALRM
+ pthread_t thread;
+ if (!bfs_echeck(thread_create(&thread, NULL, hook_thread, NULL) == 0)) {
goto unhook;
}
- pthread_t thread;
- ret &= bfs_echeck(thread_create(&thread, NULL, hook_thread, NULL) == 0);
- if (!ret) {
- goto untime;
+ // Block SIGALRM in this thread so the handler runs concurrently with
+ // sighook()/sigunhook()
+ sigset_t mask;
+ if (!bfs_echeck(block_signal(SIGALRM, &mask) == 0)) {
+ goto unthread;
}
- while (ret && load(&count, relaxed) < 1000) {
- ret &= swap_hooks(&hook) == 0;
+ // Check that we can unregister and re-register a hook
+ sigunhook(hook);
+ hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
+ if (!bfs_echeck(hook, "sighook(SIGALRM)")) {
+ goto unblock;
}
- void *ptr;
- thread_join(thread, &ptr);
- ret &= bfs_check(ptr);
+ // Test SH_ONESHOT
+ oneshot = sighook(SIGALRM, alrm_oneshot, NULL, SH_ONESHOT);
+ if (!bfs_echeck(oneshot, "sighook(SH_ONESHOT)")) {
+ goto unblock;
+ }
-untime:
- ival.it_value.tv_usec = 0;
- ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0);
- if (!ret) {
- goto unhook;
+ // Create a timer that sends SIGALRM every 100 microseconds
+ const struct timespec ival = { .tv_nsec = 100 * 1000 };
+ struct timer *timer = xtimer_start(&ival);
+ if (!bfs_echeck(timer, "xtimer_start()")) {
+ goto unblock;
+ }
+
+ // Rapidly register/unregister SIGALRM hooks
+ size_t alarms;
+ while (alarms = load(&count, relaxed), alarms < 1000) {
+ size_t nshots = load(&shots, relaxed);
+ bfs_check(nshots <= 1);
+ if (alarms > 1) {
+ bfs_check(nshots == 1);
+ }
+ if (alarms >= 500) {
+ sigunhook(oneshot);
+ oneshot = NULL;
+ }
+
+ struct sighook *next = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE);
+ if (!bfs_echeck(next, "sighook(SIGALRM)")) {
+ break;
+ }
+
+ sigunhook(hook);
+ hook = next;
}
+ // Stop the timer
+ xtimer_stop(timer);
+unblock:
+ // Restore the old signal mask
+ errno = pthread_sigmask(SIG_SETMASK, &mask, NULL);
+ bfs_echeck(errno == 0, "pthread_sigmask()");
+unthread:
+ // Quit the background thread
+ mutex_lock(&mutex);
+ done = true;
+ mutex_unlock(&mutex);
+ cond_signal(&cond);
+ thread_join(thread, NULL);
unhook:
+ // Unregister the SIGALRM hooks
+ sigunhook(oneshot);
sigunhook(hook);
-done:
- return ret;
+}
+
+/** atsigexit() hook. */
+static void exit_hook(int sig, siginfo_t *info, void *arg) {
+ // Write the signal that's killing us to the pipe
+ int *pipes = arg;
+ if (xwrite(pipes[1], &sig, sizeof(sig)) != sizeof(sig)) {
+ abort();
+ }
+}
+
+/** Tests for atsigexit(). */
+static void check_sigexit(int sig) {
+ // To wait for the child to call atsigexit()
+ int ready[2];
+ bfs_everify(pipe(ready) == 0);
+
+ // Written in the atsigexit() handler
+ int killed[2];
+ bfs_everify(pipe(killed) == 0);
+
+ pid_t pid;
+ bfs_everify((pid = fork()) >= 0);
+
+ if (pid > 0) {
+ // Parent
+ xclose(ready[1]);
+ xclose(killed[1]);
+
+ // Wait for the child to call atsigexit()
+ char c;
+ bfs_everify(xread(ready[0], &c, 1) == 1);
+
+ // Kill the child with the signal
+ bfs_everify(kill(pid, sig) == 0);
+
+ // Check that the child died to the right signal
+ int wstatus;
+ if (bfs_echeck(xwaitpid(pid, &wstatus, 0) == pid)) {
+ bfs_check(WIFSIGNALED(wstatus) && WTERMSIG(wstatus) == sig);
+ }
+
+ // Check that the signal hook wrote the signal number to the pipe
+ int hsig;
+ if (bfs_echeck(xread(killed[0], &hsig, sizeof(hsig)) == sizeof(hsig))) {
+ bfs_check(hsig == sig);
+ }
+ } else {
+ // Child
+ xclose(ready[0]);
+ xclose(killed[0]);
+
+ // exit_hook() will write to killed[1]
+ bfs_everify(atsigexit(exit_hook, killed) != NULL);
+
+ // Tell the parent we're ready
+ bfs_everify(xwrite(ready[1], "A", 1) == 1);
+
+ // Wait until we're killed
+ const struct timespec dur = { .tv_nsec = 1 };
+ while (true) {
+ nanosleep(&dur, NULL);
+ }
+ }
+}
+
+void check_sighook(void) {
+ check_hooks();
+
+ check_sigexit(SIGINT);
+ check_sigexit(SIGQUIT);
+ check_sigexit(SIGPIPE);
+
+ // macOS cannot distinguish between sync and async SIG{BUS,ILL,SEGV}
+#if !__APPLE__
+ check_sigexit(SIGSEGV);
+#endif
}
diff --git a/tests/stddirs.sh b/tests/stddirs.sh
index b3cd521..1569fee 100644
--- a/tests/stddirs.sh
+++ b/tests/stddirs.sh
@@ -14,13 +14,31 @@ make_basic() {
# Creates a file+directory structure with various permissions for tests
make_perms() {
- "$XTOUCH" -p -M000 "$1/f---"
- "$XTOUCH" -p -M444 "$1/fr--"
- "$XTOUCH" -p -M222 "$1/f-w-"
- "$XTOUCH" -p -M644 "$1/frw-"
- "$XTOUCH" -p -M311 "$1/f-wx"
- "$XTOUCH" -p -M555 "$1/fr-x" "$1/dr-x/"
- "$XTOUCH" -p -M755 "$1/frwx" "$1/drwx/"
+ "$XTOUCH" -p -M000 "$1/f---------"
+ "$XTOUCH" -p -M111 "$1/f--x--x--x"
+ "$XTOUCH" -p -M222 "$1/f-w--w--w-"
+ "$XTOUCH" -p -M333 "$1/f-wx-wx-wx"
+ "$XTOUCH" -p -M444 "$1/fr--r--r--"
+ "$XTOUCH" -p -M555 "$1/fr-xr-xr-x" "$1/dr-xr-xr-x/"
+ "$XTOUCH" -p -M666 "$1/frw-rw-rw-"
+ "$XTOUCH" -p -M777 "$1/frwxrwxrwx" "$1/drwxrwxrwx/"
+
+ "$XTOUCH" -p -M220 "$1/f-w--w----"
+ "$XTOUCH" -p -M331 "$1/f-wx-wx--x"
+ "$XTOUCH" -p -M664 "$1/frw-rw-r--"
+ "$XTOUCH" -p -M775 "$1/frwxrwxr-x" "$1/drwxrwxr-x/"
+
+ "$XTOUCH" -p -M311 "$1/f-wx--x--x"
+ "$XTOUCH" -p -M644 "$1/frw-r--r--"
+ "$XTOUCH" -p -M755 "$1/frwxr-xr-x" "$1/drwxr-xr-x/"
+
+ "$XTOUCH" -p -M100 "$1/f--x------"
+ "$XTOUCH" -p -M200 "$1/f-w-------"
+ "$XTOUCH" -p -M300 "$1/f-wx------"
+ "$XTOUCH" -p -M400 "$1/fr--------"
+ "$XTOUCH" -p -M500 "$1/fr-x------" "$1/dr-x------/"
+ "$XTOUCH" -p -M600 "$1/frw-------"
+ "$XTOUCH" -p -M700 "$1/frwxr-----" "$1/drwx------/"
}
# Creates a file+directory structure with various symbolic and hard links
@@ -133,7 +151,7 @@ make_stddirs() {
if ((CLEAN)); then
defer clean_stddirs
else
- printf "Test files saved to ${BLD}%s${RST}\n" "$TMP"
+ color printf "Test files saved to ${BLD}%s${RST}\n" "$TMP"
fi
chown "$(id -u):$(id -g)" "$TMP"
diff --git a/tests/tests.h b/tests/tests.h
index 2958fe1..d395c7c 100644
--- a/tests/tests.h
+++ b/tests/tests.h
@@ -8,70 +8,67 @@
#ifndef BFS_TESTS_H
#define BFS_TESTS_H
-#include "prelude.h"
#include "bfstd.h"
#include "diag.h"
-/** Unit test function type. */
-typedef bool test_fn(void);
-
/** Memory allocation tests. */
-bool check_alloc(void);
+void check_alloc(void);
/** Standard library wrapper tests. */
-bool check_bfstd(void);
+void check_bfstd(void);
/** Bit manipulation tests. */
-bool check_bit(void);
+void check_bit(void);
/** I/O queue tests. */
-bool check_ioq(void);
+void check_ioq(void);
/** Linked list tests. */
-bool check_list(void);
+void check_list(void);
/** Signal hook tests. */
-bool check_sighook(void);
+void check_sighook(void);
/** Trie tests. */
-bool check_trie(void);
+void check_trie(void);
/** Process spawning tests. */
-bool check_xspawn(void);
+void check_xspawn(void);
/** Time tests. */
-bool check_xtime(void);
+void check_xtime(void);
-/** Don't ignore the bfs_check() return value. */
-attr(nodiscard)
-static inline bool bfs_check(bool ret) {
- return ret;
-}
+/** Record a single check and return the result. */
+bool bfs_check_impl(bool result);
/**
* Check a condition, logging a message on failure but continuing.
*/
#define bfs_check(...) \
- bfs_check(bfs_check_(#__VA_ARGS__, __VA_ARGS__, "", ""))
+ 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))
+ bfs_check_impl((cond) || (bfs_check__(format, BFS_DIAG_MSG_(format, str), __VA_ARGS__), false))
+
+#define bfs_check__(format, ...) \
+ bfs_diagf(sizeof(format) > 1 \
+ ? BFS_DIAG_FORMAT_("%s" format "%s") \
+ : BFS_DIAG_FORMAT_("Check failed: `%s`"), \
+ BFS_DIAG_ARGS_(__VA_ARGS__))
/**
* Check a condition, logging the current error string on failure.
*/
#define bfs_echeck(...) \
- bfs_echeck_(#__VA_ARGS__, __VA_ARGS__, "", errstr())
+ bfs_echeck_(#__VA_ARGS__, __VA_ARGS__, "", )
#define bfs_echeck_(str, cond, format, ...) \
- ((cond) ? true : (bfs_diag( \
- sizeof(format) > 1 \
- ? "%.0s" format "%s: %s" \
- : "Check failed: `%s`: %s", \
- str, __VA_ARGS__), false))
+ bfs_check_impl((cond) || (bfs_echeck__(format, BFS_DIAG_MSG_(format, str), __VA_ARGS__), false))
+
+#define bfs_echeck__(format, ...) \
+ bfs_diagf(sizeof(format) > 1 \
+ ? BFS_DIAG_FORMAT_("%s" format "%s: %s") \
+ : BFS_DIAG_FORMAT_("Check failed: `%s`: %s"), \
+ BFS_DIAG_ARGS_(__VA_ARGS__ errstr(), ))
#endif // BFS_TESTS_H
diff --git a/tests/trie.c b/tests/trie.c
index ebaae5d..59bde40 100644
--- a/tests/trie.c
+++ b/tests/trie.c
@@ -1,10 +1,12 @@
// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD
-#include "prelude.h"
#include "tests.h"
-#include "trie.h"
+
+#include "bfs.h"
#include "diag.h"
+#include "trie.h"
+
#include <stdlib.h>
#include <string.h>
@@ -18,9 +20,11 @@ static const char *keys[] = {
"quuuux",
"pre",
- "pref",
"prefi",
+ "pref",
"prefix",
+ "p",
+ "pRefix",
"AAAA",
"AADD",
@@ -35,18 +39,20 @@ static const char *keys[] = {
">>>>>>",
">>><<<",
">>>",
+
+ "AAAAAAA",
+ "AAAAAAAB",
+ "AAAAAAAa",
};
static const size_t nkeys = countof(keys);
-bool check_trie(void) {
- bool ret = true;
-
+void check_trie(void) {
struct trie trie;
trie_init(&trie);
for (size_t i = 0; i < nkeys; ++i) {
- ret &= bfs_check(!trie_find_str(&trie, keys[i]));
+ bfs_check(!trie_find_str(&trie, keys[i]));
const char *prefix = NULL;
for (size_t j = 0; j < i; ++j) {
@@ -60,37 +66,38 @@ bool check_trie(void) {
struct trie_leaf *leaf = trie_find_prefix(&trie, keys[i]);
if (prefix) {
bfs_verify(leaf);
- ret &= bfs_check(strcmp(prefix, leaf->key) == 0);
+ bfs_check(strcmp(prefix, leaf->key) == 0);
} else {
- ret &= bfs_check(!leaf);
+ bfs_check(!leaf);
}
leaf = trie_insert_str(&trie, keys[i]);
bfs_verify(leaf);
- ret &= bfs_check(strcmp(keys[i], leaf->key) == 0);
- ret &= bfs_check(leaf->length == strlen(keys[i]) + 1);
+ bfs_check(strcmp(keys[i], leaf->key) == 0);
+ bfs_check(leaf->length == strlen(keys[i]) + 1);
}
{
size_t i = 0;
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);
+ bfs_check(leaf == trie_find_str(&trie, keys[i]));
+ bfs_check(leaf == trie_insert_str(&trie, keys[i]));
+ bfs_check(!leaf->prev || leaf->prev->next == leaf);
+ bfs_check(!leaf->next || leaf->next->prev == leaf);
++i;
}
- ret &= bfs_check(i == nkeys);
+ bfs_check(i == nkeys);
}
for (size_t i = 0; i < nkeys; ++i) {
struct trie_leaf *leaf = trie_find_str(&trie, keys[i]);
bfs_verify(leaf);
- ret &= bfs_check(strcmp(keys[i], leaf->key) == 0);
- ret &= bfs_check(leaf->length == strlen(keys[i]) + 1);
+ bfs_check(strcmp(keys[i], leaf->key) == 0);
+ bfs_check(leaf->length == strlen(keys[i]) + 1);
trie_remove(&trie, leaf);
leaf = trie_find_str(&trie, keys[i]);
- ret &= bfs_check(!leaf);
+ bfs_check(!leaf);
const char *postfix = NULL;
for (size_t j = i + 1; j < nkeys; ++j) {
@@ -104,14 +111,14 @@ bool check_trie(void) {
leaf = trie_find_postfix(&trie, keys[i]);
if (postfix) {
bfs_verify(leaf);
- ret &= bfs_check(strcmp(postfix, leaf->key) == 0);
+ bfs_check(strcmp(postfix, leaf->key) == 0);
} else {
- ret &= bfs_check(!leaf);
+ bfs_check(!leaf);
}
}
for_trie (leaf, &trie) {
- ret &= bfs_check(false, "trie should be empty");
+ bfs_check(false, "trie should be empty");
}
// This tests the "jump" node handling on 32-bit platforms
@@ -120,18 +127,17 @@ bool check_trie(void) {
bfs_verify(longstr);
memset(longstr, 0xAC, longsize);
- ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize));
- ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize));
+ bfs_check(!trie_find_mem(&trie, longstr, longsize));
+ bfs_check(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));
+ bfs_check(!trie_find_mem(&trie, longstr, longsize));
+ bfs_check(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));
+ bfs_check(!trie_find_mem(&trie, longstr, longsize));
+ bfs_check(trie_insert_mem(&trie, longstr, longsize));
free(longstr);
trie_destroy(&trie);
- return ret;
}
diff --git a/tests/util.sh b/tests/util.sh
index 76b72b9..1718a1a 100644
--- a/tests/util.sh
+++ b/tests/util.sh
@@ -16,6 +16,7 @@ ROOT=$(_realpath "$(dirname -- "$TESTS")")
TESTS="$ROOT/tests"
BIN="$ROOT/bin"
MKSOCK="$BIN/tests/mksock"
+PTYX="$BIN/tests/ptyx"
XTOUCH="$BIN/tests/xtouch"
UNAME=$(uname)
@@ -70,7 +71,7 @@ stdenv() {
fi
}
-# Drop root priviliges or bail
+# Drop root privileges 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
@@ -189,3 +190,28 @@ pop_defers() {
return $ret
}
+
+## Parallelism
+
+# Get the number of processors
+_nproc() {
+ {
+ nproc \
+ || sysctl -n hw.ncpu \
+ || getconf _NPROCESSORS_ONLN \
+ || echo 1
+ } 2>/dev/null
+}
+
+# Run wait, looping if interrupted
+_wait() {
+ local ret=130
+
+ # "If wait is interrupted by a signal, the return status will be greater than 128"
+ while ((ret > 128)); do
+ ret=0
+ wait "$@" || ret=$?
+ done
+
+ return $ret
+}
diff --git a/tests/xspawn.c b/tests/xspawn.c
index f48e220..6864192 100644
--- a/tests/xspawn.c
+++ b/tests/xspawn.c
@@ -1,12 +1,13 @@
// 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>
@@ -49,102 +50,123 @@ fail:
return NULL;
}
-/** Check that we resolve executables in $PATH correctly. */
-static bool check_use_path(bool use_posix) {
- bool ret = true;
+/** Add an entry to $PATH. */
+static int add_path(const char *entry, char **old_path) {
+ int ret = -1;
+ const char *new_path = NULL;
- struct bfs_spawn spawn;
- ret &= bfs_echeck(bfs_spawn_init(&spawn) == 0);
- if (!ret) {
- goto out;
- }
+ *old_path = getenv("PATH");
+ if (*old_path) {
+ *old_path = strdup(*old_path);
+ if (!*old_path) {
+ goto done;
+ }
- spawn.flags |= BFS_SPAWN_USE_PATH;
- if (!use_posix) {
- spawn.flags &= ~BFS_SPAWN_USE_POSIX;
+ new_path = dstrprintf("%s:%s", entry, *old_path);
+ if (!new_path) {
+ goto done;
+ }
+ } else {
+ new_path = entry;
}
- ret &= bfs_echeck(bfs_spawn_addopen(&spawn, 10, "bin", O_RDONLY | O_DIRECTORY, 0) == 0);
- ret &= bfs_echeck(bfs_spawn_adddup2(&spawn, 10, 11) == 0);
- ret &= bfs_echeck(bfs_spawn_addclose(&spawn, 10) == 0);
- ret &= bfs_echeck(bfs_spawn_addfchdir(&spawn, 11) == 0);
- ret &= bfs_echeck(bfs_spawn_addclose(&spawn, 11) == 0);
- if (!ret) {
- goto destroy;
+ ret = setenv("PATH", new_path, true);
+
+done:
+ if (new_path && new_path != entry) {
+ dstrfree((dchar *)new_path);
}
- // Check that $PATH is resolved in the parent's environment
- char **envp;
- ret &= bfs_echeck(envp = envdup());
- if (!ret) {
- goto destroy;
+ if (ret != 0) {
+ free(*old_path);
+ *old_path = NULL;
}
- // Check that $PATH is resolved after the file actions
- char *old_path = getenv("PATH");
- dchar *new_path = NULL;
+ return ret;
+}
+
+/** Undo add_path(). */
+static int reset_path(char *old_path) {
+ int ret;
+
if (old_path) {
- ret &= bfs_echeck(old_path = strdup(old_path));
- if (!ret) {
- goto env;
- }
- new_path = dstrprintf("tests:%s", old_path);
+ ret = setenv("PATH", old_path, true);
+ free(old_path);
} else {
- new_path = dstrdup("tests");
- }
- ret &= bfs_check(new_path);
- if (!ret) {
- goto path;
+ ret = unsetenv("PATH");
}
- ret &= bfs_echeck(setenv("PATH", new_path, true) == 0);
- if (!ret) {
- goto path;
- }
+ return ret;
+}
- char *argv[] = {"xspawnee", old_path, NULL};
- pid_t pid = bfs_spawn("xspawnee", &spawn, argv, envp);
- ret &= bfs_echeck(pid >= 0, "bfs_spawn()");
- if (!ret) {
- goto unset;
+/** Spawn the test binary and check for success. */
+static void check_spawnee(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp) {
+ pid_t pid = bfs_spawn(exe, ctx, argv, envp);
+ if (!bfs_echeck(pid >= 0, "bfs_spawn('%s')", exe)) {
+ return;
}
int wstatus;
- ret &= bfs_echeck(xwaitpid(pid, &wstatus, 0) == pid)
+ bool exited = bfs_echeck(xwaitpid(pid, &wstatus, 0) == pid)
&& bfs_check(WIFEXITED(wstatus));
- if (ret) {
+ if (exited) {
int wexit = WEXITSTATUS(wstatus);
- ret &= bfs_check(wexit == EXIT_SUCCESS, "xspawnee: exit(%d)", wexit);
+ bfs_check(wexit == EXIT_SUCCESS, "xspawnee: exit(%d)", wexit);
}
+}
-unset:
- if (old_path) {
- ret &= bfs_echeck(setenv("PATH", old_path, true) == 0);
- } else {
- ret &= bfs_echeck(unsetenv("PATH") == 0);
+/** Check that we resolve executables in $PATH correctly. */
+static void check_use_path(bool use_posix) {
+ struct bfs_spawn spawn;
+ if (!bfs_echeck(bfs_spawn_init(&spawn) == 0)) {
+ return;
+ }
+
+ spawn.flags |= BFS_SPAWN_USE_PATH;
+ if (!use_posix) {
+ spawn.flags &= ~BFS_SPAWN_USE_POSIX;
+ }
+
+ bool init = bfs_echeck(bfs_spawn_addopen(&spawn, 10, "bin", O_RDONLY | O_DIRECTORY, 0) == 0)
+ && bfs_echeck(bfs_spawn_adddup2(&spawn, 10, 11) == 0)
+ && bfs_echeck(bfs_spawn_addclose(&spawn, 10) == 0)
+ && bfs_echeck(bfs_spawn_addfchdir(&spawn, 11) == 0)
+ && bfs_echeck(bfs_spawn_addclose(&spawn, 11) == 0);
+ if (!init) {
+ goto destroy;
+ }
+
+ // Check that $PATH is resolved in the parent's environment
+ char **envp = envdup();
+ if (!bfs_echeck(envp, "envdup()")) {
+ goto destroy;
}
-path:
- dstrfree(new_path);
- free(old_path);
+
+ // Check that $PATH is resolved after the file actions
+ char *old_path;
+ if (!bfs_echeck(add_path("tests", &old_path) == 0)) {
+ goto env;
+ }
+
+ char *argv[] = {"xspawnee", old_path, NULL};
+ check_spawnee("xspawnee", &spawn, argv, envp);
+ check_spawnee("tests/xspawnee", &spawn, argv, envp);
+
+ bfs_echeck(reset_path(old_path) == 0);
env:
for (char **var = envp; *var; ++var) {
free(*var);
}
free(envp);
destroy:
- ret &= bfs_echeck(bfs_spawn_destroy(&spawn) == 0);
-out:
- return ret;
+ bfs_echeck(bfs_spawn_destroy(&spawn) == 0);
}
/** Check path resolution of non-existent executables. */
-static bool check_enoent(bool use_posix) {
- bool ret = true;
-
+static void check_enoent(bool use_posix) {
struct bfs_spawn spawn;
- ret &= bfs_echeck(bfs_spawn_init(&spawn) == 0);
- if (!ret) {
- goto out;
+ if (!bfs_echeck(bfs_spawn_init(&spawn) == 0)) {
+ return;
}
spawn.flags |= BFS_SPAWN_USE_PATH;
@@ -154,46 +176,45 @@ static bool check_enoent(bool use_posix) {
char *argv[] = {"eW6f5RM9Qi", NULL};
pid_t pid = bfs_spawn("eW6f5RM9Qi", &spawn, argv, NULL);
- ret &= bfs_echeck(pid < 0 && errno == ENOENT, "bfs_spawn()");
+ bfs_echeck(pid < 0 && errno == ENOENT, "bfs_spawn()");
- ret &= bfs_echeck(bfs_spawn_destroy(&spawn) == 0);
-out:
- return ret;
+ bfs_echeck(bfs_spawn_destroy(&spawn) == 0);
}
-static bool check_resolve(void) {
- bool ret = true;
+static void check_resolve(void) {
char *exe;
exe = bfs_spawn_resolve("sh");
- ret &= bfs_echeck(exe, "bfs_spawn_resolve('sh')");
+ bfs_echeck(exe, "bfs_spawn_resolve('sh')");
free(exe);
exe = bfs_spawn_resolve("/bin/sh");
- ret &= bfs_echeck(exe && strcmp(exe, "/bin/sh") == 0);
+ bfs_echeck(exe && strcmp(exe, "/bin/sh") == 0);
free(exe);
exe = bfs_spawn_resolve("bin/tests/xspawnee");
- ret &= bfs_echeck(exe && strcmp(exe, "bin/tests/xspawnee") == 0);
+ bfs_echeck(exe && strcmp(exe, "bin/tests/xspawnee") == 0);
free(exe);
- ret &= bfs_echeck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT);
+ bfs_echeck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT);
- ret &= bfs_echeck(!bfs_spawn_resolve("bin/eW6f5RM9Qi") && errno == ENOENT);
+ bfs_echeck(!bfs_spawn_resolve("bin/eW6f5RM9Qi") && errno == ENOENT);
- return ret;
+ char *old_path;
+ if (bfs_echeck(add_path("bin/tests", &old_path) == 0)) {
+ exe = bfs_spawn_resolve("xspawnee");
+ bfs_echeck(exe && strcmp(exe, "bin/tests/xspawnee") == 0);
+ free(exe);
+ bfs_echeck(reset_path(old_path) == 0);
+ }
}
-bool check_xspawn(void) {
- bool ret = true;
+void check_xspawn(void) {
+ check_use_path(true);
+ check_use_path(false);
- ret &= check_use_path(true);
- ret &= check_use_path(false);
+ check_enoent(true);
+ check_enoent(false);
- ret &= check_enoent(true);
- ret &= check_enoent(false);
-
- ret &= check_resolve();
-
- return ret;
+ check_resolve();
}
diff --git a/tests/xtime.c b/tests/xtime.c
index 1907e26..c890a1e 100644
--- a/tests/xtime.c
+++ b/tests/xtime.c
@@ -1,11 +1,12 @@
// Copyright © Tavian Barnes <tavianator@tavianator.com>
// SPDX-License-Identifier: 0BSD
-#include "prelude.h"
#include "tests.h"
-#include "xtime.h"
-#include "bfstd.h"
+
+#include "bfs.h"
#include "diag.h"
+#include "xtime.h"
+
#include <errno.h>
#include <limits.h>
#include <stdint.h>
@@ -39,35 +40,31 @@ static bool check_one_xgetdate(const char *str, int error, time_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;
+static void check_xgetdate(void) {
+ check_one_xgetdate("", EINVAL, 0);
+ check_one_xgetdate("????", EINVAL, 0);
+ check_one_xgetdate("1991", EINVAL, 0);
+ check_one_xgetdate("1991-??", EINVAL, 0);
+ check_one_xgetdate("1991-12", EINVAL, 0);
+ check_one_xgetdate("1991-12-", EINVAL, 0);
+ check_one_xgetdate("1991-12-??", EINVAL, 0);
+ check_one_xgetdate("1991-12-14", 0, 692668800);
+ check_one_xgetdate("1991-12-14-", EINVAL, 0);
+ check_one_xgetdate("1991-12-14T", EINVAL, 0);
+ check_one_xgetdate("1991-12-14T??", EINVAL, 0);
+ check_one_xgetdate("1991-12-14T10", 0, 692704800);
+ check_one_xgetdate("1991-12-14T10:??", EINVAL, 0);
+ check_one_xgetdate("1991-12-14T10:11", 0, 692705460);
+ check_one_xgetdate("1991-12-14T10:11:??", EINVAL, 0);
+ check_one_xgetdate("1991-12-14T10:11:12", 0, 692705472);
+ check_one_xgetdate("1991-12-14T10Z", 0, 692704800);
+ check_one_xgetdate("1991-12-14T10:11Z", 0, 692705460);
+ check_one_xgetdate("1991-12-14T10:11:12Z", 0, 692705472);
+ check_one_xgetdate("1991-12-14T10:11:12?", EINVAL, 0);
+ check_one_xgetdate("1991-12-14T03-07", 0, 692704800);
+ check_one_xgetdate("1991-12-14T06:41-03:30", 0, 692705460);
+ check_one_xgetdate("1991-12-14T03:11:12-07:00", 0, 692705472);
+ check_one_xgetdate("19911214 031112-0700", 0, 692705472);;
}
#define TM_FORMAT "%04d-%02d-%02d %02d:%02d:%02d (%d/7, %d/365%s)"
@@ -92,11 +89,9 @@ static bool check_one_xmktime(time_t expected) {
}
/** xmktime() tests. */
-static bool check_xmktime(void) {
- bool ret = true;
-
+static void check_xmktime(void) {
for (time_t time = -10; time <= 10; ++time) {
- ret &= check_one_xmktime(time);
+ check_one_xmktime(time);
}
// Attempt to trigger overflow (but don't test for it, since it's not mandatory)
@@ -111,12 +106,10 @@ static bool check_xmktime(void) {
};
time_t time;
xmktime(&tm, &time);
-
- return ret;
}
/** Check one xtimegm() result. */
-static bool check_one_xtimegm(const struct tm *tm) {
+static void check_one_xtimegm(const struct tm *tm) {
struct tm tma = *tm, tmb = *tm;
time_t ta, tb;
ta = mktime(&tma);
@@ -124,47 +117,51 @@ static bool check_one_xtimegm(const struct tm *tm) {
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));
+ bool pass = true;
+ pass &= bfs_check(ta == tb, "%jd != %jd", (intmax_t)ta, (intmax_t)tb);
+ if (ta != -1) {
+ pass &= bfs_check(tm_equal(&tma, &tmb));
+ }
- if (!ret) {
+ if (!pass) {
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) {
+static void check_xtimegm_overflow(const struct tm *tm) {
struct tm copy = *tm;
time_t time = 123;
- bool ret = true;
- ret &= bfs_check(xtimegm(&copy, &time) == -1 && errno == EOVERFLOW);
- ret &= bfs_check(tm_equal(&copy, tm));
- ret &= bfs_check(time == 123);
+ bool pass = true;
+ pass &= bfs_check(xtimegm(&copy, &time) == -1 && errno == EOVERFLOW);
+ pass &= bfs_check(tm_equal(&copy, tm));
+ pass &= bfs_check(time == 123);
- if (!ret) {
+ if (!pass) {
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;
-
+static void check_xtimegm(void) {
struct tm tm = {
.tm_isdst = -1,
};
+#if BFS_HAS_TIMEGM
+ // Check that xtimegm(-1) isn't an error
+ for (time_t time = -10; time <= 10; ++time) {
+ if (bfs_check(gmtime_r(&time, &tm), "gmtime_r(%jd)", (intmax_t)time)) {
+ check_one_xtimegm(&tm);
+ }
+ }
+#else
// 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)
@@ -172,24 +169,19 @@ static bool check_xtimegm(void) {
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);
+ 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;
+ check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX });
+ check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX });
+ check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX });
+ check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX });
+#endif // !BFS_HAS_TIMEGM
}
-bool check_xtime(void) {
- bool ret = true;
- ret &= check_xgetdate();
- ret &= check_xmktime();
- ret &= check_xtimegm();
- return ret;
+void check_xtime(void) {
+ check_xgetdate();
+ check_xmktime();
+ check_xtimegm();
}
diff --git a/tests/xtouch.c b/tests/xtouch.c
index 427e3e0..f33c573 100644
--- a/tests/xtouch.c
+++ b/tests/xtouch.c
@@ -1,10 +1,10 @@
// 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 <stdio.h>
@@ -217,11 +217,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) {
+ unsigned int mode;
+ if (xstrtoui(marg, NULL, 8, &mode) == 0 && mode < 01000) {
args.fmode = args.dmode = mode;
} else {
fprintf(stderr, "%s: Invalid mode '%s'\n", cmd, marg);
@@ -247,8 +244,8 @@ int main(int argc, char *argv[]) {
times[1] = times[0];
} else {
// Don't use UTIME_NOW, so that multiple paths all get the same timestamp
- if (xgettime(&times[0]) != 0) {
- perror("xgettime()");
+ if (clock_gettime(CLOCK_REALTIME, &times[0]) != 0) {
+ perror("clock_gettime()");
return EXIT_FAILURE;
}
times[1] = times[0];