summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-03-27 11:27:01 -0400
committerTavian Barnes <tavianator@tavianator.com>2024-03-27 13:13:23 -0400
commitbca78d2597c78b0166d76d023eee1354f5abd04e (patch)
treecfe0b39dc687e1014a7e5bf138d6f9df0b1acd0b /tests
parent25769288f62816d733004c9e8347c35dd0e4ce2a (diff)
downloadbfs-bca78d2597c78b0166d76d023eee1354f5abd04e.tar.xz
tests/xspawn: New unit test
Diffstat (limited to 'tests')
-rw-r--r--tests/main.c1
-rw-r--r--tests/tests.h3
-rw-r--r--tests/xspawn.c154
-rw-r--r--tests/xspawnee.c17
4 files changed, 175 insertions, 0 deletions
diff --git a/tests/main.c b/tests/main.c
index 7dec320..69903d4 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -117,6 +117,7 @@ int main(int argc, char *argv[]) {
run_test(&ctx, "bit", check_bit);
run_test(&ctx, "ioq", check_ioq);
run_test(&ctx, "trie", check_trie);
+ run_test(&ctx, "xspawn", check_xspawn);
run_test(&ctx, "xtime", check_xtime);
done:
diff --git a/tests/tests.h b/tests/tests.h
index b644450..351badb 100644
--- a/tests/tests.h
+++ b/tests/tests.h
@@ -29,6 +29,9 @@ bool check_ioq(void);
/** Trie tests. */
bool check_trie(void);
+/** Process spawning tests. */
+bool check_xspawn(void);
+
/** Time tests. */
bool check_xtime(void);
diff --git a/tests/xspawn.c b/tests/xspawn.c
new file mode 100644
index 0000000..e356842
--- /dev/null
+++ b/tests/xspawn.c
@@ -0,0 +1,154 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include "tests.h"
+#include "../src/alloc.h"
+#include "../src/bfstd.h"
+#include "../src/config.h"
+#include "../src/dstring.h"
+#include "../src/xspawn.h"
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+
+/** Duplicate the current environment. */
+static char **envdup(void) {
+ extern char **environ;
+
+ char **envp = NULL;
+ size_t envc = 0;
+
+ for (char **var = environ; ; ++var) {
+ char *copy = NULL;
+ if (*var) {
+ copy = strdup(*var);
+ if (!copy) {
+ goto fail;
+ }
+ }
+
+ char **dest = RESERVE(char *, &envp, &envc);
+ if (!dest) {
+ free(copy);
+ goto fail;
+ }
+ *dest = copy;
+
+ if (!*var) {
+ break;
+ }
+ }
+
+ return envp;
+
+fail:
+ for (size_t i = 0; i < envc; ++i) {
+ free(envp[i]);
+ }
+ free(envp);
+ return NULL;
+}
+
+/** Check that we resolve executables in $PATH correctly. */
+static bool check_path_resolution(bool use_posix) {
+ bool ret = true;
+
+ struct bfs_spawn spawn;
+ ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0);
+ if (!ret) {
+ goto out;
+ }
+
+ spawn.flags |= BFS_SPAWN_USE_PATH;
+ if (!use_posix) {
+ spawn.flags &= ~BFS_SPAWN_USE_POSIX;
+ }
+
+ const char *builddir = getenv("BUILDDIR");
+ dchar *bin = dstrprintf("%s/bin", builddir ? builddir : ".");
+ ret &= bfs_pcheck(bin, "dstrprintf()");
+ if (!ret) {
+ goto destroy;
+ }
+
+ ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, bin, O_RDONLY | O_DIRECTORY, 0) == 0);
+ ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 10) == 0);
+ ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0);
+ if (!ret) {
+ goto bin;
+ }
+
+ // Check that $PATH is resolved in the parent's environment
+ char **envp;
+ ret &= bfs_pcheck(envp = envdup());
+ if (!ret) {
+ goto bin;
+ }
+
+ // Check that $PATH is resolved after the file actions
+ char *old_path = getenv("PATH");
+ dchar *new_path = NULL;
+ if (old_path) {
+ ret &= bfs_pcheck(old_path = strdup(old_path));
+ if (!ret) {
+ goto env;
+ }
+ new_path = dstrprintf("tests:%s", old_path);
+ } else {
+ new_path = dstrdup("tests");
+ }
+ ret &= bfs_check(new_path);
+ if (!ret) {
+ goto path;
+ }
+
+ ret &= bfs_pcheck(setenv("PATH", new_path, true) == 0);
+ if (!ret) {
+ goto path;
+ }
+
+ char *argv[] = {"xspawnee", old_path, NULL};
+ pid_t pid = bfs_spawn("xspawnee", &spawn, argv, envp);
+ ret &= bfs_pcheck(pid >= 0, "bfs_spawn()");
+ if (!ret) {
+ goto unset;
+ }
+
+ int wstatus;
+ ret &= bfs_pcheck(xwaitpid(pid, &wstatus, 0) == pid)
+ && bfs_check(WIFEXITED(wstatus));
+ if (ret) {
+ int wexit = WEXITSTATUS(wstatus);
+ ret &= bfs_check(wexit == EXIT_SUCCESS, "xspawnee: exit(%d)", wexit);
+ }
+
+unset:
+ if (old_path) {
+ ret &= bfs_pcheck(setenv("PATH", old_path, true) == 0);
+ } else {
+ ret &= bfs_pcheck(unsetenv("PATH") == 0);
+ }
+path:
+ dstrfree(new_path);
+ free(old_path);
+env:
+ for (char **var = envp; *var; ++var) {
+ free(*var);
+ }
+ free(envp);
+bin:
+ dstrfree(bin);
+destroy:
+ ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0);
+out:
+ return ret;
+}
+
+bool check_xspawn(void) {
+ bool ret = true;
+
+ ret &= check_path_resolution(true);
+ ret &= check_path_resolution(false);
+
+ return ret;
+}
diff --git a/tests/xspawnee.c b/tests/xspawnee.c
new file mode 100644
index 0000000..b0a76ca
--- /dev/null
+++ b/tests/xspawnee.c
@@ -0,0 +1,17 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include <stdlib.h>
+#include <string.h>
+
+/** Child binary for bfs_spawn() tests. */
+int main(int argc, char *argv[]) {
+ if (argc >= 2) {
+ const char *path = getenv("PATH");
+ if (!path || strcmp(path, argv[1]) != 0) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}