summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile10
-rw-r--r--tests/main.c1
-rw-r--r--tests/tests.h3
-rw-r--r--tests/xspawn.c154
-rw-r--r--tests/xspawnee.c17
5 files changed, 183 insertions, 2 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 7dcd76f..080fb17 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -270,10 +270,15 @@ LIBBFS := \
$(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS)
# Testing utilities
-TEST_UTILS := $(BIN)/tests/mksock $(BIN)/tests/xtouch
+TEST_UTILS := \
+ $(BIN)/tests/mksock \
+ $(BIN)/tests/xspawnee \
+ $(BIN)/tests/xtouch
$(BIN)/tests/mksock: $(OBJ)/tests/mksock.o $(LIBBFS)
+$(BIN)/tests/xspawnee: $(OBJ)/tests/xspawnee.o
+
$(BIN)/tests/xtouch: $(OBJ)/tests/xtouch.o $(LIBBFS)
# All test binaries
@@ -286,6 +291,7 @@ $(BIN)/tests/units: \
$(OBJ)/tests/ioq.o \
$(OBJ)/tests/main.o \
$(OBJ)/tests/trie.o \
+ $(OBJ)/tests/xspawn.o \
$(OBJ)/tests/xtime.o \
$(LIBBFS)
@@ -294,7 +300,7 @@ tests: $(TESTS)
.PHONY: tests
# Run the unit tests
-unit-tests: $(BIN)/tests/units
+unit-tests: $(BIN)/tests/units $(BIN)/tests/xspawnee
$<
.PHONY: unit-tests
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;
+}