From 26dc784103914b9d6ba36e0a96fa4b3af977626f Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Thu, 4 Aug 2022 21:43:38 +0200
Subject: user/tests: split the tests by parts of codebase

---
 src/user/app/tests/kernel/fs.c          |  34 ++++
 src/user/app/tests/kernel/misc.c        |  65 ++++++++
 src/user/app/tests/kernel/miscsyscall.c | 264 ++++++++++++++++++++++++++++++++
 3 files changed, 363 insertions(+)
 create mode 100644 src/user/app/tests/kernel/fs.c
 create mode 100644 src/user/app/tests/kernel/misc.c
 create mode 100644 src/user/app/tests/kernel/miscsyscall.c

(limited to 'src/user/app/tests/kernel')

diff --git a/src/user/app/tests/kernel/fs.c b/src/user/app/tests/kernel/fs.c
new file mode 100644
index 0000000..c8d2eae
--- /dev/null
+++ b/src/user/app/tests/kernel/fs.c
@@ -0,0 +1,34 @@
+#include "../tests.h"
+#include <camellia/syscalls.h>
+
+static void test_unfinished_req(void) {
+	handle_t h;
+	if (_syscall_fork(FORK_NEWFS, &h)) {
+		// TODO make a similar test with all 0s passed to fs_wait
+		struct fs_wait_response res;
+		_syscall_fs_wait(NULL, 0, &res);
+		// TODO second fs_wait
+		exit(0);
+	} else {
+		_syscall_mount(h, "/", 1);
+		int ret = _syscall_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) {
+	handle_t h;
+	if (_syscall_fork(FORK_NEWFS, &h)) {
+		exit(0);
+	} else {
+		_syscall_mount(h, "/", 1);
+		int ret = _syscall_open("/", 1, 0);
+		test(ret < 0);
+	}
+}
+
+void r_k_fs(void) {
+	run_test(test_unfinished_req);
+	run_test(test_orphaned_fs);
+}
diff --git a/src/user/app/tests/kernel/misc.c b/src/user/app/tests/kernel/misc.c
new file mode 100644
index 0000000..6899e18
--- /dev/null
+++ b/src/user/app/tests/kernel/misc.c
@@ -0,0 +1,65 @@
+#include "../tests.h"
+#include <camellia/errno.h>
+#include <camellia/syscalls.h>
+#include <string.h>
+
+static void test_fault_kill(void) {
+	if (!fork()) { /* invalid memory access */
+		asm volatile("movb $69, 0" ::: "memory");
+		// TODO test_fail which works in children
+		printf("this shouldn't happen");
+		exit(-1);
+	}
+	if (!fork()) { /* #GP */
+		asm volatile("hlt" ::: "memory");
+		printf("this shouldn't happen");
+		exit(-1);
+	}
+
+	int await_cnt = 0;
+	while (_syscall_await() != ~0) await_cnt++;
+	test(await_cnt == 2);
+}
+
+static void test_efault(void) {
+	const char *str = "o, 16 characters";
+	char str2[16];
+	char *invalid = (void*)0x1000;
+	handle_t h;
+
+	memcpy(str2, str, 16);
+
+	test((h = _syscall_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE)));
+	test(_syscall_write(h, str, 16, 0, 0) == 16);
+	test(_syscall_write(h, str2, 16, 0, 0) == 16);
+
+	test(_syscall_write(h, invalid, 16, 0, 0) == -EFAULT);
+
+	/* x64 non-canonical pointers */
+	test(_syscall_write(h, (void*)((uintptr_t)str ^ 0x8000000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str ^ 0x1000000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str ^ 0x0100000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str ^ 0x0010000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str ^ 0x0001000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str ^ 0x0000800000000000), 16, 0, 0) == -EFAULT);
+
+	test(_syscall_write(h, (void*)((uintptr_t)str2 ^ 0x8000000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str2 ^ 0x1000000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str2 ^ 0x0100000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str2 ^ 0x0010000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str2 ^ 0x0001000000000000), 16, 0, 0) == -EFAULT);
+	test(_syscall_write(h, (void*)((uintptr_t)str2 ^ 0x0000800000000000), 16, 0, 0) == -EFAULT);
+
+	test(_syscall_write(h, str, 16, 0, 0) == 16);
+	close(h);
+}
+
+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/user/app/tests/kernel/miscsyscall.c b/src/user/app/tests/kernel/miscsyscall.c
new file mode 100644
index 0000000..22b6b33
--- /dev/null
+++ b/src/user/app/tests/kernel/miscsyscall.c
@@ -0,0 +1,264 @@
+#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 = _syscall_await()) != ~0) {
+		test(0 <= ret && ret < 16);
+		counts[ret]++;
+	}
+
+	for (int i = 0; i < 16; i++)
+		test(counts[i] == 1);
+}
+
+static void test_pipe(void) {
+	const char *pipe_msgs[2] = {"hello", "world"};
+	handle_t ends[2];
+	char buf[16];
+
+	/* test regular reads / writes */
+	test(_syscall_pipe(ends, 0) >= 0);
+	if (!fork()) {
+		/* those repeated asserts ensure that you can't read/write to the wrong ends */
+		test(_syscall_read(ends[1], buf, 16, 0) < 0);
+		test(_syscall_write(ends[0], buf, 16, 0, 0) < 0);
+
+		test(5 == _syscall_write(ends[1], pipe_msgs[0], 5, -1, 0));
+
+		test(_syscall_read(ends[1], buf, 16, 0) < 0);
+		test(_syscall_write(ends[0], buf, 16, 0, 0) < 0);
+
+		test(5 == _syscall_write(ends[1], pipe_msgs[1], 5, -1, 0));
+
+		exit(0);
+	} else {
+		test(_syscall_read(ends[1], buf, 16, 0) < 0);
+		test(_syscall_write(ends[0], buf, 16, 0, 0) < 0);
+
+		test(5 == _syscall_read(ends[0], buf, 16, 0));
+		test(!memcmp(buf, pipe_msgs[0], 5));
+
+		test(_syscall_read(ends[1], buf, 16, 0) < 0);
+		test(_syscall_write(ends[0], buf, 16, 0, 0) < 0);
+
+		test(5 == _syscall_read(ends[0], buf, 16, 0));
+		test(!memcmp(buf, pipe_msgs[1], 5));
+
+		_syscall_await();
+	}
+	close(ends[0]);
+	close(ends[1]);
+
+
+	/* writing to pipes with one end closed */
+	test(_syscall_pipe(ends, 0) >= 0);
+	for (int i = 0; i < 16; i++) {
+		if (!fork()) {
+			close(ends[1]);
+			test(_syscall_read(ends[0], buf, 16, 0) < 0);
+			exit(0);
+		}
+	}
+	close(ends[1]);
+	for (int i = 0; i < 16; i++)
+		_syscall_await();
+	close(ends[0]);
+
+	test(_syscall_pipe(ends, 0) >= 0);
+	for (int i = 0; i < 16; i++) {
+		if (!fork()) {
+			close(ends[0]);
+			test(_syscall_write(ends[1], buf, 16, 0, 0) < 0);
+			exit(0);
+		}
+	}
+	close(ends[0]);
+	for (int i = 0; i < 16; i++)
+		_syscall_await();
+	close(ends[1]);
+
+
+	/* queueing */
+	test(_syscall_pipe(ends, 0) >= 0);
+	for (int i = 0; i < 16; i++) {
+		if (!fork()) {
+			test(_syscall_write(ends[1], pipe_msgs[0], 5, -1, 0) == 5);
+			exit(0);
+		}
+	}
+	close(ends[1]);
+	for (int i = 0; i < 16; i++) {
+		test(_syscall_read(ends[0], buf, sizeof buf, 0) == 5);
+		_syscall_await();
+	}
+	test(_syscall_read(ends[0], buf, sizeof buf, 0) < 0);
+	close(ends[0]);
+
+	test(_syscall_pipe(ends, 0) >= 0);
+	for (int i = 0; i < 16; i++) {
+		if (!fork()) {
+			memset(buf, 0, sizeof buf);
+			test(_syscall_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(_syscall_write(ends[1], pipe_msgs[1], 5, -1, 0) == 5);
+		_syscall_await();
+	}
+	test(_syscall_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;
+	_syscall_memflag(page, 4096, MEMFLAG_PRESENT); // allocate page
+	memset(page, 77, 4096); // write to it
+	_syscall_memflag(page, 4096, 0); // free it
+
+	if (!fork()) {
+		memset(page, 11, 4096); // should segfault
+		exit(0);
+	} else {
+		test(_syscall_await() != 0); // test if the process crashed
+	}
+
+	char *pages[4];
+	for (size_t i = 0; i < 4; i++) {
+		pages[i] = _syscall_memflag(NULL, 4096, MEMFLAG_FINDFREE);
+		test(pages[i] == _syscall_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++)
+		_syscall_memflag(pages, 4096, MEMFLAG_PRESENT);
+}
+
+static void test_dup(void) {
+	handle_t pipe[2];
+	handle_t h1, h2;
+	test(_syscall_pipe(pipe, 0) >= 0);
+
+	if (!fork()) {
+		close(pipe[0]);
+
+		h1 = _syscall_dup(pipe[1], -1, 0);
+		test(h1 >= 0);
+		test(h1 != pipe[1]);
+		h2 = _syscall_dup(h1, -1, 0);
+		test(h2 >= 0);
+		test(h2 != pipe[1] && h2 != h1);
+
+		_syscall_write(pipe[1], "og", 2, 0, 0);
+		_syscall_write(h1, "h1", 2, 0, 0);
+		_syscall_write(h2, "h2", 2, 0, 0);
+
+		close(pipe[1]);
+		_syscall_write(h1, "h1", 2, 0, 0);
+		_syscall_write(h2, "h2", 2, 0, 0);
+
+		test(_syscall_dup(h1, pipe[1], 0) == pipe[1]);
+		test(_syscall_dup(h2, pipe[1], 0) == pipe[1]);
+		test(_syscall_dup(h1, pipe[1], 0) == pipe[1]);
+		test(_syscall_dup(h2, pipe[1], 0) == pipe[1]);
+		close(h1);
+		close(h2);
+
+		test(_syscall_dup(pipe[1], h2, 0) == h2);
+		_syscall_write(h2, "h2", 2, 0, 0);
+		close(h2);
+
+		test(_syscall_dup(pipe[1], h1, 0) == h1);
+		_syscall_write(h1, "h1", 2, 0, 0);
+		close(h1);
+
+		exit(0);
+	} else {
+		char buf[16];
+		size_t count = 0;
+		close(pipe[1]);
+		while (_syscall_read(pipe[0], buf, sizeof buf, 0) >= 0)
+			count++;
+		test(count == 7);
+		_syscall_await();
+	}
+
+
+	close(pipe[0]);
+}
+
+static void test_execbuf(void) {
+	// not really a test, TODO
+	// also TODO check returning last syscall value
+	const char str1[] = "test_execbuf: string 1\n";
+	const char str2[] = "test_execbuf: and 2\n";
+	uint64_t buf[] = {
+		EXECBUF_SYSCALL, _SYSCALL_WRITE, 1, (uintptr_t)str1, sizeof(str1) - 1, -1, 0,
+		EXECBUF_SYSCALL, _SYSCALL_WRITE, 1, (uintptr_t)str2, sizeof(str2) - 1, -1, 0,
+		EXECBUF_SYSCALL, _SYSCALL_EXIT, 0, 0, 0, 0, 0,
+	};
+	_syscall_execbuf(buf, sizeof buf);
+	test_fail();
+}
+
+static void test_sleep(void) {
+	// TODO yet another of those fake tests that you have to verify by hand
+	if (!fork()) {
+		if (!fork()) {
+			_syscall_sleep(100);
+			printf("1\n");
+			_syscall_sleep(200);
+			printf("3\n");
+			_syscall_sleep(200);
+			printf("5\n");
+			_syscall_exit(0);
+		}
+		if (!fork()) {
+			printf("0\n");
+			_syscall_sleep(200);
+			printf("2\n");
+			_syscall_sleep(200);
+			printf("4\n");
+			/* get killed while asleep
+			* a peaceful death, i suppose. */
+			for (;;) _syscall_sleep(1000000000);
+		}
+		_syscall_await();
+		_syscall_exit(0);
+	}
+
+	/* this part checks if, after killing an asleep process, other processes can still wake up */
+	_syscall_sleep(600);
+	printf("6\n");
+}
+
+void r_k_miscsyscall(void) {
+	run_test(test_await);
+	run_test(test_pipe);
+	run_test(test_memflag);
+	run_test(test_dup);
+	run_test(test_execbuf);
+	run_test(test_sleep);
+}
-- 
cgit v1.2.3