summaryrefslogtreecommitdiff
path: root/src/irmd/reg/tests
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2024-02-17 10:19:46 +0100
committerSander Vrijders <sander@ouroboros.rocks>2024-02-19 11:49:07 +0100
commit06ee3370998f965b469d1c2859e3e34159c71e20 (patch)
tree93881747a4f1e99f6932231b0cb2358941cb9741 /src/irmd/reg/tests
parent7bb8aed15c7f29de4d9719acf8db7fdf73731af5 (diff)
downloadouroboros-06ee3370998f965b469d1c2859e3e34159c71e20.tar.gz
ouroboros-06ee3370998f965b469d1c2859e3e34159c71e20.zip
irmd: Revise IRMd internals
This is a full revision of the IRMd internal implementation. The registry is now a proper subcomponent managing its own internal lock (a single mutex). Some tests are added for the registry and its data structures. Some macros for tests are added in <ouroboros/test.h>. Flow allocation is now more symmetric between the client side (alloc) and server size (accept). Each will create a flow in pending state (ALLOC_PENDING/ACCEPT_PENDING) that is potentially fulfilled by an IPCP using respond_alloc and respond_accept primitives. Deallocation is split in flow_dealloc (application side) and ipcp_flow_dealloc (IPCP side) to get the flow in DEALLOC_PENDING and DEALLOCATED state. Cleanup of failed flow allocation is now properly handled instead of relying on the sanitizer thread. The new sanitizer only needs to monitor crashed processes. On shutdown, the IRMd will now detect hanging processes and SIGKILL them and clean up their fuse mountpoints if needed. A lot of other things have been cleaned up and shuffled around a bit. Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks> Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/irmd/reg/tests')
-rw-r--r--src/irmd/reg/tests/CMakeLists.txt29
-rw-r--r--src/irmd/reg/tests/flow_test.c294
-rw-r--r--src/irmd/reg/tests/ipcp_test.c89
-rw-r--r--src/irmd/reg/tests/name_test.c283
-rw-r--r--src/irmd/reg/tests/proc_test.c107
-rw-r--r--src/irmd/reg/tests/prog_test.c105
-rw-r--r--src/irmd/reg/tests/reg_test.c1583
7 files changed, 2490 insertions, 0 deletions
diff --git a/src/irmd/reg/tests/CMakeLists.txt b/src/irmd/reg/tests/CMakeLists.txt
new file mode 100644
index 00000000..bc1354ed
--- /dev/null
+++ b/src/irmd/reg/tests/CMakeLists.txt
@@ -0,0 +1,29 @@
+get_filename_component(tmp ".." ABSOLUTE)
+get_filename_component(src_folder "${tmp}" NAME)
+
+create_test_sourcelist(${src_folder}_tests test_suite.c
+ # Add new tests here
+ flow_test.c
+ ipcp_test.c
+ name_test.c
+ proc_test.c
+ prog_test.c
+ reg_test.c
+)
+
+add_executable(${src_folder}_test EXCLUDE_FROM_ALL ${${src_folder}_tests})
+target_link_libraries(${src_folder}_test ouroboros-common)
+
+if (CMAKE_BUILD_TYPE MATCHES "Debug*")
+ add_compile_flags(${src_folder}_test -DCONFIG_OUROBOROS_DEBUG)
+endif ()
+
+add_dependencies(check ${src_folder}_test)
+
+set(tests_to_run ${${src_folder}_tests})
+remove(tests_to_run test_suite.c)
+
+foreach(test ${tests_to_run})
+ get_filename_component(test_name ${test} NAME_WE)
+ add_test(irmd/reg/${test_name} ${C_TEST_PATH}/${src_folder}_test ${test_name})
+endforeach(test)
diff --git a/src/irmd/reg/tests/flow_test.c b/src/irmd/reg/tests/flow_test.c
new file mode 100644
index 00000000..efdf21d2
--- /dev/null
+++ b/src/irmd/reg/tests/flow_test.c
@@ -0,0 +1,294 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * The IPC Resource Manager - Registry - Flows - Unit Tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "../flow.c"
+
+#include <ouroboros/test.h>
+
+#include <string.h>
+
+#define TEST_DATA "testpiggybackdata"
+
+static int test_reg_flow_create(void)
+{
+ struct reg_flow * f;
+
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 1,
+ .qs = qos_raw,
+ .state = FLOW_INIT
+ };
+
+ TEST_START();
+
+ f = reg_flow_create(&info);
+ if (f == NULL) {
+ printf("Failed to create flow.\n");
+ goto fail;
+ }
+
+ reg_flow_destroy(f);
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_flow_create_no_id(void) {
+ struct flow_info info = {
+ .id = 0,
+ .n_pid = 1,
+ .qs = qos_raw,
+ .state = FLOW_INIT
+ };
+
+ reg_flow_create(&info); /* assert fail */
+
+ return 0;
+}
+
+static int test_reg_flow_create_no_pid(void) {
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 0,
+ .qs = qos_raw,
+ .state = FLOW_INIT
+ };
+
+ reg_flow_create(&info); /* assert fail */
+
+ return 0;
+}
+
+static int test_reg_flow_create_has_n_1_pid(void) {
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 0,
+ .n_1_pid = 1,
+ .qs = qos_raw,
+ .state = FLOW_INIT
+ };
+
+ reg_flow_create(&info); /* assert fail */
+
+ return 0;
+}
+
+static int test_reg_flow_create_wrong_state(void) {
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 0,
+ .n_1_pid = 1,
+ .qs = qos_raw,
+ .state = FLOW_ALLOC_PENDING
+ };
+
+ reg_flow_create(&info); /* assert fail */
+
+ return 0;
+}
+
+static int test_reg_flow_create_has_mpl(void) {
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 1,
+ .n_1_pid = 0,
+ .mpl = 10,
+ .qs = qos_raw,
+ .state = FLOW_ALLOC_PENDING
+ };
+
+ reg_flow_create(&info); /* assert fail */
+
+ return 0;
+}
+
+static int test_reg_flow_update(void)
+{
+ struct reg_flow * f;
+
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 1,
+ .qs = qos_raw,
+ .state = FLOW_INIT
+ };
+
+ struct flow_info upd = {
+ .id = 1,
+ .n_pid = 1,
+ .qs = qos_data,
+ .state = FLOW_DEALLOCATED
+ };
+
+ TEST_START();
+
+ f = reg_flow_create(&info);
+ if (f == NULL) {
+ printf("Failed to create flow.\n");
+ goto fail;
+ }
+
+ reg_flow_update(f, &upd);
+
+ if (memcmp(&f->info, &upd, sizeof(upd)) != 0) {
+ printf("Flow info not updated.\n");
+ goto fail;
+ }
+
+ reg_flow_destroy(f);
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_flow_update_wrong_id(void)
+{
+ struct reg_flow * f;
+
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 1,
+ .qs = qos_raw,
+ .state = FLOW_INIT
+ };
+
+ struct flow_info upd = {
+ .id = 2,
+ .n_pid = 1,
+ .qs = qos_data,
+ .state = FLOW_DEALLOCATED
+ };
+
+ TEST_START();
+
+ f = reg_flow_create(&info);
+ if (f == NULL) {
+ printf("Failed to create flow.\n");
+ goto fail;
+ }
+
+ reg_flow_update(f, &upd); /* assert fail */
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_flow_assert_fails(void)
+{
+ int ret = 0;
+
+ ret |= test_assert_fail(test_reg_flow_create_no_id);
+
+ ret |= test_assert_fail(test_reg_flow_create_no_pid);
+
+ ret |= test_assert_fail(test_reg_flow_create_has_n_1_pid);
+
+ ret |= test_assert_fail(test_reg_flow_create_wrong_state);
+
+ ret |= test_assert_fail(test_reg_flow_create_has_mpl);
+
+ ret |= test_assert_fail(test_reg_flow_update_wrong_id);
+
+ return ret;
+}
+
+static int test_flow_data(void)
+{
+ struct reg_flow * f;
+
+ struct flow_info info = {
+ .id = 1,
+ .n_pid = 1,
+ .qs = qos_raw,
+ .state = FLOW_INIT
+ };
+
+ char * data;
+ buffer_t buf;
+ buffer_t rcv = {NULL, 0};
+
+ TEST_START();
+
+ data = strdup(TEST_DATA);
+ if (data == NULL) {
+ printf("Failed to strdup data.\n");
+ goto fail;
+ }
+
+ buf.data = (uint8_t *) data;
+ buf.len = strlen(data);
+
+ f = reg_flow_create(&info);
+ if (f == NULL) {
+ printf("Failed to create flow.\n");
+ goto fail;
+ }
+
+ reg_flow_set_data(f, &buf);
+
+ reg_flow_get_data(f, &rcv);
+
+ freebuf(buf);
+ clrbuf(rcv);
+
+ reg_flow_destroy(f);
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ free(data);
+ TEST_FAIL();
+ return -1;
+}
+
+int flow_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_reg_flow_create();
+
+ ret |= test_reg_flow_update();
+
+ ret |= test_reg_flow_assert_fails();
+
+ ret |= test_flow_data();
+
+ return ret;
+} \ No newline at end of file
diff --git a/src/irmd/reg/tests/ipcp_test.c b/src/irmd/reg/tests/ipcp_test.c
new file mode 100644
index 00000000..4e0a764b
--- /dev/null
+++ b/src/irmd/reg/tests/ipcp_test.c
@@ -0,0 +1,89 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * The IPC Resource Manager - Registry - IPCPs - Unit Tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include <ouroboros/test.h>
+
+#include "../ipcp.c"
+
+#define TEST_PID 65535
+
+static int test_reg_ipcp_create(void)
+{
+ struct reg_ipcp * ipcp;
+ struct ipcp_info info = {
+ .pid = TEST_PID,
+ .state = IPCP_BOOT
+ };
+ struct layer_info layer = {
+ .name = "testlayer",
+ .dir_hash_algo = DIR_HASH_SHA3_224
+ };
+
+ TEST_START();
+
+ ipcp = reg_ipcp_create(&info);
+ if (ipcp == NULL) {
+ printf("Failed to create ipcp.\n");
+ goto fail;
+ }
+
+ if (strcmp(ipcp->layer.name, "Not enrolled.") != 0) {
+ printf("Layer name was not set.\n");
+ goto fail;
+ }
+
+ ipcp->info.state = IPCP_OPERATIONAL;
+
+ reg_ipcp_set_layer(ipcp, &layer);
+
+ if (strcmp(ipcp->layer.name, layer.name) != 0) {
+ printf("Layer name was not set.\n");
+ goto fail;
+ }
+
+ if (ipcp->info.state != IPCP_OPERATIONAL) {
+ printf("IPCP state was not set.\n");
+ goto fail;
+ }
+
+ reg_ipcp_destroy(ipcp);
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ TEST_FAIL();
+ return -1;
+}
+
+int ipcp_test(int argc,
+ char ** argv)
+{
+ int res = 0;
+
+ (void) argc;
+ (void) argv;
+
+ res |= test_reg_ipcp_create();
+
+ return res;
+} \ No newline at end of file
diff --git a/src/irmd/reg/tests/name_test.c b/src/irmd/reg/tests/name_test.c
new file mode 100644
index 00000000..e2ceb0fc
--- /dev/null
+++ b/src/irmd/reg/tests/name_test.c
@@ -0,0 +1,283 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * The IPC Resource Manager - Registry - Names - Unit Tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "../name.c"
+
+#define TEST_PID 65534
+#define TEST_PROG "/usr/bin/testprog"
+#define TEST_NAME "testservicename"
+
+static int test_reg_name_create(void)
+{
+ struct reg_name * n;
+ struct name_info info = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR,
+ };
+
+ n = reg_name_create(&info);
+ if (n == NULL) {
+ printf("Failed to create name %s.\n", info.name);
+ goto fail;
+ }
+
+ reg_name_destroy(n);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+static int test_reg_name_add_proc(void)
+{
+ struct reg_name * n;
+ struct name_info info = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR,
+ };
+
+ n = reg_name_create(&info);
+ if (n == NULL) {
+ printf("Failed to create name %s.\n", info.name);
+ goto fail;
+ }
+
+ if (reg_name_add_proc(n, TEST_PID) < 0) {
+ printf("Failed to add proc.\n");
+ goto fail;
+ }
+
+ if (n->n_procs != 1) {
+ printf("n_procs not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_name_has_proc(n, TEST_PID)) {
+ printf("Proc not found.\n");
+ goto fail;
+ }
+
+ reg_name_del_proc(n, TEST_PID);
+
+ if (n->n_procs != 0) {
+ printf("n_procs not updated.\n");
+ goto fail;
+ }
+
+ reg_name_destroy(n);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+static int test_reg_name_add_prog(void)
+{
+ struct reg_name * n;
+ struct name_info info = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR,
+ };
+
+ char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL};
+
+ n = reg_name_create(&info);
+ if (n == NULL) {
+ printf("Failed to create name %s.\n", info.name);
+ goto fail;
+ }
+
+ if (reg_name_add_prog(n, exec) < 0) {
+ printf("Failed to add prog.\n");
+ goto fail;
+ }
+
+ if (n->n_progs != 1) {
+ printf("n_progs not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_name_has_prog(n, TEST_PROG)) {
+ printf("Prog not found.\n");
+ goto fail;
+ }
+
+ reg_name_del_prog(n, TEST_PROG);
+
+ if (n->n_progs != 0) {
+ printf("n_progs not updated.\n");
+ goto fail;
+ }
+
+ reg_name_destroy(n);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+static int test_reg_name_add_active(enum pol_balance lb)
+{
+ struct reg_name * n;
+ pid_t pid;
+ struct name_info info = {
+ .name = TEST_NAME,
+ .pol_lb = lb,
+ };
+
+ n = reg_name_create(&info);
+ if (n == NULL) {
+ printf("Failed to create name %s.\n", info.name);
+ goto fail;
+ }
+
+ if (reg_name_get_active(n) != -1) {
+ printf("Got active from empty actives.\n");
+ goto fail;
+ }
+
+ if (reg_name_add_proc(n, TEST_PID) < 0) {
+ printf("Failed to add proc 0.\n");
+ goto fail;
+ }
+
+ if (reg_name_add_proc(n, TEST_PID + 1) < 0) {
+ printf("Failed to add proc 1.\n");
+ goto fail;
+ }
+
+ if (reg_name_add_proc(n, TEST_PID + 2) < 0) {
+ printf("Failed to add proc 2.\n");
+ goto fail;
+ }
+
+ if (reg_name_add_active(n, TEST_PID) < 0) {
+ printf("Failed to add active.\n");
+ goto fail;
+ }
+
+ if (n->n_active != 1) {
+ printf("n_active not updated.\n");
+ goto fail;
+ }
+
+ if (reg_name_get_active(n) != TEST_PID) {
+ printf("Failed to get active.\n");
+ goto fail;
+ }
+
+ if (reg_name_get_active(n) != TEST_PID) {
+ printf("Failed to get active.\n");
+ goto fail;
+ }
+
+ if (reg_name_add_active(n, TEST_PID + 1) < 0) {
+ printf("Failed to add active 3.\n");
+ goto fail;
+ }
+
+ if (reg_name_add_active(n, TEST_PID + 1) < 0) {
+ printf("Failed to add active 3.\n");
+ goto fail;
+ }
+
+
+ if (reg_name_add_active(n, TEST_PID + 2) < 0) {
+ printf("Failed to add active 4.\n");
+ goto fail;
+ }
+
+ if (n->n_procs != 3) {
+ printf("n_procs not updated.\n");
+ goto fail;
+ }
+
+ if (n->n_active != 4) {
+ printf("n_active not updated.\n");
+ goto fail;
+ }
+
+ pid = info.pol_lb == LB_RR ? TEST_PID : TEST_PID + 2;
+
+ if (reg_name_get_active(n) != pid) {
+ printf("Got wrong active pid 1.\n");
+ goto fail;
+ }
+
+ reg_name_del_active(n, pid);
+
+ if (reg_name_add_active(n, pid) < 0) {
+ printf("Failed to add active 4.\n");
+ goto fail;
+ }
+
+ pid = info.pol_lb == LB_RR ? TEST_PID + 1 : TEST_PID + 2;
+
+ if (reg_name_get_active(n) != pid) {
+ printf("Got wrong active pid 2 %d.\n", pid);
+ goto fail;
+ }
+
+ reg_name_del_proc(n, TEST_PID + 2);
+
+ reg_name_del_proc(n, TEST_PID + 1);
+
+ reg_name_del_proc(n, TEST_PID);
+
+ if (n->n_procs != 0) {
+ printf("n_procs not updated.\n");
+ goto fail;
+ }
+
+ if (n->n_active != 0) {
+ printf("n_active not updated.\n");
+ goto fail;
+ }
+
+ reg_name_destroy(n);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+
+int name_test(int argc,
+ char ** argv)
+{
+ int res = 0;
+
+ (void) argc;
+ (void) argv;
+
+ res |= test_reg_name_create();
+
+ res |= test_reg_name_add_proc();
+
+ res |= test_reg_name_add_prog();
+
+ res |= test_reg_name_add_active(LB_RR);
+
+ res |= test_reg_name_add_active(LB_SPILL);
+
+ return res;
+} \ No newline at end of file
diff --git a/src/irmd/reg/tests/proc_test.c b/src/irmd/reg/tests/proc_test.c
new file mode 100644
index 00000000..5c9dd865
--- /dev/null
+++ b/src/irmd/reg/tests/proc_test.c
@@ -0,0 +1,107 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * The IPC Resource Manager - Registry - Processes - Unit Tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "../proc.c"
+
+#define TEST_PID 65534
+#define TEST_PROG "usr/bin/testprog"
+
+static int test_reg_proc_create(void)
+{
+ struct reg_proc * proc;
+ struct proc_info info = {
+ .pid = TEST_PID,
+ .prog = TEST_PROG
+ };
+
+ proc = reg_proc_create(&info);
+ if (proc == NULL) {
+ printf("Failed to create proc.\n");
+ goto fail;
+ }
+
+ reg_proc_destroy(proc);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+static int test_reg_proc_add_name(void)
+{
+ struct reg_proc * proc;
+ struct proc_info info = {
+ .pid = TEST_PID,
+ .prog = TEST_PROG
+ };
+
+ char * name = "testname";
+
+ proc = reg_proc_create(&info);
+ if (proc == NULL) {
+ printf("Failed to create proc.\n");
+ goto fail;
+ }
+
+ if (reg_proc_add_name(proc, name) < 0) {
+ printf("Failed to add name.");
+ goto fail;
+ }
+
+ if (proc->n_names != 1) {
+ printf("n_names not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_proc_has_name(proc, name)) {
+ printf("Name not found.\n");
+ goto fail;
+ }
+
+ reg_proc_del_name(proc, name);
+
+ if (proc->n_names != 0) {
+ printf("n_names not updated.\n");
+ goto fail;
+ }
+
+ reg_proc_destroy(proc);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+int proc_test(int argc,
+ char ** argv)
+{
+ int res = 0;
+
+ (void) argc;
+ (void) argv;
+
+ res |= test_reg_proc_create();
+
+ res |= test_reg_proc_add_name();
+
+ return res;
+}
diff --git a/src/irmd/reg/tests/prog_test.c b/src/irmd/reg/tests/prog_test.c
new file mode 100644
index 00000000..2565204b
--- /dev/null
+++ b/src/irmd/reg/tests/prog_test.c
@@ -0,0 +1,105 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * The IPC Resource Manager - Registry - Programs - Unit Tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#include "../prog.c"
+
+#define TEST_PROG "usr/bin/testprog"
+
+
+static int test_reg_prog_create(void)
+{
+ struct reg_prog * prog;
+ struct prog_info info = {
+ .name = TEST_PROG
+ };
+
+ prog = reg_prog_create(&info);
+ if (prog == NULL) {
+ printf("Failed to create prog.\n");
+ goto fail;
+ }
+
+ reg_prog_destroy(prog);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+static int test_reg_prog_add_name(void)
+{
+ struct reg_prog * prog;
+ struct prog_info info = {
+ .name = TEST_PROG
+ };
+
+ char * name = "testname";
+
+ prog = reg_prog_create(&info);
+ if (prog == NULL) {
+ printf("Failed to create prog.\n");
+ goto fail;
+ }
+
+ if (reg_prog_add_name(prog, name) < 0) {
+ printf("Failed to add name.");
+ goto fail;
+ }
+
+ if (prog->n_names != 1) {
+ printf("n_names not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_prog_has_name(prog, name)) {
+ printf("Name not found.\n");
+ goto fail;
+ }
+
+ reg_prog_del_name(prog, name);
+
+ if (prog->n_names != 0) {
+ printf("n_names not updated.\n");
+ goto fail;
+ }
+
+ reg_prog_destroy(prog);
+
+ return 0;
+ fail:
+ return -1;
+}
+
+int prog_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_reg_prog_create();
+
+ ret |= test_reg_prog_add_name();
+
+ return ret;
+} \ No newline at end of file
diff --git a/src/irmd/reg/tests/reg_test.c b/src/irmd/reg/tests/reg_test.c
new file mode 100644
index 00000000..35290caf
--- /dev/null
+++ b/src/irmd/reg/tests/reg_test.c
@@ -0,0 +1,1583 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * The IPC Resource Manager - Registry - Unit Tests
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+
+#include "../reg.c"
+
+#include <ouroboros/test.h>
+
+#define TEST_PID 3666
+#define TEST_N_1_PID 3999
+#define TEST_FAKE_ID 9128349
+#define TEST_MPL 5
+#define TEST_PROG "reg_test" /* own binary for binary check */
+#define TEST_IPCP "testipcp"
+#define TEST_NAME "testname"
+#define TEST_DATA "testpbufdata"
+#define TEST_DATA2 "testpbufdata2"
+#define TEST_LAYER "testlayer"
+#define REG_TEST_FAIL() \
+ do { TEST_FAIL(); memset(&reg, 0, sizeof(reg)); } while(0)
+
+static int test_reg_init(void)
+{
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_create_flow(void)
+{
+ struct flow_info info = {
+ .n_pid = TEST_PID,
+ .qs = qos_raw,
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_flow(&info) < 0) {
+ printf("Failed to create flow.\n");
+ goto fail;
+ }
+
+ if (info.id == 0) {
+ printf("Failed to update id.'n");
+ goto fail;
+ }
+
+ if (reg.n_flows != 1) {
+ printf("n_flows was not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_has_flow(info.id)) {
+ printf("Failed to find flow.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_flow(info.id) < 0) {
+ printf("Failed to destroy flow.\n");
+ goto fail;
+ }
+
+ if (reg.n_flows != 0) {
+ printf("n_flows was not updated.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_allocate_flow_timeout(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_MS(1);
+ buffer_t pbuf;
+ buffer_t rbuf = {NULL, 0};
+
+ struct flow_info info = {
+ .n_pid = TEST_PID,
+ .qs = qos_raw
+ };
+
+ TEST_START();
+
+ pbuf.data = (uint8_t *) strdup(TEST_DATA);;
+ if (pbuf.data == NULL) {
+ printf("Failed to strdup data.\n");
+ goto fail;
+ }
+
+ pbuf.len = strlen((char *) pbuf.data) + 1;
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+
+ ts_add(&abstime, &timeo, &abstime);
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_flow(&info) < 0) {
+ printf("Failed to add flow.\n");
+ goto fail;
+ }
+
+ if (reg_prepare_flow_accept(&info, &pbuf) < 0) {
+ printf("Failed to prepare flow for accept.\n");
+ goto fail;
+ }
+
+ if (reg_wait_flow_accepted(&info, &rbuf, &abstime) != -ETIMEDOUT) {
+ printf("Wait allocated did not timeout.\n");
+ goto fail;
+ }
+
+ if (info.state != FLOW_DEALLOCATED) {
+ printf("Flow did not timeout in deallocated state.\n");
+ goto fail;
+ }
+
+ if (pbuf.data == NULL) {
+ printf("Flow data was updated on timeout.");
+ goto fail;
+ }
+
+ freebuf(pbuf);
+ reg_destroy_flow(info.id);
+
+ if (reg.n_flows != 0) {
+ printf("Flow did not destroy.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static void * test_flow_respond_alloc(void * o)
+{
+ struct flow_info * info = (struct flow_info *) o;
+ buffer_t pbuf = {NULL, 0};
+
+ if (info->state == FLOW_ALLOCATED) {
+ pbuf.data = (uint8_t *) strdup(TEST_DATA2);
+ if (pbuf.data == NULL) {
+ printf("Failed to strdup data2.\n");
+ goto fail;
+ }
+ pbuf.len = strlen((char *) pbuf.data) + 1;
+ }
+
+ reg_respond_alloc(info, &pbuf);
+
+ return (void *) 0;
+ fail:
+ return (void *) -1;
+}
+
+static void * test_flow_respond_accept(void * o)
+{
+ struct flow_info * info = (struct flow_info *) o;
+ buffer_t pbuf;
+
+ pbuf.data = (uint8_t *) strdup(TEST_DATA2);
+ if (pbuf.data == NULL) {
+ printf("Failed to strdup data2.\n");
+ goto fail;
+ }
+ pbuf.len = strlen((char *) pbuf.data) + 1;
+
+ reg_respond_accept(info, &pbuf);
+
+ if (info->qs.cypher_s == 0) {
+ freebuf(pbuf);
+ } else if (strcmp((char *) pbuf.data, TEST_DATA) != 0) {
+ printf("Data was not passed correctly.\n");
+ goto fail;
+ }
+
+ return (void *) 0;
+ fail:
+ return (void *) -1;
+}
+
+static int test_reg_accept_flow_success(void)
+{
+ pthread_t thr;
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+ buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)};
+ buffer_t rbuf = {NULL, 0};
+
+ struct flow_info info = {
+ .n_pid = TEST_PID,
+ .qs = qos_raw
+ };
+
+ struct flow_info n_1_info = {
+ .n_1_pid = TEST_N_1_PID,
+ .qs = qos_data_crypt,
+ .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */
+ };
+
+ TEST_START();
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+
+ ts_add(&abstime, &timeo, &abstime);
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_flow(&info) < 0) {
+ printf("Failed to add flow.\n");
+ goto fail;
+ }
+
+ if (reg_prepare_flow_accept(&info, &pbuf) < 0) {
+ printf("Failed to prepare flow for accept.\n");
+ goto fail;
+ }
+
+ n_1_info.id = info.id;
+ n_1_info.mpl = 1;
+
+ pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info);
+
+ if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) {
+ printf("Flow allocation failed.\n");
+ goto fail;
+ }
+
+ pthread_join(thr, NULL);
+
+ if (info.state != FLOW_ALLOCATED) {
+ printf("Flow succeeded but not in allocated state.\n");
+ goto fail;
+ }
+
+ if (rbuf.data == NULL) {
+ printf("rbuf data not returned.\n");
+ goto fail;
+ }
+
+ if (strcmp((char *) rbuf.data, TEST_DATA2) != 0) {
+ printf("Data2 was not passed correctly.\n");
+ goto fail;
+ }
+
+ freebuf(rbuf);
+
+ reg_dealloc_flow(&info);
+
+ if (info.state != FLOW_DEALLOC_PENDING) {
+ printf("Flow dealloc requested but not in pending state.\n");
+ goto fail;
+ }
+
+ reg_dealloc_flow_resp(&info);
+
+ if (info.state != FLOW_DEALLOCATED) {
+ printf("Flow deallocated but not in deallocated state.\n");
+ goto fail;
+ }
+
+ reg_destroy_flow(n_1_info.id);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_accept_flow_success_no_crypt(void)
+{
+ pthread_t thr;
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+ buffer_t pbuf = {(uint8_t *) TEST_DATA, strlen(TEST_DATA)};
+ buffer_t rbuf = {NULL, 0};
+
+ struct flow_info info = {
+ .n_pid = TEST_PID,
+ .qs = qos_raw
+ };
+
+ struct flow_info n_1_info = {
+ .n_1_pid = TEST_N_1_PID,
+ .qs = qos_data,
+ .state = FLOW_ALLOCATED /* RESPONSE SUCCESS */
+ };
+
+ TEST_START();
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+
+ ts_add(&abstime, &timeo, &abstime);
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_flow(&info) < 0) {
+ printf("Failed to add flow.\n");
+ goto fail;
+ }
+
+ if (reg_prepare_flow_accept(&info, &pbuf) < 0) {
+ printf("Failed to prepare flow for accept.\n");
+ goto fail;
+ }
+
+ n_1_info.id = info.id;
+ n_1_info.mpl = 1;
+
+ pthread_create(&thr, NULL, test_flow_respond_accept, &n_1_info);
+
+ if (reg_wait_flow_accepted(&info, &rbuf, &abstime) < 0 ) {
+ printf("Flow allocation failed.\n");
+ goto fail;
+ }
+
+ pthread_join(thr, NULL);
+
+ if (info.state != FLOW_ALLOCATED) {
+ printf("Flow succeeded but not in allocated state.\n");
+ goto fail;
+ }
+
+ if (rbuf.data == NULL) {
+ printf("rbuf data was not returned.\n");
+ goto fail;
+ }
+
+ if (strcmp((char *) rbuf.data, TEST_DATA) != 0) {
+ printf("Data was updated.\n");
+ goto fail;
+ }
+
+ n_1_info.state = FLOW_DEALLOCATED;
+
+ reg_dealloc_flow(&info);
+
+ if (info.state != FLOW_DEALLOC_PENDING) {
+ printf("Flow dealloc requested but not in pending state.\n");
+ goto fail;
+ }
+
+ reg_dealloc_flow_resp(&info);
+
+ if (info.state != FLOW_DEALLOCATED) {
+ printf("Flow deallocated but not in deallocated state.\n");
+ goto fail;
+ }
+
+ reg_destroy_flow(n_1_info.id);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+
+static int test_reg_allocate_flow_fail(void)
+{
+ buffer_t buf = {NULL, 0};
+ pthread_t thr;
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+
+ struct flow_info info = {
+ .n_pid = TEST_PID,
+ .qs = qos_raw
+ };
+
+ struct flow_info n_1_info = {
+ .n_1_pid = TEST_N_1_PID,
+ .qs = qos_data,
+ .state = FLOW_DEALLOCATED /* RESPONSE FAIL */
+ };
+
+ TEST_START();
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+
+ ts_add(&abstime, &timeo, &abstime);
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_flow(&info) < 0) {
+ printf("Failed to add flow.\n");
+ goto fail;
+ }
+
+ info.n_1_pid = TEST_N_1_PID;
+
+ if (reg_prepare_flow_alloc(&info) < 0) {
+ printf("Failed to prepare flow for alloc.\n");
+ goto fail;
+ }
+
+ n_1_info.id = info.id;
+
+ pthread_create(&thr, NULL, test_flow_respond_alloc, &n_1_info);
+
+ if (reg_wait_flow_allocated(&info, &buf, &abstime) == 0 ) {
+ printf("Flow allocation succeeded.\n");
+ goto fail;
+ }
+
+ pthread_join(thr, NULL);
+
+ if (info.state != FLOW_DEALLOCATED) {
+ printf("Flow failed but not in deallocated state.\n");
+ goto fail;
+ }
+
+ reg_destroy_flow(n_1_info.id);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_flow(void) {
+ int ret = 0;
+
+ ret |= test_reg_create_flow();
+
+ ret |= test_reg_allocate_flow_timeout();
+
+ ret |= test_reg_accept_flow_success();
+
+ ret |= test_reg_accept_flow_success_no_crypt();
+
+ ret |= test_reg_allocate_flow_fail();
+
+ return ret;
+}
+
+static int test_reg_create_ipcp(void)
+{
+ struct ipcp_info info = {
+ .name = TEST_IPCP,
+ .pid = TEST_PID,
+ .state = IPCP_BOOT /* set by spawn_ipcp */
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_ipcp(&info) < 0) {
+ printf("Failed to create ipcp.\n");
+ goto fail;
+ }
+
+ if (reg.n_ipcps != 1) {
+ printf("n_ipcps was not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_has_ipcp(info.pid)) {
+ printf("Failed to find ipcp.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_ipcp(info.pid) < 0) {
+ printf("Failed to destroy ipcp.\n");
+ goto fail;
+ }
+
+ if (reg.n_ipcps != 0) {
+ printf("n_ipcps was not updated.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_set_layer(void)
+{
+ struct reg_ipcp * ipcp;
+ struct ipcp_info info = {
+ .name = TEST_IPCP,
+ .pid = TEST_PID,
+ .state = IPCP_BOOT /* set by spawn_ipcp */
+ };
+ struct layer_info layer = {
+ .name = TEST_LAYER,
+ };
+
+ struct ipcp_info get_info = {
+ .pid = TEST_PID
+ };
+ struct layer_info get_layer;
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_ipcp(&info) < 0) {
+ printf("Failed to create ipcp.\n");
+ goto fail;
+ }
+
+ ipcp = __reg_get_ipcp(info.pid);
+ ipcp->info.state = IPCP_OPERATIONAL;
+ info.state = IPCP_ENROLLED;
+
+ reg_set_layer_for_ipcp(&info, &layer);
+
+ reg_get_ipcp(&get_info, &get_layer);
+
+ if (memcmp(&get_info, &info, sizeof(ipcp)) != 0) {
+ printf("Failed to set ipcp info.\n");
+ goto fail;
+ }
+
+ if (memcmp(&get_layer, &layer, sizeof(layer)) != 0) {
+ printf("Failed to set layer info.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_ipcp(info.pid) < 0) {
+ printf("Failed to destroy ipcp.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_ipcp(void)
+{
+ int ret = 0;
+
+ ret |= test_reg_create_ipcp();
+
+ ret |= test_set_layer();
+
+ return ret;
+}
+
+static int test_reg_create_name(void)
+{
+ struct name_info info = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_name(&info) < 0) {
+ printf("Failed to create name.\n");
+ goto fail;
+ }
+
+ if (reg.n_names != 1) {
+ printf("n_names was not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_has_name(info.name)) {
+ printf("Failed to find name.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_name(info.name) < 0) {
+ printf("Failed to destroy name.\n");
+ goto fail;
+ }
+
+ if (reg.n_names != 0) {
+ printf("n_names was not updated.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_name(void)
+{
+ int ret = 0;
+
+ ret |= test_reg_create_name();
+
+ return ret;
+}
+
+static int test_reg_create_proc(void)
+{
+ struct proc_info info = {
+ .pid = TEST_PID,
+ .prog = TEST_PROG
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_proc(&info) < 0) {
+ printf("Failed to create process.\n");
+ goto fail;
+ }
+
+ if (reg.n_procs != 1) {
+ printf("n_procs was not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_has_proc(info.pid)) {
+ printf("Failed to find process.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_proc(info.pid) < 0) {
+ printf("Failed to destroy process.\n");
+ goto fail;
+ }
+
+ if (reg.n_procs != 0) {
+ printf("n_procs was not updated.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_proc(void)
+{
+ int ret = 0;
+
+ ret |= test_reg_create_proc();
+
+ return ret;
+}
+
+static int test_reg_spawned(void)
+{
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_spawned(TEST_PID) < 0) {
+ printf("Failed to create process.\n");
+ goto fail;
+ }
+
+ if (reg.n_spawned != 1) {
+ printf("n_spawned was not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_has_spawned(TEST_PID)) {
+ printf("Failed to find spawned.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_spawned(TEST_PID) < 0) {
+ printf("Failed to destroy spawned.\n");
+ goto fail;
+ }
+
+ if (reg.n_spawned != 0) {
+ printf("n_spawned was not updated.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_create_prog(void)
+{
+ struct prog_info info = {
+ .name = TEST_PROG
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_prog(&info) < 0) {
+ printf("Failed to create program.\n");
+ goto fail;
+ }
+
+ if (reg.n_progs != 1) {
+ printf("n_progs was not updated.\n");
+ goto fail;
+ }
+
+ if (!reg_has_prog(info.name)) {
+ printf("Failed to find program.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_prog(info.name) < 0) {
+ printf("Failed to destroy program.\n");
+ goto fail;
+ }
+
+ if (reg.n_progs != 0) {
+ printf("n_progs was not updated.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_reg_prog(void)
+{
+ int ret = 0;
+
+ ret |= test_reg_create_prog();
+
+ return ret;
+}
+
+static int test_bind_proc(void)
+{
+ struct proc_info pinfo = {
+ .pid = TEST_PID,
+ .prog = TEST_PROG
+ };
+
+ struct name_info ninfo = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR
+ };
+
+ TEST_START();
+
+ if (reg_init()) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_name(&ninfo) < 0) {
+ printf("Failed to create name.\n");
+ goto fail;
+ }
+
+ if (reg_create_proc(&pinfo) < 0) {
+ printf("Failed to create proc.\n");
+ goto fail;
+ }
+
+ if (reg_bind_proc(TEST_NAME, TEST_PID) < 0) {
+ printf("Failed to bind proc.\n");
+ goto fail;
+ }
+
+ if (reg_unbind_proc(TEST_NAME, TEST_PID) < 0) {
+ printf("Failed to unbind proc.\n");
+ goto fail;
+ }
+
+ reg_destroy_proc(TEST_PID);
+
+ if (reg_name_has_proc( __reg_get_name(TEST_NAME), TEST_PID)) {
+ printf("Proc still in name after destroy.\n");
+ goto fail;
+ }
+
+ reg_destroy_name(TEST_NAME);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_bind_prog(void)
+{
+ struct prog_info pinfo = {
+ .name = TEST_PROG
+ };
+
+ struct name_info ninfo = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR
+ };
+
+ char * exec[] = { TEST_PROG, "--argswitch", "argvalue", NULL};
+
+ TEST_START();
+
+ if (reg_init()) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_name(&ninfo) < 0) {
+ printf("Failed to create name.\n");
+ goto fail;
+ }
+
+ if (reg_create_prog(&pinfo) < 0) {
+ printf("Failed to create prog.\n");
+ goto fail;
+ }
+
+ if (reg_bind_prog(TEST_NAME, exec, BIND_AUTO) < 0) {
+ printf("Failed to bind prog.\n");
+ goto fail;
+ }
+
+ if (!reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) {
+ printf("Prog not found in name.\n");
+ goto fail;
+ }
+
+ if (!reg_prog_has_name( __reg_get_prog(TEST_PROG), TEST_NAME)) {
+ printf("Name not found in prog.\n");
+ goto fail;
+ }
+
+ if (reg_unbind_prog(TEST_NAME, TEST_PROG) < 0) {
+ printf("Failed to unbind prog.\n");
+ goto fail;
+ }
+
+ if (reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) {
+ printf("Prog still in name after unbind.\n");
+ goto fail;
+ }
+
+ if (reg_prog_has_name( __reg_get_prog(TEST_PROG), TEST_NAME)) {
+ printf("Name still in prog after unbind.\n");
+ goto fail;
+ }
+
+ if (reg_bind_prog(TEST_NAME, exec, 0) < 0) {
+ printf("Failed to bind prog.\n");
+ goto fail;
+ }
+
+ if (reg_name_has_prog( __reg_get_name(TEST_NAME), TEST_PROG)) {
+ printf("Non-auto prog found in name.\n");
+ goto fail;
+ }
+
+ if (reg_unbind_prog(TEST_NAME, TEST_PROG) < 0) {
+ printf("Failed to unbind prog.\n");
+ goto fail;
+ }
+
+ reg_destroy_prog(TEST_PROG);
+
+ reg_destroy_name(TEST_NAME);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_inherit_prog(void)
+{
+ struct name_info nameinfo = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR
+ };
+
+ struct prog_info proginfo = {
+ .name = TEST_PROG
+ };
+
+ struct proc_info procinfo = {
+ .pid = TEST_PID,
+ .prog = TEST_PROG
+ };
+
+ char * exec[] = { TEST_PROG, NULL};
+
+ TEST_START();
+
+ if (reg_init()) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_name(&nameinfo) < 0) {
+ printf("Failed to create name.\n");
+ goto fail;
+ }
+
+ if (reg_create_prog(&proginfo) < 0) {
+ printf("Failed to create prog.\n");
+ goto fail;
+ }
+
+ if (reg_bind_prog(TEST_NAME, exec, 0) < 0) {
+ printf("Failed to bind prog.\n");
+ goto fail;
+ }
+
+ if (reg_create_proc(&procinfo) < 0) {
+ printf("Failed to create proc.\n");
+ goto fail;
+ }
+
+ if (!reg_name_has_proc(__reg_get_name(TEST_NAME), TEST_PID)) {
+ printf("Failed to update name from prog.\n");
+ goto fail;
+ }
+
+ if (!reg_proc_has_name(__reg_get_proc(TEST_PID), TEST_NAME)) {
+ printf("Failed to update proc from prog.\n");
+ goto fail;
+ }
+
+ reg_destroy_proc(TEST_PID);
+
+ reg_destroy_prog(TEST_PROG);
+
+ reg_destroy_name(TEST_NAME);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_wait_accepting_timeout(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_MS(1);
+ int flow_id;
+ uint8_t hash[64];
+ struct name_info ninfo = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR
+ };
+
+ TEST_START();
+
+ if (reg_init()) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_name(&ninfo) < 0) {
+ printf("Failed to create name.\n");
+ goto fail;
+ }
+
+ str_hash(HASH_SHA3_256, hash, ninfo.name);
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime);
+ if (flow_id != -ETIMEDOUT) {
+ printf("Wait accept did not time out: %d.\n", flow_id);
+ goto fail;
+ }
+
+ reg_destroy_name(TEST_NAME);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_wait_accepting_fail_name(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+ int flow_id;
+ uint8_t hash[64];
+
+ TEST_START();
+
+ if (reg_init()) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+ str_hash(HASH_SHA3_256, hash, "C0FF33");
+
+ flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime);
+ if (flow_id != -ENAME) {
+ printf("Wait accept did not fail on name: %d.\n", flow_id);
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static void * test_call_flow_accept(void * o)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_MS(1);
+ buffer_t pbuf = {NULL, 0};
+
+ struct proc_info pinfo = {
+ .pid = TEST_PID,
+ .prog = TEST_PROG
+ };
+
+ struct flow_info info = {
+ .n_pid = pinfo.pid,
+ .qs = qos_raw,
+ };
+
+ if (reg_create_proc(&pinfo) < 0) {
+ printf("Failed to create proc.\n");
+ goto fail;
+ }
+
+ if (reg_bind_proc((char *) o, TEST_PID) < 0) {
+ printf("Failed to bind proc.\n");
+ goto fail;
+ }
+
+ if (reg_create_flow(&info) < 0) {
+ printf("Failed to create flow.\n");
+ goto fail;
+ }
+
+ info.state = FLOW_ACCEPT_PENDING;
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ reg_prepare_flow_accept(&info, &pbuf);
+
+ if (reg_wait_flow_accepted(&info, &pbuf, &abstime) != -ETIMEDOUT) {
+ printf("Wait allocated did not timeout.\n");
+ goto fail;
+ }
+
+ reg_destroy_flow(info.id);
+ reg_destroy_proc(pinfo.pid);
+
+ return (void *) 0;
+ fail:
+ return (void *) -1;
+}
+
+static int test_wait_accepting_success(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+ int flow_id;
+ pthread_t thr;
+ uint8_t hash[64];
+ struct name_info ninfo = {
+ .name = TEST_NAME,
+ .pol_lb = LB_RR
+ };
+
+ TEST_START();
+
+ if (reg_init()) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_name(&ninfo) < 0) {
+ printf("Failed to create name.\n");
+ goto fail;
+ }
+
+ pthread_create(&thr, NULL, test_call_flow_accept, ninfo.name);
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ str_hash(HASH_SHA3_256, hash, ninfo.name);
+
+ flow_id = reg_wait_flow_accepting(HASH_SHA3_256, hash, &abstime);
+ if (flow_id < 0) {
+ printf("Wait accept did not return a flow id: %d.", flow_id);
+ goto fail;
+ }
+
+ pthread_join(thr, NULL);
+
+ reg_destroy_name(TEST_NAME);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_wait_accepting(void)
+{
+ int ret = 0;
+
+ ret |= test_wait_accepting_timeout();
+
+ ret |= test_wait_accepting_fail_name();
+
+ ret |= test_wait_accepting_success();
+
+ return ret;
+}
+
+static int test_wait_ipcp_boot_timeout(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_MS(1);
+ struct ipcp_info info = {
+ .name = TEST_IPCP,
+ .pid = TEST_PID,
+ .state = IPCP_BOOT /* set by spawn_ipcp */
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_ipcp(&info) < 0) {
+ printf("Failed to create ipcp.\n");
+ goto fail;
+ }
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ if (reg_wait_ipcp_boot(&info, &abstime) != -ETIMEDOUT) {
+ printf("Wait boot did not timeout.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_ipcp(info.pid) < 0) {
+ printf("Failed to destroy ipcp.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static void * test_ipcp_respond(void * o)
+{
+ (void) o;
+
+ reg_respond_ipcp((struct ipcp_info *) o);
+
+ return (void *) 0;
+}
+
+static int test_wait_ipcp_boot_fail(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+ pthread_t thr;
+ struct ipcp_info info = {
+ .name = TEST_IPCP,
+ .pid = TEST_PID,
+ .state = IPCP_BOOT /* set by spawn_ipcp */
+ };
+ struct ipcp_info resp_info = {
+ .name = TEST_IPCP,
+ .pid = TEST_PID,
+ .state = IPCP_NULL
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_ipcp(&info) < 0) {
+ printf("Failed to create ipcp.\n");
+ goto fail;
+ }
+
+ pthread_create(&thr, NULL, test_ipcp_respond, &resp_info);
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ info.state = IPCP_BOOT;
+
+ if (reg_wait_ipcp_boot(&info, &abstime) == 0) {
+ printf("IPCP boot reported success.\n");
+ goto fail;
+ }
+
+ pthread_join(thr, NULL);
+
+ if (reg_destroy_ipcp(info.pid) < 0) {
+ printf("Failed to destroy ipcp.\n");
+ goto fail;
+ }
+
+ if (reg.n_ipcps != 0) {
+ printf("n_ipcps was not updated.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_wait_ipcp_boot_success(void)
+{
+ pthread_t thr;
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+ struct ipcp_info info = {
+ .name = TEST_IPCP,
+ .pid = TEST_PID,
+ .state = IPCP_BOOT /* set by spawn_ipcp */
+ };
+ struct ipcp_info resp_info = {
+ .name = TEST_IPCP,
+ .pid = TEST_PID,
+ .state = IPCP_OPERATIONAL
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ if (reg_create_ipcp(&info) < 0) {
+ printf("Failed to create ipcp.\n");
+ goto fail;
+ }
+
+ pthread_create(&thr, NULL, test_ipcp_respond, &resp_info);
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ info.state = IPCP_BOOT;
+
+ if (reg_wait_ipcp_boot(&info, &abstime) < 0) {
+ printf("IPCP boot failed.\n");
+ goto fail;
+ }
+
+ pthread_join(thr, NULL);
+
+ if (info.state != IPCP_OPERATIONAL) {
+ printf("IPCP boot succeeded in non-operational state.\n");
+ goto fail;
+ }
+
+ if (reg_destroy_ipcp(info.pid) < 0) {
+ printf("Failed to destroy ipcp.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_wait_ipcp_boot(void)
+{
+ int ret = 0;
+
+ ret |= test_wait_ipcp_boot_timeout();
+
+ ret |= test_wait_ipcp_boot_fail();
+
+ ret |= test_wait_ipcp_boot_success();
+
+ return ret;
+}
+
+static int test_wait_proc_timeout(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_MS(1);
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ if (reg_wait_proc(TEST_PID, &abstime) != -ETIMEDOUT) {
+ printf("Wait proc did not timeout.\n");
+ goto fail;
+ }
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static void * test_proc(void * o)
+{
+ (void) o;
+
+ reg_create_proc((struct proc_info *) o);
+
+ return (void *) 0;
+}
+
+static int test_wait_proc_success(void)
+{
+ struct timespec abstime;
+ struct timespec timeo = TIMESPEC_INIT_S(1);
+ pthread_t thr;
+ struct proc_info info = {
+ .pid = TEST_PID,
+ .prog = TEST_PROG
+ };
+
+ TEST_START();
+
+ if (reg_init() < 0) {
+ printf("Failed to init registry.\n");
+ goto fail;
+ }
+
+ pthread_create(&thr, NULL, test_proc, &info);
+
+ clock_gettime(PTHREAD_COND_CLOCK, &abstime);
+ ts_add(&abstime, &timeo, &abstime);
+
+ if (reg_wait_proc(info.pid, &abstime) < 0) {
+ printf("Waiting for proc failed.\n");
+ goto fail;
+ }
+
+ pthread_join(thr, NULL);
+
+ reg_destroy_proc(info.pid);
+
+ reg_fini();
+
+ TEST_SUCCESS();
+
+ return 0;
+ fail:
+ REG_TEST_FAIL();
+ return -1;
+}
+
+static int test_wait_proc(void)
+{
+ int ret = 0;
+
+ ret |= test_wait_proc_timeout();
+
+ ret |= test_wait_proc_success();
+
+ return ret;
+}
+
+
+int reg_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_reg_init();
+
+ ret |= test_reg_flow();
+
+ ret |= test_reg_ipcp();
+
+ ret |= test_reg_name();
+
+ ret |= test_reg_proc();
+
+ ret |= test_reg_prog();
+
+ ret |= test_reg_spawned();
+
+ ret |= test_bind_proc();
+
+ ret |= test_bind_prog();
+
+ ret |= test_inherit_prog();
+
+ ret |= test_wait_accepting();
+
+ ret |= test_wait_ipcp_boot();
+
+ ret |= test_wait_proc();
+
+ return ret;
+} \ No newline at end of file