diff options
-rw-r--r-- | GNUmakefile | 10 | ||||
-rw-r--r-- | tests/main.c | 1 | ||||
-rw-r--r-- | tests/tests.h | 3 | ||||
-rw-r--r-- | tests/xspawn.c | 154 | ||||
-rw-r--r-- | tests/xspawnee.c | 17 |
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; +} |