diff options
author | dzwdz | 2023-08-14 18:51:07 +0200 |
---|---|---|
committer | dzwdz | 2023-08-14 18:51:07 +0200 |
commit | 642b5fb0007b64c77d186fcb018d571152ee1d47 (patch) | |
tree | 1c466461f3602d306be309a053edae558ef2568e /src/cmd/tests | |
parent | 8050069c57b729c18c19b1a03ab6e4bf63b4735e (diff) |
reorganization: first steps
Diffstat (limited to 'src/cmd/tests')
-rw-r--r-- | src/cmd/tests/kernel/fdlimit.c | 49 | ||||
-rw-r--r-- | src/cmd/tests/kernel/fs.c | 79 | ||||
-rw-r--r-- | src/cmd/tests/kernel/misc.c | 66 | ||||
-rw-r--r-- | src/cmd/tests/kernel/miscsyscall.c | 315 | ||||
-rw-r--r-- | src/cmd/tests/kernel/path.c | 108 | ||||
-rw-r--r-- | src/cmd/tests/kernel/threads.c | 55 | ||||
-rw-r--r-- | src/cmd/tests/libc/esemaphore.c | 95 | ||||
-rw-r--r-- | src/cmd/tests/libc/setjmp.c | 31 | ||||
-rw-r--r-- | src/cmd/tests/libc/string.c | 124 | ||||
-rw-r--r-- | src/cmd/tests/shared/printf.c | 55 | ||||
-rw-r--r-- | src/cmd/tests/shared/ringbuf.c | 49 | ||||
-rw-r--r-- | src/cmd/tests/stress.c | 28 | ||||
-rw-r--r-- | src/cmd/tests/tests.c | 68 | ||||
-rw-r--r-- | src/cmd/tests/tests.h | 37 |
14 files changed, 1159 insertions, 0 deletions
diff --git a/src/cmd/tests/kernel/fdlimit.c b/src/cmd/tests/kernel/fdlimit.c new file mode 100644 index 0000000..f332357 --- /dev/null +++ b/src/cmd/tests/kernel/fdlimit.c @@ -0,0 +1,49 @@ +#include "../tests.h" +#include <camellia/flags.h> +#include <errno.h> +#include <unistd.h> + +static void test_fdlimit_pipe(void) { + hid_t ends[2]; + hid_t h[2] = {-1, -1}; + for (;;) { + hid_t cur = _sys_open("/", 1, OPEN_READ); + if (cur == -EMFILE) break; + test(cur >= 0); + h[0] = h[1]; + h[1] = cur; + } + test(h[0] >= 0); /* we need at least two handles */ + + test(_sys_pipe(ends, 0) == -EMFILE); + test(_sys_open("/", 1, OPEN_READ) == -EMFILE); + + close(h[1]); + test(_sys_pipe(ends, 0) == -EMFILE); + test(_sys_open("/", 1, OPEN_READ) == h[1]); + test(_sys_open("/", 1, OPEN_READ) == -EMFILE); + + close(h[0]); + close(h[1]); + test(_sys_pipe(ends, 0) == 0); +} + +static void test_fdlimit_fork(void) { + for (;;) { + hid_t cur = _sys_open("/", 1, OPEN_READ); + if (cur == -EMFILE) break; + test(cur >= 0); + } + + if (!_sys_fork(0, NULL)) _sys_exit(123); + + test(_sys_fork(FORK_NEWFS, NULL) == -EMFILE); + test(_sys_await() == 123); + test(_sys_await() == ~0); +} + +void r_k_fdlimit(void) { + /* all these tests implicitly test if the vfs returns -EMFILE */ + run_test(test_fdlimit_pipe); + run_test(test_fdlimit_fork); +} diff --git a/src/cmd/tests/kernel/fs.c b/src/cmd/tests/kernel/fs.c new file mode 100644 index 0000000..b5684d7 --- /dev/null +++ b/src/cmd/tests/kernel/fs.c @@ -0,0 +1,79 @@ +#include "../tests.h" +#include <camellia/flags.h> +#include <camellia/syscalls.h> +#include <stdbool.h> +#include <string.h> + +static void test_unfinished_req(void) { + hid_t h = -1; + int ret = _sys_fork(FORK_NEWFS, &h); + test(0 <= ret); + if (ret == 0) { + // TODO make a similar test with all 0s passed to fs_wait + struct ufs_request res; + _sys_fs_wait(NULL, 0, &res); + // TODO second fs_wait + exit(0); + } else { + test(0 <= h); + test(_sys_mount(h, "/", 1) == 0); + int ret = _sys_open("/", 1, 0); + test(ret < 0); + // the handler quits while handling that call - but this syscall should return anyways + } +} + +static void test_orphaned_fs(void) { + hid_t h = -1; + int ret = _sys_fork(FORK_NEWFS, &h); + test(0 <= ret); + if (ret == 0) { + exit(0); + } else { + test(0 <= h); + test(_sys_mount(h, "/", 1) == 0); + int ret = _sys_open("/", 1, 0); + test(ret < 0); + } +} + +static void test_fs_cleanup(void) { + const char *msg = "success"; + int msglen = 8; + char buf[16]; + hid_t ends[2]; + test(_sys_pipe(ends, 0) >= 0); + if (!_sys_fork(0, NULL)) { + hid_t h = -1; + if (_sys_fork(FORK_NEWFS, &h) == 0) { + for (;;) { + struct ufs_request req; + hid_t reqh = _sys_fs_wait(buf, sizeof buf, &req); + if (reqh < 0) break; + _sys_fs_respond(reqh, NULL, 0, 0); /* success */ + } + /* this is the test: does it break out of the loop + * when it should cleanup */ + _sys_write(ends[1], msg, msglen, -1, 0); + exit(0); + } else { + test(_sys_mount(h, "/", 1) == 0); + h = _sys_open("/", 1, 0); + test(h >= 0); + _sys_close(h); + // TODO another test without the delay + _sys_sleep(0); + exit(0); + } + } else { + test(_sys_read(ends[0], buf, sizeof buf, 0) == msglen); + test(memcmp(buf, msg, msglen) == 0); + } +} + +void r_k_fs(void) { + run_test(test_unfinished_req); + run_test(test_orphaned_fs); + run_test(test_fs_cleanup); + run_test(test_fs_cleanup); +} diff --git a/src/cmd/tests/kernel/misc.c b/src/cmd/tests/kernel/misc.c new file mode 100644 index 0000000..5d6b531 --- /dev/null +++ b/src/cmd/tests/kernel/misc.c @@ -0,0 +1,66 @@ +#include "../tests.h" +#include <camellia/errno.h> +#include <camellia/flags.h> +#include <camellia/syscalls.h> +#include <string.h> +#include <unistd.h> + +static void test_fault_kill(void) { + if (!fork()) { /* invalid memory access */ + asm volatile("movb $69, 0" ::: "memory"); + test_fail(); + } + if (!fork()) { /* #GP */ + asm volatile("hlt" ::: "memory"); + test_fail(); + } + + int await_cnt = 0; + while (_sys_await() != ~0) await_cnt++; + test(await_cnt == 2); +} + +static void test_efault(void) { +#if 0 + const char *str = "o, 16 characters"; + char str2[16]; + char *invalid = (void*)0x1000; + hid_t h; + + memcpy(str2, str, 16); + + test((h = _sys_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE | OPEN_WRITE)) >= 0); + test(_sys_write(h, str, 16, 0, 0) == 16); + test(_sys_write(h, str2, 16, 0, 0) == 16); + + test(_sys_write(h, invalid, 16, 0, 0) == -EFAULT); + + /* x64 non-canonical pointers */ + test(_sys_write(h, (void*)((uintptr_t)str ^ 0x8000000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str ^ 0x1000000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0100000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0010000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0001000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str ^ 0x0000800000000000), 16, 0, 0) == -EFAULT); + + test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x8000000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x1000000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0100000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0010000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0001000000000000), 16, 0, 0) == -EFAULT); + test(_sys_write(h, (void*)((uintptr_t)str2 ^ 0x0000800000000000), 16, 0, 0) == -EFAULT); + + test(_sys_write(h, str, 16, 0, 0) == 16); + close(h); +#endif +} + +static void test_invalid_syscall(void) { + test(_syscall(~0, 0, 0, 0, 0, 0) < 0); +} + +void r_k_misc(void) { + run_test(test_fault_kill); + run_test(test_efault); + run_test(test_invalid_syscall); +} diff --git a/src/cmd/tests/kernel/miscsyscall.c b/src/cmd/tests/kernel/miscsyscall.c new file mode 100644 index 0000000..459da2a --- /dev/null +++ b/src/cmd/tests/kernel/miscsyscall.c @@ -0,0 +1,315 @@ +#include "../tests.h" +#include <camellia/errno.h> +#include <camellia/execbuf.h> +#include <camellia/flags.h> +#include <camellia/syscalls.h> +#include <string.h> +#include <unistd.h> + + +static void test_await(void) { + /* creates 16 child processes, each returning a different value. then checks + * if await() returns every value exactly once */ + int ret; + int counts[16] = {0}; + + for (int i = 0; i < 16; i++) + if (!fork()) + exit(i); + + while ((ret = _sys_await()) != ~0) { + test(0 <= ret && ret < 16); + counts[ret]++; + } + + for (int i = 0; i < 16; i++) + test(counts[i] == 1); +} + +static void test_await2(void) { + /* hangs on failure */ + if (!fork()) { + if (!fork()) { + for (;;) _sys_sleep(1000000000); + } else { + exit(123); + } + } + test(_sys_await() == 123); + test(_sys_await() == ~0); /* don't wait for tombstone */ + _sys_filicide(); +} + +static void test_wait2_basic(void) { + struct sys_wait2 data = {0}; + int pid; + pid = fork(); + if (pid == 0) { + exit(420); + } + test(_sys_wait2(-1, 0, &data) == pid); + test(data.status == 420); + test(_sys_wait2(-1, 0, NULL) == -ECHILD); +} + +static void test_pipe(void) { + const char *pipe_msgs[2] = {"hello", "world"}; + hid_t ends[2]; + char buf[16]; + + /* test regular reads / writes */ + test(_sys_pipe(ends, 0) >= 0); + if (!fork()) { + /* those repeated asserts ensure that you can't read/write to the wrong ends */ + test(_sys_read(ends[1], buf, 16, 0) < 0); + test(_sys_write(ends[0], buf, 16, 0, 0) < 0); + + test(5 == _sys_write(ends[1], pipe_msgs[0], 5, -1, 0)); + + test(_sys_read(ends[1], buf, 16, 0) < 0); + test(_sys_write(ends[0], buf, 16, 0, 0) < 0); + + test(5 == _sys_write(ends[1], pipe_msgs[1], 5, -1, 0)); + + exit(0); + } else { + test(_sys_read(ends[1], buf, 16, 0) < 0); + test(_sys_write(ends[0], buf, 16, 0, 0) < 0); + + test(5 == _sys_read(ends[0], buf, 16, 0)); + test(!memcmp(buf, pipe_msgs[0], 5)); + + test(_sys_read(ends[1], buf, 16, 0) < 0); + test(_sys_write(ends[0], buf, 16, 0, 0) < 0); + + test(5 == _sys_read(ends[0], buf, 16, 0)); + test(!memcmp(buf, pipe_msgs[1], 5)); + + _sys_await(); + } + close(ends[0]); + close(ends[1]); + + + /* writing to pipes with one end closed */ + test(_sys_pipe(ends, 0) >= 0); + for (int i = 0; i < 16; i++) { + if (!fork()) { + close(ends[1]); + test(_sys_read(ends[0], buf, 16, 0) < 0); + exit(0); + } + } + close(ends[1]); + for (int i = 0; i < 16; i++) + _sys_await(); + close(ends[0]); + + test(_sys_pipe(ends, 0) >= 0); + for (int i = 0; i < 16; i++) { + if (!fork()) { + close(ends[0]); + test(_sys_write(ends[1], buf, 16, 0, 0) < 0); + exit(0); + } + } + close(ends[0]); + for (int i = 0; i < 16; i++) + _sys_await(); + close(ends[1]); + + + /* queueing */ + test(_sys_pipe(ends, 0) >= 0); + for (int i = 0; i < 16; i++) { + if (!fork()) { + test(_sys_write(ends[1], pipe_msgs[0], 5, -1, 0) == 5); + exit(0); + } + } + close(ends[1]); + for (int i = 0; i < 16; i++) { + test(_sys_read(ends[0], buf, sizeof buf, 0) == 5); + _sys_await(); + } + test(_sys_read(ends[0], buf, sizeof buf, 0) < 0); + close(ends[0]); + + test(_sys_pipe(ends, 0) >= 0); + for (int i = 0; i < 16; i++) { + if (!fork()) { + memset(buf, 0, sizeof buf); + test(_sys_read(ends[0], buf, 5, -1) == 5); + test(!memcmp(buf, pipe_msgs[1], 5)); + exit(0); + } + } + close(ends[0]); + for (int i = 0; i < 16; i++) { + test(_sys_write(ends[1], pipe_msgs[1], 5, -1, 0) == 5); + _sys_await(); + } + test(_sys_write(ends[1], pipe_msgs[1], 5, -1, 0) < 0); + close(ends[1]); + + + // not a to.do detect when all processes that can read are stuck on writing to the pipe and vice versa + // it seems like linux just lets the process hang endlessly. +} + +static void test_memflag(void) { + void *page = (void*)0x77777000; + _sys_memflag(page, 4096, MEMFLAG_PRESENT); // allocate page + memset(page, 77, 4096); // write to it + _sys_memflag(page, 4096, 0); // free it + + if (!fork()) { + memset(page, 11, 4096); // should segfault + exit(0); + } else { + test(_sys_await() != 0); // test if the process crashed + } + + char *pages[4]; + for (size_t i = 0; i < 4; i++) { + pages[i] = _sys_memflag(NULL, 4096, MEMFLAG_FINDFREE); + test(pages[i] == _sys_memflag(NULL, 4096, MEMFLAG_FINDFREE | MEMFLAG_PRESENT)); + if (i > 0) test(pages[i] != pages[i-1]); + *pages[i] = 0x77; + } + + for (size_t i = 0; i < 4; i++) + _sys_memflag(pages, 4096, MEMFLAG_PRESENT); +} + +static void test_dup(void) { + hid_t pipe[2]; + hid_t h1, h2; + test(_sys_pipe(pipe, 0) >= 0); + + if (!fork()) { + close(pipe[0]); + + h1 = _sys_dup(pipe[1], -1, 0); + test(h1 >= 0); + test(h1 != pipe[1]); + h2 = _sys_dup(h1, -1, 0); + test(h2 >= 0); + test(h2 != pipe[1] && h2 != h1); + + _sys_write(pipe[1], "og", 2, 0, 0); + _sys_write(h1, "h1", 2, 0, 0); + _sys_write(h2, "h2", 2, 0, 0); + + close(pipe[1]); + _sys_write(h1, "h1", 2, 0, 0); + _sys_write(h2, "h2", 2, 0, 0); + + test(_sys_dup(h1, pipe[1], 0) == pipe[1]); + test(_sys_dup(h2, pipe[1], 0) == pipe[1]); + test(_sys_dup(h1, pipe[1], 0) == pipe[1]); + test(_sys_dup(h2, pipe[1], 0) == pipe[1]); + close(h1); + close(h2); + + test(_sys_dup(pipe[1], h2, 0) == h2); + _sys_write(h2, "h2", 2, 0, 0); + close(h2); + + test(_sys_dup(pipe[1], h1, 0) == h1); + _sys_write(h1, "h1", 2, 0, 0); + close(h1); + + exit(0); + } else { + char buf[16]; + size_t count = 0; + close(pipe[1]); + while (_sys_read(pipe[0], buf, sizeof buf, 0) >= 0) + count++; + test(count == 7); + _sys_await(); + } + + + close(pipe[0]); +} + +static void test_execbuf(void) { + if (!fork()) { + uint64_t buf[] = { + EXECBUF_SYSCALL, _SYS_EXIT, 123, 0, 0, 0, 0, + }; + _sys_execbuf(buf, sizeof buf); + test_fail(); + } else { + test(_sys_await() == 123); + } +} + +static void test_sleep(void) { + hid_t reader; + FILE *writer; + if (!forkpipe(&writer, &reader)) { + if (!fork()) { + if (!fork()) { + _sys_sleep(100); + fprintf(writer, "1"); + _sys_sleep(200); + fprintf(writer, "3"); + _sys_sleep(200); + fprintf(writer, "5"); + _sys_exit(0); + } + if (!fork()) { + fprintf(writer, "0"); + _sys_sleep(200); + fprintf(writer, "2"); + _sys_sleep(200); + fprintf(writer, "4"); + /* get killed while asleep + * a peaceful death, i suppose. */ + for (;;) _sys_sleep(1000000000); + } + _sys_await(); + _sys_filicide(); + _sys_exit(0); + } + + /* this part checks if, after killing an asleep process, + * other processes can still wake up */ + _sys_sleep(600); + fprintf(writer, "6"); + exit(0); + } else { + const char *expected = "0123456"; + size_t target = strlen(expected); + size_t pos = 0; + for (;;) { + char buf[128]; + long ret = _sys_read(reader, buf, sizeof buf, 0); + if (ret < 0) break; + test(pos + ret <= target); + test(memcmp(buf, expected + pos, ret) == 0); + pos += ret; + } + test(pos == target); + } +} + +static void test_badopen(void) { + test(_sys_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE | OPEN_WRITE) >= 0); + test(_sys_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE) == -EINVAL); +} + +void r_k_miscsyscall(void) { + run_test(test_await); + run_test(test_await2); + run_test(test_wait2_basic); + run_test(test_pipe); + run_test(test_memflag); + run_test(test_dup); + run_test(test_execbuf); + run_test(test_sleep); + run_test(test_badopen); +} diff --git a/src/cmd/tests/kernel/path.c b/src/cmd/tests/kernel/path.c new file mode 100644 index 0000000..5a22c36 --- /dev/null +++ b/src/cmd/tests/kernel/path.c @@ -0,0 +1,108 @@ +#include "../tests.h" +#include <camellia/path.h> +#include <string.h> +#include <camellia/compat.h> +#include <camellia/fs/misc.h> + +static void test_path_simplify(void) { + const char *testcases[][2] = { + {"/", "/"}, + {"/.", "/"}, + {"//", "/"}, + {"/asdf", "/asdf"}, + {"/asdf/", "/asdf/"}, + {"/asdf//", "/asdf/"}, + {"/asdf/./", "/asdf/"}, + {"/a/./b", "/a/b"}, + {"/a/./b/", "/a/b/"}, + + // some slightly less easy cases + {"/asdf/..", "/"}, + {"/asdf/../", "/"}, + {"/asdf/.", "/asdf/"}, + {"/asdf//.", "/asdf/"}, + + {"/foo/bar/..", "/foo/"}, + {"/foo/bar/../baz", "/foo/baz"}, + {"/foo/bar/../baz/", "/foo/baz/"}, + {"/foo/bar/xyz/..", "/foo/bar/"}, + {"/foo/bar/xyz/../", "/foo/bar/"}, + + // going under the root or close to it + {"/..", NULL}, + {"/../asdf", NULL}, + {"/../asdf/", NULL}, + {"/./a/../..", NULL}, + {"/a/a/../..", "/"}, + {"/a/../a/..", "/"}, + {"/a/../../a", NULL}, + {"/../a/../a", NULL}, + {"/../../a/a", NULL}, + {"/////../..", NULL}, + {"//a//../..", NULL}, + + // relative paths aren't allowed + {"relative", NULL}, + {"some/stuff", NULL}, + {"./stuff", NULL}, + {"../stuff", NULL}, + {"", NULL}, + }; + + char buf[256]; + for (size_t i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) { + const char *input = testcases[i][0]; + const char *expected = testcases[i][1]; + int len = path_simplify(input, buf, strlen(input)); + if (expected == NULL) { + test(len == 0); + } else { + // TODO an argument for printing info on test failure + test(len == (int)strlen(expected) && !memcmp(expected, buf, len)); + } + } +} + +static void mount_resolve_drv(const char *path) { + if (mount_at(path) != 0) return; + + struct ufs_request res; + while (!c0_fs_wait(NULL, 0, &res)) { + // TODO does the first argument of c0_fs_respond need to be non-const? + c0_fs_respond((void*)path, strlen(path), 0); + } + exit(1); +} + +static void test_mount_resolve(void) { + const char *testcases[][2] = { + {"/", "/"}, + {"/test", "/"}, + {"/dir", "/dir"}, + {"/dir/..", "/"}, + {"/dir/../dir", "/dir"}, + {"/dirry", "/"}, + {"/dir/", "/dir"}, + {"/dir/shadowed", "/dir"}, + {"/dir/shadowed/", "/dir"}, + }; + mount_resolve_drv("/"); + mount_resolve_drv("/dir/shadowed"); + mount_resolve_drv("/dir"); + + char buf[16]; + for (size_t i = 0; i < sizeof(testcases) / sizeof(testcases[0]); i++) { + const char *input = testcases[i][0]; + const char *expected = testcases[i][1]; + hid_t h = _sys_open(input, strlen(input), 0); + test(h >= 0); + int len = _sys_read(h, buf, sizeof buf, 0); + test(len == (int)strlen(expected) && !memcmp(expected, buf, len)); + _sys_close(h); + } +} + +void r_k_path(void) { + run_test(test_path_simplify); + run_test(test_mount_resolve); +} diff --git a/src/cmd/tests/kernel/threads.c b/src/cmd/tests/kernel/threads.c new file mode 100644 index 0000000..b3c1c06 --- /dev/null +++ b/src/cmd/tests/kernel/threads.c @@ -0,0 +1,55 @@ +#include "../tests.h" +#include <camellia/flags.h> +#include <camellia/syscalls.h> +#include <string.h> +#include <esemaphore.h> +#include <thread.h> + +int global_n; +static void basic_thread(void *sem) { + global_n = 10; + esem_signal(sem); +} +static void test_basic_thread(void) { + struct evil_sem *sem = esem_new(0); + global_n = 0; + thread_create(FORK_NOREAP, basic_thread, sem); + esem_wait(sem); + test(global_n == 10); +} + +hid_t global_h; +static void shared_handle(void *sem) { + hid_t ends[2]; + test(_sys_pipe(ends, 0) >= 0); + global_h = ends[0]; + esem_signal(sem); + _sys_write(ends[1], "Hello!", 7, -1, 0); +} +static void test_shared_handle(void) { + struct evil_sem *sem = esem_new(0); + char buf[16]; + global_h = -1; + thread_create(FORK_NOREAP, shared_handle, sem); + esem_wait(sem); + + test(global_h >= 0); + test(_sys_read(global_h, buf, sizeof buf, 0) == 7); + test(!strcmp("Hello!", buf)); +} + +static void many_thread(void *arg) { + *(uint64_t*)arg += 10; +} +static void test_many_threads(void) { + uint64_t n = 0; + for (int i = 0; i < 10; i++) thread_create(0, many_thread, &n); + for (int i = 0; i < 10; i++) _sys_await(); + test(n == 100); +} + +void r_k_threads(void) { + run_test(test_basic_thread); + run_test(test_shared_handle); + run_test(test_many_threads); +} diff --git a/src/cmd/tests/libc/esemaphore.c b/src/cmd/tests/libc/esemaphore.c new file mode 100644 index 0000000..f089f4f --- /dev/null +++ b/src/cmd/tests/libc/esemaphore.c @@ -0,0 +1,95 @@ +#include "../tests.h" +#include <camellia/flags.h> +#include <camellia/syscalls.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <esemaphore.h> + +static void odd(hid_t out, struct evil_sem *sem1, struct evil_sem *sem2) { + _sys_write(out, "1", 1, -1, 0); + esem_signal(sem1); + + esem_wait(sem2); + _sys_write(out, "3", 1, -1, 0); + esem_signal(sem1); + + esem_wait(sem2); + _sys_write(out, "5", 1, -1, 0); + esem_signal(sem1); +} + +static void even(hid_t out, struct evil_sem *sem1, struct evil_sem *sem2) { + esem_wait(sem1); + _sys_write(out, "2", 1, -1, 0); + esem_signal(sem2); + + esem_wait(sem1); + _sys_write(out, "4", 1, -1, 0); + esem_signal(sem2); + + esem_wait(sem1); + _sys_write(out, "6", 1, -1, 0); + esem_signal(sem2); +} + +static void test_semaphore(void) { + struct evil_sem *sem1, *sem2; + hid_t pipe[2]; + test(_sys_pipe(pipe, 0) >= 0); + + if (!fork()) { + sem1 = esem_new(0); + sem2 = esem_new(0); + test(sem1 && sem2); + if (!fork()) { + odd(pipe[1], sem1, sem2); + exit(69); + } else { + even(pipe[1], sem1, sem2); + test(_sys_await() == 69); + } + esem_free(sem1); + esem_free(sem2); + + _sys_write(pipe[1], "|", 1, -1, 0); + + sem1 = esem_new(0); + sem2 = esem_new(0); + test(sem1 && sem2); + if (!fork()) { + even(pipe[1], sem1, sem2); + exit(69); + } else { + odd(pipe[1], sem1, sem2); + test(_sys_await() == 69); + _sys_await(); + } + esem_free(sem1); + esem_free(sem2); + + _sys_filicide(); + exit(0); + } else { + close(pipe[1]); + + char buf[16]; + size_t pos = 0; + for (;;) { + int ret = _sys_read(pipe[0], buf + pos, sizeof(buf) - pos, 0); + if (ret < 0) break; + pos += ret; + } + buf[pos] = '\0'; // idc about the "potential" overflow + if (strcmp(buf, "123456|123456")) { + printf("%s\n", buf); + test_fail(); + } + + _sys_await(); + } +} + +void r_libc_esemaphore(void) { + run_test(test_semaphore); +} diff --git a/src/cmd/tests/libc/setjmp.c b/src/cmd/tests/libc/setjmp.c new file mode 100644 index 0000000..0dded9d --- /dev/null +++ b/src/cmd/tests/libc/setjmp.c @@ -0,0 +1,31 @@ +#include "../tests.h" +#include <stdbool.h> +#include <setjmp.h> + +static void test_setjmp(void) { + jmp_buf env; + volatile bool inner; + int val; + inner = false; + if (!(val = setjmp(env))) { + inner = true; + longjmp(env, 1234); + test(0); + } else { + test(val == 1234); + test(inner); + } + inner = false; + if (!(val = setjmp(env))) { + inner = true; + longjmp(env, 0); + test(0); + } else { + test(val == 1); + test(inner); + } +} + +void r_libc_setjmp(void) { + run_test(test_setjmp); +} diff --git a/src/cmd/tests/libc/string.c b/src/cmd/tests/libc/string.c new file mode 100644 index 0000000..6afe350 --- /dev/null +++ b/src/cmd/tests/libc/string.c @@ -0,0 +1,124 @@ +#include "../tests.h" +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +static void test_memcmp(void) { + test(0 == memcmp("some", "thing", 0)); + test(0 != memcmp("some", "thing", 1)); + test(0 != memcmp("some", "thing", 4)); + + test(0 == memcmp("test", "tennis", 0)); + test(0 == memcmp("test", "tennis", 1)); + test(0 == memcmp("test", "tennis", 2)); + test(0 != memcmp("test", "tennis", 3)); + test(0 != memcmp("test", "tennis", 4)); + test(0 != memcmp("test", "tennis", 5)); + + test(0 > memcmp("foo", "moo", 4)); + test(0 < memcmp("moo", "foo", 4)); + test(0 > memcmp("555", "654", 3)); + test(0 < memcmp("654", "555", 3)); +} + +static bool memall(const unsigned char *s, unsigned char c, size_t n) { + for (size_t i = 0; i < n; i++) + if (s[i] != c) return false; + return true; +} + +static void test_memset(void) { + const size_t buflen = 4096; + void *buf = malloc(buflen); + test(buf); + for (int i = 0; i < 257; i++) { + memset(buf, i, buflen); + test(memall(buf, i & 0xff, buflen)); + } + free(buf); +} + +static void test_memmove(void) { + const int partsize = 64; + char buf[partsize * 3]; + for (int i = 0; i < partsize * 2; i++) { + memset(buf, 0, sizeof buf); + for (int j = 0; j < partsize; j++) buf[i + j] = j; + memmove(buf + partsize, buf + i, partsize); + for (int j = 0; j < partsize; j++) test(buf[partsize + j] == j); + } +} + +static void test_strcmp(void) { + test(0 == strcmp("string", "string")); + test(0 > strcmp("str", "string")); + test(0 < strcmp("string", "str")); + + test(0 != strcmp("stress", "string")); + + test(0 != strncmp("abc", "ab", 3)); + test(0 == strncmp("abc", "ab", 2)); +} + +static void test_strtol(void) { + char *end; + test(1234 == strtol("1234", NULL, 10)); + test(1234 == strtol("+1234", NULL, 10)); + test(-1234 == strtol("-1234", NULL, 10)); + + test(1234 == strtol("1234", &end, 10)); + test(!strcmp("", end)); + test(1234 == strtol(" 1234hello", &end, 10)); + test(!strcmp("hello", end)); + + test(1234 == strtol(" 1234hello", &end, 0)); + test(!strcmp("hello", end)); + test(0xCAF3 == strtol(" 0xCaF3hello", &end, 0)); + test(!strcmp("hello", end)); + test(01234 == strtol(" 01234hello", &end, 0)); + test(!strcmp("hello", end)); +} + +static void test_strspn(void) { + test(0 == strspn("", "1234")); + test(0 == strspn("asdf", "1234")); + test(0 == strspn("a2df", "1234")); + test(2 == strspn("42df", "1234")); + test(4 == strspn("4211", "1234")); + + test(0 == strcspn("", "1234")); + test(4 == strcspn("asdf", "1234")); + test(1 == strcspn("a2df", "1234")); +} + +static void test_strtok(void) { + const char *sep = " \t"; + { + char line[] = "LINE TO BE SEPARATED"; + test(!strcmp(strtok(line, sep), "LINE")); + test(!strcmp(strtok(NULL, sep), "TO")); + test(!strcmp(strtok(NULL, sep), "BE")); + test(!strcmp(strtok(NULL, sep), "SEPARATED")); + for (int i = 0; i < 4; i++) + test(strtok(NULL, sep) == NULL); + } + { + char line[] = " LINE TO\tBE \t SEPARATED "; + test(!strcmp(strtok(line, sep), "LINE")); + test(!strcmp(strtok(NULL, sep), "TO")); + test(!strcmp(strtok(NULL, sep), "BE")); + test(!strcmp(strtok(NULL, sep), "SEPARATED")); + for (int i = 0; i < 4; i++) + test(strtok(NULL, sep) == NULL); + } +} + +void r_libc_string(void) { + run_test(test_memcmp); + run_test(test_memset); + run_test(test_memmove); + run_test(test_strcmp); + run_test(test_strtol); + run_test(test_strspn); + run_test(test_strtok); +} diff --git a/src/cmd/tests/shared/printf.c b/src/cmd/tests/shared/printf.c new file mode 100644 index 0000000..d8df48a --- /dev/null +++ b/src/cmd/tests/shared/printf.c @@ -0,0 +1,55 @@ +#include "../tests.h" +#include <stdio.h> +#include <string.h> + +#pragma GCC diagnostic ignored "-Wformat-truncation" + +static void test_printf(void) { + char buf[64]; + memset(buf, '?', 64); + + /* test proper overflow handling in snprintf */ + test(13 == snprintf(buf, 15, "That's 0x%x", 0x1337)); + test(!memcmp(buf, "That's 0x1337\0??", 16)); + test(17 == snprintf(buf, 15, "%05x %05x %05x", 0, 0, 0)); + test(!memcmp(buf, "00000 00000 00\0?", 16)); + + /* all the other stuff */ + snprintf(buf, sizeof buf, "%010x", 0x1BABE); + test(!strcmp(buf, "000001babe")); + snprintf(buf, sizeof buf, "%10x", 0x1BABE); + test(!strcmp(buf, " 1babe")); + snprintf(buf, sizeof buf, "%10s", "hello"); + test(!strcmp(buf, " hello")); + + snprintf(buf, sizeof buf, "%s%%%s", "ab", "cd"); + test(!strcmp(buf, "ab%cd")); + + snprintf(buf, sizeof buf, "%05u,%05u", 1234, 56789); + test(!strcmp(buf, "01234,56789")); + + snprintf(buf, sizeof buf, "%5d,%5d", 123, 4567); + test(!strcmp(buf, " 123, 4567")); + snprintf(buf, sizeof buf, "%5d,%5d", -123, -4567); + test(!strcmp(buf, " -123,-4567")); + + snprintf(buf, sizeof buf, "%u,%d,%x", 0, 0, 0); + test(!strcmp(buf, "0,0,0")); + + /* precision */ + snprintf(buf, sizeof buf, "%5.2u,%5.2d,%5.2x", 0, 0, 0); + test(!strcmp(buf, " 00, 00, 00")); + snprintf(buf, sizeof buf, "%5.2u,%5.2d,%5.2x", 10, -10, 0x10); + test(!strcmp(buf, " 10, -10, 10")); + snprintf(buf, sizeof buf, "%5.3d", -1); + test(!strcmp(buf, " -001")); + snprintf(buf, sizeof buf, "%.5d", 123); + test(!strcmp(buf, "00123")); + + snprintf(buf, sizeof buf, "%.1s,%.10s,%.*s", "hello", "hello", 3, "hello"); + test(!strcmp(buf, "h,hello,hel")); +} + +void r_s_printf(void) { + run_test(test_printf); +} diff --git a/src/cmd/tests/shared/ringbuf.c b/src/cmd/tests/shared/ringbuf.c new file mode 100644 index 0000000..d2a35a1 --- /dev/null +++ b/src/cmd/tests/shared/ringbuf.c @@ -0,0 +1,49 @@ +#include "../tests.h" +#include <shared/ring.h> +#include <string.h> + +static void test_ringbuf(void) { + char backbuf[16], cmpbuf[16]; + size_t num_read = 0, num_written = 0; + uint8_t c; + + ring_t r = {backbuf, 16, 0, 0}; + + /* aliasing */ + for (size_t i = 0; i < 16; i++) { + test(ring_used(&r) == 0); + test(ring_avail(&r) == 16); + ring_put(&r, "11 bytes...", 11); + test(ring_used(&r) == 11); + test(ring_avail(&r) == 5); + + memset(cmpbuf, 0, sizeof cmpbuf); + test(ring_get(&r, cmpbuf, 16) == 11); + test(memcmp(cmpbuf, "11 bytes...", 11) == 0); + } + + test(ring_used(&r) == 0); + for (size_t i = 0; i < 7; i++) + ring_put1b(&r, num_written++); + test(ring_used(&r) == 7); + for (size_t i = 0; i < 3; i++) { + ring_get(&r, &c, 1); + test(num_read++ == c); + } + test(ring_used(&r) == 4); + + for (size_t j = 0; j < 40; j++) { + for (size_t i = 0; i < 7; i++) + ring_put1b(&r, num_written++ & 0xff); + test(ring_used(&r) == 11); + for (size_t i = 0; i < 7; i++) { + ring_get(&r, &c, 1); + test((num_read++ & 0xff) == c); + } + test(ring_used(&r) == 4); + } +} + +void r_s_ringbuf(void) { + run_test(test_ringbuf); +} diff --git a/src/cmd/tests/stress.c b/src/cmd/tests/stress.c new file mode 100644 index 0000000..1ef018c --- /dev/null +++ b/src/cmd/tests/stress.c @@ -0,0 +1,28 @@ +#include "tests.h" +#include <camellia/flags.h> +#include <camellia/syscalls.h> +#include <stdlib.h> +#include <unistd.h> + +static void run_forked(void (*fn)()) { + if (!fork()) { + fn(); + exit(0); + } else { + /* successful tests must return 0 + * TODO add a better fail msg */ + if (_sys_await() != 0) test_fail(); + } +} + + +static void stress_fork(void) { + for (size_t i = 0; i < 2048; i++) { + if (!fork()) exit(0); + _sys_await(); + } +} + +void stress_all(void) { + run_forked(stress_fork); +} diff --git a/src/cmd/tests/tests.c b/src/cmd/tests/tests.c new file mode 100644 index 0000000..5cba682 --- /dev/null +++ b/src/cmd/tests/tests.c @@ -0,0 +1,68 @@ +#include "tests.h" +#include <camellia/syscalls.h> +#include <unistd.h> + +__attribute__((visibility("hidden"))) +extern char __executable_start[]; + +FILE *fail_trig; + +void run_test(void (*fn)()) { + if (!fork()) { + fn(); + _sys_filicide(); + exit(0); + } else { + /* successful tests must return 0 */ + if (_sys_await() != 0) { + test_failf("%p, base %p", (void*)((void*)fn - (void*)__executable_start), __executable_start); + } + } +} + +int forkpipe(FILE **f, hid_t *h) { + hid_t ends[2]; + if (_sys_pipe(ends, 0) < 0) { + fprintf(stderr, "couldn't create pipe\n"); + exit(1); + } + int ret = fork(); + if (!ret) { + close(ends[0]); + *f = fdopen(ends[1], "w"); + *h = -1; + } else { + close(ends[1]); + *f = NULL; + *h = ends[0]; + } + return ret; +} + +int main(void) { + hid_t reader; + if (!forkpipe(&fail_trig, &reader)) { + r_k_miscsyscall(); + r_k_fs(); + r_k_fdlimit(); + r_k_misc(); + r_k_path(); + r_k_threads(); + r_libc_esemaphore(); + r_libc_setjmp(); + r_libc_string(); + r_s_printf(); + r_s_ringbuf(); + exit(0); + } else { + for (;;) { + char buf[128]; + long ret = _sys_read(reader, buf, sizeof buf, 0); + if (ret < 0) break; + printf("\033[31mFAIL\033[0m "); + fwrite(buf, ret, 1, stdout); + printf("\n"); + } + } + return 0; +} diff --git a/src/cmd/tests/tests.h b/src/cmd/tests/tests.h new file mode 100644 index 0000000..5037e1a --- /dev/null +++ b/src/cmd/tests/tests.h @@ -0,0 +1,37 @@ +#pragma once +#include <camellia/syscalls.h> +#include <stdio.h> +#include <stdlib.h> + +#define TMPFILEPATH "/tmp/.test_internal" + +void run_test(void (*fn)()); + +void r_k_fdlimit(void); +void r_k_fs(void); +void r_k_misc(void); +void r_k_miscsyscall(void); +void r_k_path(void); +void r_k_threads(void); +void r_libc_esemaphore(void); +void r_libc_setjmp(void); +void r_libc_string(void); +void r_s_printf(void); +void r_s_ringbuf(void); + +extern FILE *fail_trig; + +int forkpipe(FILE **f, hid_t *h); + +#define argify(str) str, sizeof(str) - 1 +#define test_fail() do { \ + fprintf(fail_trig, "%s():%u", __func__, __LINE__); \ + fflush(fail_trig); \ + exit(0); \ +} while (0) +#define test_failf(fmt, ...) do { \ + fprintf(fail_trig, "%s():%u " fmt, __func__, __LINE__, __VA_ARGS__); \ + fflush(fail_trig); \ + exit(0); \ +} while (0) +#define test(cond) if (!(cond)) test_fail(); |