diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/bfstd.c | 4 | ||||
-rw-r--r-- | tests/ioq.c | 6 | ||||
-rw-r--r-- | tests/main.c | 6 | ||||
-rw-r--r-- | tests/sighook.c | 97 | ||||
-rw-r--r-- | tests/tests.h | 18 | ||||
-rw-r--r-- | tests/util.sh | 9 | ||||
-rw-r--r-- | tests/xspawn.c | 53 | ||||
-rw-r--r-- | tests/xtime.c | 20 | ||||
-rw-r--r-- | tests/xtouch.c | 2 |
9 files changed, 150 insertions, 65 deletions
diff --git a/tests/bfstd.c b/tests/bfstd.c index 07b68b0..f0f61ce 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -15,12 +15,12 @@ static bool check_base_dir(const char *path, const char *dir, const char *base) bool ret = true; char *xdir = xdirname(path); - bfs_verify(xdir, "xdirname(): %s", xstrerror(errno)); + bfs_everify(xdir, "xdirname()"); ret &= bfs_check(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir); free(xdir); char *xbase = xbasename(path); - bfs_verify(xbase, "xbasename(): %s", xstrerror(errno)); + bfs_everify(xbase, "xbasename()"); ret &= bfs_check(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); free(xbase); diff --git a/tests/ioq.c b/tests/ioq.c index ef5ee3b..99c98a2 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -40,15 +40,15 @@ static void check_ioq_push_block(void) { const size_t depth = 2; struct ioq *ioq = ioq_create(depth, 1); - bfs_verify(ioq, "ioq_create(): %s", xstrerror(errno)); + bfs_everify(ioq, "ioq_create()"); // Push enough operations to fill the queue for (size_t i = 0; i < depth; ++i) { struct bfs_dir *dir = bfs_allocdir(); - bfs_verify(dir, "bfs_allocdir(): %s", xstrerror(errno)); + bfs_everify(dir, "bfs_allocdir()"); int ret = ioq_opendir(ioq, dir, AT_FDCWD, ".", 0, NULL); - bfs_verify(ret == 0, "ioq_opendir(): %s", xstrerror(errno)); + bfs_everify(ret == 0, "ioq_opendir()"); } bfs_verify(ioq_capacity(ioq) == 0); diff --git a/tests/main.c b/tests/main.c index 429772b..aef0583 100644 --- a/tests/main.c +++ b/tests/main.c @@ -9,7 +9,6 @@ #include "tests.h" #include "bfstd.h" #include "color.h" -#include <errno.h> #include <locale.h> #include <stdio.h> #include <stdlib.h> @@ -90,10 +89,6 @@ static void run_test(struct test_ctx *ctx, const char *test, test_fn *fn) { } } -const char *bfs_errstr(void) { - return xstrerror(errno); -} - int main(int argc, char *argv[]) { // Try to set a UTF-8 locale if (!setlocale(LC_ALL, "C.UTF-8")) { @@ -116,6 +111,7 @@ int main(int argc, char *argv[]) { run_test(&ctx, "bfstd", check_bfstd); run_test(&ctx, "bit", check_bit); run_test(&ctx, "ioq", check_ioq); + run_test(&ctx, "sighook", check_sighook); run_test(&ctx, "trie", check_trie); run_test(&ctx, "xspawn", check_xspawn); run_test(&ctx, "xtime", check_xtime); diff --git a/tests/sighook.c b/tests/sighook.c new file mode 100644 index 0000000..c94526e --- /dev/null +++ b/tests/sighook.c @@ -0,0 +1,97 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "sighook.h" +#include "atomic.h" +#include "thread.h" +#include <pthread.h> +#include <signal.h> +#include <sys/time.h> + +static atomic size_t count = 0; + +/** SIGALRM handler. */ +static void alrm_hook(int sig, siginfo_t *info, void *arg) { + fetch_add(&count, 1, relaxed); +} + +/** Swap out an old hook for a new hook. */ +static int swap_hooks(struct sighook **hook) { + struct sighook *next = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); + if (!bfs_echeck(next, "sighook(SIGALRM)")) { + return -1; + } + + sigunhook(*hook); + *hook = next; + return 0; +} + +/** Background thread that rapidly (un)registers signal hooks. */ +static void *hook_thread(void *ptr) { + struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); + if (!bfs_echeck(hook, "sighook(SIGALRM)")) { + return NULL; + } + + while (load(&count, relaxed) < 1000) { + if (swap_hooks(&hook) != 0) { + sigunhook(hook); + return NULL; + } + } + + sigunhook(hook); + return &count; +} + +bool check_sighook(void) { + bool ret = true; + + struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); + ret &= bfs_echeck(hook, "sighook(SIGALRM)"); + if (!ret) { + goto done; + } + + struct itimerval ival = { + .it_value = { + .tv_usec = 100, + }, + .it_interval = { + .tv_usec = 100, + }, + }; + ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0); + if (!ret) { + goto unhook; + } + + pthread_t thread; + ret &= bfs_echeck(thread_create(&thread, NULL, hook_thread, NULL) == 0); + if (!ret) { + goto untime; + } + + while (ret && load(&count, relaxed) < 1000) { + ret &= swap_hooks(&hook) == 0; + } + + void *ptr; + thread_join(thread, &ptr); + ret &= bfs_check(ptr); + +untime: + ival.it_value.tv_usec = 0; + ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0); + if (!ret) { + goto unhook; + } + +unhook: + sigunhook(hook); +done: + return ret; +} diff --git a/tests/tests.h b/tests/tests.h index 9078938..19b7f5e 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -26,6 +26,9 @@ bool check_bit(void); /** I/O queue tests. */ bool check_ioq(void); +/** Signal hook tests. */ +bool check_sighook(void); + /** Trie tests. */ bool check_trie(void); @@ -54,20 +57,17 @@ static inline bool bfs_check(bool ret) { : "Check failed: `%s`%s", \ str, __VA_ARGS__), false)) -/** Get a string description of the last error. */ -const char *bfs_errstr(void); - /** * Check a condition, logging the current error string on failure. */ -#define bfs_pcheck(...) \ - bfs_pcheck_(#__VA_ARGS__, __VA_ARGS__, "", "") +#define bfs_echeck(...) \ + bfs_echeck_(#__VA_ARGS__, __VA_ARGS__, "", bfs_errstr()) -#define bfs_pcheck_(str, cond, format, ...) \ +#define bfs_echeck_(str, cond, format, ...) \ ((cond) ? true : (bfs_diag( \ sizeof(format) > 1 \ - ? "%.0s" format "%s%s: %s" \ - : "Check failed: `%s`%s: %s", \ - str, __VA_ARGS__, bfs_errstr()), false)) + ? "%.0s" format "%s: %s" \ + : "Check failed: `%s`: %s", \ + str, __VA_ARGS__), false)) #endif // BFS_TESTS_H diff --git a/tests/util.sh b/tests/util.sh index 7dba9fb..3969db5 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -12,12 +12,9 @@ _realpath() ( ) # Globals -TESTS=$(_realpath "$TESTS") -if [ "${BUILDDIR-}" ]; then - BIN=$(_realpath "$BUILDDIR/bin") -else - BIN=$(_realpath "$TESTS/../bin") -fi +ROOT=$(_realpath "$(dirname -- "$TESTS")") +TESTS="$ROOT/tests" +BIN="$ROOT/bin" MKSOCK="$BIN/tests/mksock" XTOUCH="$BIN/tests/xtouch" UNAME=$(uname) diff --git a/tests/xspawn.c b/tests/xspawn.c index 7362aa5..b1d6dc1 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -54,7 +54,7 @@ static bool check_use_path(bool use_posix) { bool ret = true; struct bfs_spawn spawn; - ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0); + ret &= bfs_echeck(bfs_spawn_init(&spawn) == 0); if (!ret) { goto out; } @@ -64,34 +64,27 @@ static bool check_use_path(bool use_posix) { spawn.flags &= ~BFS_SPAWN_USE_POSIX; } - const char *builddir = getenv("BUILDDIR"); - dchar *bin = dstrprintf("%s/bin", builddir ? builddir : "."); - ret &= bfs_pcheck(bin, "dstrprintf()"); + 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 &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, bin, O_RDONLY | O_DIRECTORY, 0) == 0); - ret &= bfs_pcheck(bfs_spawn_adddup2(&spawn, 10, 11) == 0); - ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0); - ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 11) == 0); - ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 11) == 0); - if (!ret) { - goto bin; - } - // Check that $PATH is resolved in the parent's environment char **envp; - ret &= bfs_pcheck(envp = envdup()); + ret &= bfs_echeck(envp = envdup()); if (!ret) { - goto bin; + goto destroy; } // Check that $PATH is resolved after the file actions char *old_path = getenv("PATH"); dchar *new_path = NULL; if (old_path) { - ret &= bfs_pcheck(old_path = strdup(old_path)); + ret &= bfs_echeck(old_path = strdup(old_path)); if (!ret) { goto env; } @@ -104,20 +97,20 @@ static bool check_use_path(bool use_posix) { goto path; } - ret &= bfs_pcheck(setenv("PATH", new_path, true) == 0); + ret &= bfs_echeck(setenv("PATH", new_path, true) == 0); if (!ret) { goto path; } char *argv[] = {"xspawnee", old_path, NULL}; pid_t pid = bfs_spawn("xspawnee", &spawn, argv, envp); - ret &= bfs_pcheck(pid >= 0, "bfs_spawn()"); + ret &= bfs_echeck(pid >= 0, "bfs_spawn()"); if (!ret) { goto unset; } int wstatus; - ret &= bfs_pcheck(xwaitpid(pid, &wstatus, 0) == pid) + ret &= bfs_echeck(xwaitpid(pid, &wstatus, 0) == pid) && bfs_check(WIFEXITED(wstatus)); if (ret) { int wexit = WEXITSTATUS(wstatus); @@ -126,9 +119,9 @@ static bool check_use_path(bool use_posix) { unset: if (old_path) { - ret &= bfs_pcheck(setenv("PATH", old_path, true) == 0); + ret &= bfs_echeck(setenv("PATH", old_path, true) == 0); } else { - ret &= bfs_pcheck(unsetenv("PATH") == 0); + ret &= bfs_echeck(unsetenv("PATH") == 0); } path: dstrfree(new_path); @@ -138,10 +131,8 @@ env: free(*var); } free(envp); -bin: - dstrfree(bin); destroy: - ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0); + ret &= bfs_echeck(bfs_spawn_destroy(&spawn) == 0); out: return ret; } @@ -151,7 +142,7 @@ static bool check_enoent(bool use_posix) { bool ret = true; struct bfs_spawn spawn; - ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0); + ret &= bfs_echeck(bfs_spawn_init(&spawn) == 0); if (!ret) { goto out; } @@ -163,9 +154,9 @@ static bool check_enoent(bool use_posix) { char *argv[] = {"eW6f5RM9Qi", NULL}; pid_t pid = bfs_spawn("eW6f5RM9Qi", &spawn, argv, NULL); - ret &= bfs_pcheck(pid < 0 && errno == ENOENT, "bfs_spawn()"); + ret &= bfs_echeck(pid < 0 && errno == ENOENT, "bfs_spawn()"); - ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0); + ret &= bfs_echeck(bfs_spawn_destroy(&spawn) == 0); out: return ret; } @@ -175,18 +166,18 @@ static bool check_resolve(void) { char *exe; exe = bfs_spawn_resolve("sh"); - ret &= bfs_pcheck(exe, "bfs_spawn_resolve('sh')"); + ret &= bfs_echeck(exe, "bfs_spawn_resolve('sh')"); free(exe); exe = bfs_spawn_resolve("/bin/sh"); - ret &= bfs_pcheck(exe && strcmp(exe, "/bin/sh") == 0); + ret &= bfs_echeck(exe && strcmp(exe, "/bin/sh") == 0); free(exe); exe = bfs_spawn_resolve("bin/tests/xspawnee"); - ret &= bfs_pcheck(exe && strcmp(exe, "bin/tests/xspawnee") == 0); + ret &= bfs_echeck(exe && strcmp(exe, "bin/tests/xspawnee") == 0); free(exe); - ret &= bfs_pcheck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT); + ret &= bfs_echeck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT); return ret; } diff --git a/tests/xtime.c b/tests/xtime.c index a7c63d2..1907e26 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -29,9 +29,9 @@ static bool check_one_xgetdate(const char *str, int error, time_t expected) { int ret = xgetdate(str, &ts); if (error) { - return bfs_pcheck(ret == -1 && errno == error, "xgetdate('%s')", str); + return bfs_echeck(ret == -1 && errno == error, "xgetdate('%s')", str); } else { - return bfs_pcheck(ret == 0, "xgetdate('%s')", str) + return bfs_echeck(ret == 0, "xgetdate('%s')", str) && bfs_check(ts.tv_sec == expected && ts.tv_nsec == 0, "xgetdate('%s'): %jd.%09jd != %jd", str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); @@ -82,12 +82,12 @@ static bool check_xgetdate(void) { static bool check_one_xmktime(time_t expected) { struct tm tm; if (!localtime_r(&expected, &tm)) { - bfs_diag("localtime_r(%jd): %s", (intmax_t)expected, xstrerror(errno)); + bfs_ediag("localtime_r(%jd)", (intmax_t)expected); return false; } time_t actual; - return bfs_pcheck(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT ")", TM_PRINTF(tm)) + return bfs_echeck(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT ")", TM_PRINTF(tm)) && bfs_check(actual == expected, "xmktime(" TM_FORMAT "): %jd != %jd", TM_PRINTF(tm), (intmax_t)actual, (intmax_t)expected); } @@ -137,6 +137,7 @@ static bool check_one_xtimegm(const struct tm *tm) { return ret; } +#if !BFS_HAS_TIMEGM /** Check an overflowing xtimegm() call. */ static bool check_xtimegm_overflow(const struct tm *tm) { struct tm copy = *tm; @@ -154,6 +155,7 @@ static bool check_xtimegm_overflow(const struct tm *tm) { return ret; } +#endif /** xtimegm() tests. */ static bool check_xtimegm(void) { @@ -173,11 +175,13 @@ static bool check_xtimegm(void) { ret &= check_one_xtimegm(&tm); } +#if !BFS_HAS_TIMEGM // Check integer overflow cases - check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX }); +#endif return ret; } diff --git a/tests/xtouch.c b/tests/xtouch.c index cd41842..427e3e0 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -120,7 +120,7 @@ static int at_flags(const struct args *args) { /** Touch one path. */ static int xtouch(const struct args *args, const char *path) { int dfd = open_parent(args, &path); - if (dfd < 0 && dfd != AT_FDCWD) { + if (dfd < 0 && dfd != (int)AT_FDCWD) { return -1; } |