summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorrsiddharth <s@ricketyspace.net>2020-04-17 21:02:35 -0400
committerrsiddharth <s@ricketyspace.net>2020-04-17 21:02:35 -0400
commitb924fc2f66d46ee10aa3b800a6521d3940919f9f (patch)
tree130a19d8211874e3ba01203af0d2332506106be4 /tests
parent1bab8e87d3875f672e7c36d10aea9e05f657c664 (diff)
nserver/ -> ./
Diffstat (limited to 'tests')
-rw-r--r--tests/darray_algos_tests.c102
-rw-r--r--tests/darray_tests.c361
-rw-r--r--tests/db_tests.c55
-rw-r--r--tests/hashmap_tests.c311
-rw-r--r--tests/minunit.h38
-rw-r--r--tests/ncmd_tests.c492
-rw-r--r--tests/protocol_tests.c202
-rw-r--r--tests/runtests.sh19
-rw-r--r--tests/stats_tests.c132
-rw-r--r--tests/tstree_tests.c132
10 files changed, 1844 insertions, 0 deletions
diff --git a/tests/darray_algos_tests.c b/tests/darray_algos_tests.c
new file mode 100644
index 0000000..01ef471
--- /dev/null
+++ b/tests/darray_algos_tests.c
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2010, Zed A. Shaw.
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <darray_algos.h>
+
+int testcmp(char **a, char **b)
+{
+ return strcmp(*a, *b);
+}
+
+DArray *create_words()
+{
+ DArray *result = DArray_create(0, 5);
+ char *words[] = { "asdfasfd",
+ "werwar", "13234", "asdfasfd", "oioj" };
+ int i = 0;
+
+ for (i = 0; i < 5; i++) {
+ DArray_push(result, words[i]);
+ }
+
+ return result;
+}
+
+int is_sorted(DArray *array)
+{
+ int i = 0;
+
+ for (i = 0; i < DArray_count(array) -1; i++) {
+ if (strcmp(DArray_get(array, i), DArray_get(array, i + 1)) > 0) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+char *run_sort_test(int (*func) (DArray *, DArray_compare),
+ const char *name)
+{
+ DArray *words = create_words();
+ mu_assert(!is_sorted(words), "Words should start not sorted.");
+
+ debug("--- Testing %s sorting algorithm", name);
+ int rc = func(words, (DArray_compare) testcmp);
+ mu_assert(rc == 0, "sort failed");
+ mu_assert(is_sorted(words), "didn't sort it");
+
+ DArray_destroy(words);
+
+ return NULL;
+}
+
+char *test_qsort()
+{
+ return run_sort_test(DArray_qsort, "qsort");
+}
+
+char *test_fucked_qsort()
+{
+ return run_sort_test(DArray_fucked_qsort, "fucked qsort");
+}
+
+char *test_heapsort()
+{
+ return run_sort_test(DArray_heapsort, "heapsort");
+}
+
+char *test_fucked_heapsort()
+{
+ return run_sort_test(DArray_fucked_heapsort, "fucked heapsort");
+}
+
+char *test_mergesort()
+{
+ return run_sort_test(DArray_mergesort, "mergesort");
+}
+
+char *test_fucked_mergesort()
+{
+ return run_sort_test(DArray_fucked_mergesort, "fucked mergesort");
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_qsort);
+ mu_run_test(test_fucked_qsort);
+ mu_run_test(test_heapsort);
+ mu_run_test(test_fucked_heapsort);
+ mu_run_test(test_mergesort);
+ mu_run_test(test_fucked_mergesort);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
diff --git a/tests/darray_tests.c b/tests/darray_tests.c
new file mode 100644
index 0000000..b33c460
--- /dev/null
+++ b/tests/darray_tests.c
@@ -0,0 +1,361 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2010, Zed A. Shaw.
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <darray.h>
+
+static DArray *array = NULL;
+static int *val1 = NULL;
+static int *val2 = NULL;
+
+int testcmp(int **a, int **b)
+{
+ int x = **a, y = **b;
+
+ if (x == y) {
+ return 0;
+ } else if (x < y) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+char *test_create()
+{
+ // Test fail.
+ array = DArray_create(0, 0);
+ mu_assert(array == NULL, "array must be NULL.");
+
+ // Test success.
+ array = DArray_create(sizeof(int), 100);
+ mu_assert(array != NULL, "DArray_create failed.");
+ mu_assert(array->contents != NULL, "contents are wrong in array.");
+ mu_assert(array->end == 0, "end isn't at the right spot");
+ mu_assert(array->element_size == sizeof(int),
+ "element size is wrong.");
+ mu_assert(array->max == 100, "wrong max length on initial size");
+
+ return NULL;
+}
+
+char *test_destroy()
+{
+ DArray_destroy(array);
+
+ return NULL;
+}
+
+char *test_new()
+{
+ // Test fail.
+ val1 = DArray_new(NULL);
+ mu_assert(val1 == NULL, "val1 must be NULL");
+
+ // Test success
+ val1 = DArray_new(array);
+ mu_assert(val1 != NULL, "failed to make a new element");
+
+ val2 = DArray_new(array);
+ mu_assert(val2 != NULL, "failed to make a new element");
+
+ return NULL;
+}
+
+char *test_set()
+{
+ int rc = 0;
+
+ // Test fail.
+
+ // case where array is NULL
+ rc = DArray_set(NULL, 0, val1);
+ mu_assert(rc == -1, "rc must be -1");
+
+ // case where i < 0
+ rc = DArray_set(array, -1, val1);
+ mu_assert(rc == -1, "rc must be -1");
+
+ // case where i > array->max
+ rc = DArray_set(array, array->max + 1, val1);
+ mu_assert(rc == -1, "rc must be -1");
+
+ // Test success.
+
+ // must set val1 at index 0
+ rc = DArray_set(array, 0, val1);
+ mu_assert(rc == 0, "rc must be 0");
+
+ // must set val2 at index 1
+ rc = DArray_set(array, 1, val2);
+ mu_assert(rc == 1, "rc must be 1");
+
+ return NULL;
+}
+
+char *test_get()
+{
+ void *rc = NULL;
+
+ // Test fail.
+
+ // case where array is NULL
+ rc = DArray_get(NULL, 0);
+ mu_assert(rc == NULL, "rc must be NULL");
+
+ // case where i < 0
+ rc = DArray_get(array, -2);
+ mu_assert(rc == NULL, "rc must be NULL");
+
+ // case where i > array->max
+ rc = DArray_get(array, array->max + 1);
+ mu_assert(rc == NULL, "rc must be NULL");
+
+ // Test succcess.
+ mu_assert(DArray_get(array, 0) == val1, "Wrong first value");
+ mu_assert(DArray_get(array, 1) == val2, "Wrong second value");
+
+ return NULL;
+}
+
+char *test_remove()
+{
+ int *val_check = NULL;
+
+ // Test fail.
+
+ // case where array is NULL.
+ val_check = DArray_remove(NULL, 0);
+ mu_assert(val_check == NULL, "val_check must be NULL");
+ mu_assert(array->contents[0] != NULL,
+ "array->contents[0] must not be NULL");
+
+ // case where i < 0
+ val_check = DArray_remove(array, -1);
+ mu_assert(val_check == NULL, "val_check must be NULL");
+
+ // case where i > array->max
+ val_check = DArray_remove(array, array->max + 1);
+ mu_assert(val_check == NULL, "val_check must be NULL");
+
+ // Test success.
+ val_check = DArray_remove(array, 0);
+ mu_assert(val_check !=NULL, "Should not get NULL");
+ mu_assert(*val_check == *val1, "Should get the first value.");
+ mu_assert(DArray_get(array, 0) == NULL, "Should be gone.");
+ DArray_free(val_check);
+
+ val_check = DArray_remove(array, 1);
+ mu_assert(val_check != NULL, "Should not get NULL.");
+ mu_assert(*val_check == *val2, "Should get second value.");
+ mu_assert(DArray_get(array, 1) == NULL, "Should be gone");
+ DArray_free(val_check);
+
+ return NULL;
+}
+
+char *test_expand_contract()
+{
+ int old_max = array->max;
+ DArray_expand(array);
+ mu_assert((unsigned int) array->max == old_max + array->expand_rate,
+ "Wrong size after expand.");
+
+ // Check if newly allocated space is all set to 0.
+ int i = 0;
+ for (i = old_max; i < array->max; i++) {
+ mu_assert(array->contents[i] == 0, "contents must be 0");
+ }
+
+ int rc = 0;
+ rc = DArray_contract(array);
+ mu_assert(rc == 0, "rc must be 0");
+ mu_assert((unsigned int) array->max == array->expand_rate + 1,
+ "Should stay at the expand_rate at least.");
+
+ rc = DArray_contract(array);
+ mu_assert(rc == 0, "rc must be 0");
+ mu_assert((unsigned int) array->max == array->expand_rate + 1,
+ "Should stay at the expand_rate at least");
+
+ return NULL;
+}
+
+char *test_push_pop()
+{
+ int i = 0, rc = 0;
+ for (i = 0; i < 1000; i++) {
+ int *val = DArray_new(array);
+ *val = i * 333;
+ rc = DArray_push(array, val);
+ mu_assert(rc == 0, "Darray_push failed");
+ }
+
+ mu_assert(array->max == 1201, "Wrong max size.");
+
+ for (i = 999; i >= 0; i--) {
+ int *val = DArray_pop(array);
+ mu_assert(val != NULL, "Shouldn't get a NULL");
+ mu_assert(*val == i * 333, "Wrong value.");
+
+ if ((DArray_end(array) > (int) array->expand_rate) &&
+ (DArray_end(array) % array->expand_rate)) {
+ mu_assert(array->max == DArray_end(array) + 1,
+ "DArray pop contract error");
+ }
+
+ DArray_free(val);
+ }
+
+ return NULL;
+}
+
+int is_sorted(DArray *array)
+{
+ int i = 0;
+
+ for (i = 0; i < DArray_count(array) -1; i++) {
+ if (*((int *) DArray_get(array, i)) > *((int *) DArray_get(array, i + 1))) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+char *test_shallow_copy()
+{
+ // First populate array.
+ array = DArray_create(sizeof(int), 100);
+ int i = 0, rc = 0;
+ int *val = NULL;
+ for (i = 0; i < 1000; i++) {
+ val = DArray_new(array);
+ *val = i * 333;
+ rc = DArray_push(array, val);
+ mu_assert(rc == 0, "Darray_push failed");
+ }
+ mu_assert(array->max == 1300, "Wrong max size.");
+
+ // Set at 1100
+ val = DArray_new(array);
+ *val = 42;
+ rc = DArray_set(array, 1100, val);
+ mu_assert(rc == 1100, "Error setting value at 1100");
+
+ // Set at 1110
+ val = DArray_new(array);
+ *val = 4242;
+ rc = DArray_set(array, 1110, val);
+ mu_assert(rc == 1110, "Error setting value at 1110");
+
+ // Test shallow copy
+ DArray *copy = DArray_shallow_copy(array);
+ mu_assert(copy != NULL, "Shallow copy failed");
+
+ int expected = 0;
+ for (i = 0; i < 1000; i++) {
+ expected = i * 333;
+ val = DArray_get(copy, i);
+ mu_assert(*val == expected, "Unexpected element in copy.");
+ }
+
+ expected = 42;
+ val = DArray_get(copy, 1100);
+ mu_assert(*val == expected, "Unexpected element at in copy.");
+
+ expected = 4242;
+ val = DArray_get(copy, 1110);
+ mu_assert(*val == expected, "Unexpected element at in copy.");
+
+ // Destroy copy.
+ DArray_destroy(copy);
+
+ // Destroy array.
+ DArray_clear_destroy(array);
+ return NULL;
+}
+
+char *test_sort_add()
+{
+ array = DArray_create(sizeof(int), 100);
+ mu_assert(array != NULL, "Error initializing array");
+
+ int i = 0, rc = 0;
+ int *val = NULL;
+ for (i = 0; i < 2000; i++) {
+ val = DArray_new(array);
+ mu_assert(val != NULL, "Error creating new element");
+ *val = rand();
+
+ rc = DArray_sort_add(array, val, (DArray_compare) testcmp);
+ mu_assert(rc == 0, "Error adding element.");
+ mu_assert(is_sorted(array) == 1, "array not sorted.");
+ }
+
+ DArray_clear_destroy(array);
+ return NULL;
+}
+
+char *test_find()
+{
+ array = DArray_create(sizeof(int), 100);
+ mu_assert(array != NULL, "Error initializing array");
+
+ // test setup.
+ int i = 0, rc = 0;
+ int *val = NULL;
+ for (i = 0; i < 2000; i++) {
+ val = DArray_new(array);
+ mu_assert(val != NULL, "Error creating new element");
+ *val = i;
+
+ rc = DArray_sort_add(array, val, (DArray_compare) testcmp);
+ mu_assert(rc == 0, "Error adding element");
+ mu_assert(is_sorted(array) == 1, "array not sorted");
+ }
+
+ // tests.
+ val = DArray_new(array);
+ mu_assert(val != NULL, "Error creating new element");
+
+ for (i = 0; i < 2000; i++) {
+ *val = i;
+
+ rc = DArray_find(array, val, (DArray_compare) testcmp);
+ mu_assert(rc == i, "val not found in array");
+ }
+
+ *val = 3000;
+ rc = DArray_find(array, val, (DArray_compare) testcmp);
+ mu_assert(rc == -1, "rc must be -1");
+
+ // test teardown.
+ DArray_clear_destroy(array);
+ free(val);
+ return NULL;
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_create);
+ mu_run_test(test_new);
+ mu_run_test(test_set);
+ mu_run_test(test_get);
+ mu_run_test(test_remove);
+ mu_run_test(test_expand_contract);
+ mu_run_test(test_push_pop);
+ mu_run_test(test_destroy);
+ mu_run_test(test_shallow_copy);
+ mu_run_test(test_sort_add);
+ mu_run_test(test_find);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
diff --git a/tests/db_tests.c b/tests/db_tests.c
new file mode 100644
index 0000000..dce3039
--- /dev/null
+++ b/tests/db_tests.c
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <db.h>
+
+char *test_db_init()
+{
+ int rc = db_init();
+ mu_assert(rc == 0, "db init failed");
+
+ return NULL;
+}
+
+char *test_db_store()
+{
+ char *k = "hello";
+ char *v = "kirk";
+
+ int rc = db_store(k, v);
+ mu_assert(rc == 0, "db store failed");
+
+ return NULL;
+}
+
+char *test_db_load()
+{
+ char *k = "hello";
+ char *expected_v = "kirk";
+
+ char *v = db_load(k);
+ mu_assert(v != NULL, "key not found");
+ mu_assert(strncmp(v, expected_v, strlen(expected_v)) == 0,
+ "incorrect value for key");
+
+ // cleanup.
+ free(v);
+
+ return NULL;
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_db_init);
+ mu_run_test(test_db_store);
+ mu_run_test(test_db_load);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
diff --git a/tests/hashmap_tests.c b/tests/hashmap_tests.c
new file mode 100644
index 0000000..d4835dc
--- /dev/null
+++ b/tests/hashmap_tests.c
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2010, Zed A. Shaw.
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <hashmap.h>
+#include <assert.h>
+#include <bstrlib.h>
+
+Hashmap *map = NULL;
+Hashmap *map_alt = NULL;
+static int traverse_called = 0;
+static int traverse_duplicates = 0;
+struct tagbstring test0 = bsStatic("test data 0");
+struct tagbstring test1 = bsStatic("test data 1");
+struct tagbstring test2 = bsStatic("test data 2");
+struct tagbstring test3 = bsStatic("xest data 3");
+struct tagbstring expect0 = bsStatic("THE VALUE 0");
+struct tagbstring expect1 = bsStatic("THE VALUE 1");
+struct tagbstring expect2 = bsStatic("THE VALUE 2");
+struct tagbstring expect3 = bsStatic("THE VALUE 3");
+
+static int traverse_good_cb(HashmapNode *node)
+{
+ debug("KEY: %s", bdata((bstring) node->key));
+ traverse_called++;
+ return 0;
+}
+
+static int traverse_fail_cb(HashmapNode *node)
+{
+ debug("KEY %s", bdata((bstring) node->key));
+ traverse_called++;
+
+ if (traverse_called == 2) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int traverse_duplicates_cb(HashmapNode *node)
+{
+ if ((bstring) node->data == &expect1) {
+ ++traverse_duplicates;
+ }
+
+ return 0;
+}
+
+int non_empty_buckets(Hashmap *map)
+{
+ int i = 0, non_empty = 0;
+
+ for (i = 0; i < DArray_count(map->buckets); i++) {
+ DArray *bucket = DArray_get(map->buckets, i);
+ if (bucket) {
+ non_empty += 1;
+ }
+ }
+
+ return non_empty;
+}
+
+char *test_create()
+{
+ map = Hashmap_create(NULL, NULL);
+ mu_assert(map != NULL, "Failed to create map.");
+
+ map_alt = Hashmap_create(NULL, (Hashmap_hash) fnv_hash);
+ mu_assert(map != NULL, "Failde to create map_alt.");
+
+ return NULL;
+}
+
+char *test_destroy()
+{
+ Hashmap_destroy(map);
+ Hashmap_destroy(map_alt);
+ return NULL;
+}
+
+char *test_get_test()
+{
+ int rc = Hashmap_set(map, &test1, &expect1);
+ mu_assert(rc == 0, "Failed to set &test1");
+ bstring result = Hashmap_get(map, &test1);
+ mu_assert(result == &expect1, "Wron value for test1.");
+
+ rc = Hashmap_set(map, &test2, &expect2);
+ mu_assert(rc == 0, "Failed to test test2");
+ result = Hashmap_get(map, &test2);
+ mu_assert(result == &expect2, "Wrong value for test2.");
+
+ rc = Hashmap_set(map, &test3, &expect3);
+ mu_assert(rc == 0, "Failed to set test3");
+ result = Hashmap_get(map, &test3);
+ mu_assert(result == &expect3, "Wrong value for test3");
+
+ return NULL;
+}
+
+char *test_traverse()
+{
+ int rc = Hashmap_traverse(map, traverse_good_cb);
+ mu_assert(rc == 0, "Failed to traverse");
+ mu_assert(traverse_called == 3, "Wrong count traverse");
+
+ traverse_called = 0;
+ rc = Hashmap_traverse(map, traverse_fail_cb);
+ mu_assert(rc == 1, "Failed to traverse.");
+ mu_assert(traverse_called == 2, "Wrong count traverse for fail.");
+
+ return NULL;
+}
+
+char *test_keys()
+{
+ DArray *keys = Hashmap_keys(map);
+ mu_assert(keys != NULL, "Unable to get hashmap's keys");
+ mu_assert(DArray_count(keys) == 3, "Expected three keys");
+
+ for (int i = 0; i < DArray_count(keys); i++) {
+ char *k = bdata((bstring) DArray_get(keys, i));
+
+ debug("KEY: %s", k);
+ }
+
+ // clean up.
+ DArray_destroy(keys);
+
+ return NULL;
+}
+
+char *test_delete()
+{
+ bstring deleted = (bstring) Hashmap_delete(map, &test1);
+ mu_assert(deleted != NULL, "Got NULL on delete");
+ mu_assert(deleted == &expect1, "Should get test1");
+ bstring result = Hashmap_get(map, &test1);
+ mu_assert(result == NULL, "Should delete");
+
+ deleted = (bstring) Hashmap_delete(map, &test2);
+ mu_assert(deleted != NULL, "Got NULL on delete");
+ mu_assert(deleted == &expect2, "Should get test2");
+ result = Hashmap_get(map, &test2);
+ mu_assert(result == NULL, "Should delete");
+
+ deleted = (bstring) Hashmap_delete(map, &test3);
+ mu_assert(deleted != NULL, "Got NULL on delete.");
+ mu_assert(deleted == &expect3, "Should get test3");
+ result = Hashmap_get(map, &test3);
+ mu_assert(result == NULL, "Should delete.");
+
+ return NULL;
+}
+
+char *test_bucket_destruction()
+{
+ int rc = 0, non_empty = 0;
+ bstring deleted = NULL;
+
+ // Insert some elements.
+ rc = Hashmap_set(map, &test1, &expect1);
+ mu_assert(rc == 0, "Failed to set &test1 0");
+ rc = Hashmap_set(map, &test2, &expect2);
+ mu_assert(rc == 0, "Failed to test test2");
+
+ // Non-empty buckets test.
+ non_empty = non_empty_buckets(map);
+ mu_assert(non_empty == 2, "Expected two non-empty buckets");
+
+ // Remove test1
+ deleted = Hashmap_delete(map, &test1);
+ mu_assert(deleted != NULL, "Error deleting test1");
+
+ // Non-empty buckets test.
+ non_empty = non_empty_buckets(map);
+ mu_assert(non_empty == 1, "Expected one non-empty buckets");
+
+ // Remove test2
+ deleted = Hashmap_delete(map, &test2);
+ mu_assert(deleted != NULL, "Error deleting test2");
+
+ // Non-empty buckets test.
+ non_empty = non_empty_buckets(map);
+ mu_assert(non_empty == 0, "Expected one non-empty buckets");
+
+ return NULL;
+}
+
+char *test_set_duplicates()
+{
+ int rc = 0;
+
+ // Insert test1 two times.
+ rc = Hashmap_set(map, &test1, &expect1);
+ mu_assert(rc == 0, "Failed to set &test1 0");
+ rc = Hashmap_set(map, &test1, &expect1);
+ mu_assert(rc == 0, "Failed to set &test1 1");
+
+ // Insert test2 one time.
+ rc = Hashmap_set(map, &test2, &expect2);
+ mu_assert(rc == 0, "Failed to test test2");
+
+ // Insert test3 one time.
+ rc = Hashmap_set(map, &test3, &expect3);
+ mu_assert(rc == 0, "Failed to set test3");
+
+ // Test there are two test1 nodes.
+ traverse_duplicates = 0;
+ rc = Hashmap_traverse(map, traverse_duplicates_cb);
+ mu_assert(traverse_duplicates == 2,
+ "traverse_duplicates must be 2");
+
+ // Cleanup
+ bstring deleted = NULL;
+ deleted = Hashmap_delete(map, &test1);
+ mu_assert(deleted != NULL, "Error deleting test1 0");
+ deleted = Hashmap_delete(map, &test1);
+ mu_assert(deleted != NULL, "Error deleting test1 1");
+ deleted = Hashmap_delete(map, &test2);
+ mu_assert(deleted != NULL, "Error deleting test2");
+ deleted = Hashmap_delete(map, &test3);
+ mu_assert(deleted != NULL, "Error deleting test3");
+
+ return NULL;
+}
+
+char *test_set_fucked()
+{
+ int rc = 0;
+
+ // Insert test1 three times.
+ rc = Hashmap_set_fucked(map, &test1, &expect1);
+ mu_assert(rc == 1, "Failed to set fuck &test1 0");
+ rc = Hashmap_set_fucked(map, &test1, &expect1);
+ mu_assert(rc == 0, "Failed to set fuck &test1 1");
+ rc = Hashmap_set_fucked(map, &test1, &expect1);
+ mu_assert(rc == 0, "Failed to set fuck &test1 2");
+
+ // Insert test2 one time.
+ rc = Hashmap_set_fucked(map, &test2, &expect2);
+ mu_assert(rc == 1, "Failed to test test2");
+
+ // Insert test3 one time.
+ rc = Hashmap_set_fucked(map, &test3, &expect3);
+ mu_assert(rc == 1, "Failed to set test3");
+
+ // Test there are two test1 nodes.
+ traverse_duplicates = 0;
+ rc = Hashmap_traverse(map, traverse_duplicates_cb);
+ mu_assert(traverse_duplicates == 1,
+ "traverse_duplicates must be 1");
+
+ // Cleanup
+ bstring deleted = NULL;
+ deleted = Hashmap_delete(map, &test1);
+ mu_assert(deleted != NULL, "Error deleting test1");
+ deleted = Hashmap_delete(map, &test1);
+ mu_assert(deleted == NULL, "Error test1 must be already deleted");
+ deleted = Hashmap_delete(map, &test2);
+ mu_assert(deleted != NULL, "Error deleting test2");
+ deleted = Hashmap_delete(map, &test3);
+ mu_assert(deleted != NULL, "Error deleting test3");
+
+ return NULL;
+}
+
+char *test_fnv_hash()
+{
+ uint32_t hash[3];
+
+ hash[0] = fnv_hash(&test0);
+ mu_assert(hash[0] > 0, "hash not set correctly for key test0");
+
+ hash[1] = fnv_hash(&test1);
+ mu_assert(hash[1] > 0, "hash not set correctly for key test1");
+
+ hash[2] = fnv_hash(&test2);
+ mu_assert(hash[2] > 0, "hash not set correctly for key test2");
+
+ // Check all three hashes are different.
+ mu_assert(hash[0] != hash[1], "hash for test0 = hash for test1");
+ mu_assert(hash[0] != hash[2], "hash for test0 = hash for test2");
+ mu_assert(hash[1] != hash[2], "hash for test1 = hash for test2");
+
+ return NULL;
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_create);
+ mu_run_test(test_get_test);
+ mu_run_test(test_traverse);
+ mu_run_test(test_keys);
+ mu_run_test(test_delete);
+ mu_run_test(test_bucket_destruction);
+ mu_run_test(test_set_duplicates);
+ mu_run_test(test_set_fucked);
+ mu_run_test(test_fnv_hash);
+ mu_run_test(test_destroy);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
diff --git a/tests/minunit.h b/tests/minunit.h
new file mode 100644
index 0000000..4ddf265
--- /dev/null
+++ b/tests/minunit.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2010, Zed A. Shaw.
+ */
+
+#undef NDEBUG
+#ifndef _minunit_h
+#define _minunit_h
+
+#include <stdio.h>
+#include <dbg.h>
+#include <stdlib.h>
+
+#define mu_suite_start() char *message = NULL
+
+#define mu_assert(test, message) if (!(test)) {\
+ log_err(message); return message; }
+#define mu_run_test(test) debug("\n-----%s", " " #test); \
+ message = test(); tests_run++; if (message) return message;
+
+#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
+ argc = 1; \
+ debug("----- RUNNING: %s", argv[0]); \
+ printf("----\nRUNNING: %s\n", argv[0]);\
+ char *result = name();\
+ if (result != 0) {\
+ printf("FAILED: %s\n", result); \
+ }\
+ else {\
+ printf("ALL TESTS PASSED\n");\
+ }\
+ printf("Tests run: %d\n", tests_run);\
+ exit(result != 0);\
+ }
+
+int tests_run;
+
+#endif
diff --git a/tests/ncmd_tests.c b/tests/ncmd_tests.c
new file mode 100644
index 0000000..6174371
--- /dev/null
+++ b/tests/ncmd_tests.c
@@ -0,0 +1,492 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <ncmd.h>
+
+char *test_sanitize()
+{
+ char *cmd = calloc(128, sizeof(char));
+ mu_assert(cmd != NULL, "calloc failed");
+
+ char *c_cmd = "/create api/beef\n";
+ size_t c_cmd_sz = strlen(c_cmd);
+
+ strncpy(cmd, c_cmd, c_cmd_sz + 1);
+ mu_assert(strlen(cmd) == c_cmd_sz, "strncpy failed");
+
+ sanitize(cmd);
+ mu_assert(strlen(cmd) == c_cmd_sz - 1, "sanitize failed");
+ mu_assert(strcmp(cmd, "/create api/beef") == 0, "sanitize failed");
+
+
+ // Clear cmd.
+ memset(cmd, '\0', 128);
+
+
+ c_cmd = "/create api/ham\n\n\n";
+ c_cmd_sz = strlen(c_cmd);
+
+ strncpy(cmd, c_cmd, c_cmd_sz + 1);
+ mu_assert(strlen(cmd) == c_cmd_sz, "strncpy failed");
+
+ sanitize(cmd);
+ mu_assert(strlen(cmd) == c_cmd_sz - 3, "sanitize failed");
+ mu_assert(strcmp(cmd, "/create api/ham") == 0, "sanitize failed");
+
+
+ // Clear cmd.
+ memset(cmd, '\0', 128);
+
+
+ c_cmd = "/create api/bacon\n/create api/turkey\n";
+ c_cmd_sz = strlen(c_cmd);
+
+ strncpy(cmd, c_cmd, c_cmd_sz + 1);
+ mu_assert(strlen(cmd) == c_cmd_sz, "strncpy failed");
+
+ sanitize(cmd);
+ mu_assert(strlen(cmd) == c_cmd_sz - 20, "sanitize failed");
+ mu_assert(strcmp(cmd, "/create api/bacon") == 0, "sanitize failed");
+
+
+ // Cleanup.
+ free(cmd);
+
+ return NULL;
+}
+
+char *test_check_cmd()
+{
+ char *err = (char *) calloc(RSP_SIZE, sizeof(char));
+
+ char *cmd = (char *) calloc(128, sizeof(char));
+ mu_assert(cmd != NULL, "calloc failed");
+
+ char *bacon = "/create api/bacon";
+ strncpy(cmd, bacon, strlen(bacon));
+ mu_assert(strlen(cmd) == strlen(bacon), "strncpy failed");
+
+ int rc = check_cmd(cmd, err);
+ mu_assert(rc == 0, "check_cmd failed");
+
+ memset(err, '\0', RSP_SIZE);
+ memset(cmd, '\0', 128);
+
+ char *c = "/c";
+ strncpy(cmd, c, strlen(c));
+ mu_assert(strlen(cmd) == strlen(c), "strncpy failed");
+
+ rc = check_cmd(cmd, err);
+ mu_assert(rc < 0, "check_cmd failed");
+ mu_assert(strcmp(err, "command size invalid\n") == 0,
+ "wrong err msg");
+
+ memset(err, '\0', RSP_SIZE);
+ memset(cmd, '\0', 128);
+
+
+ char *empty = "\n";
+ strncpy(cmd, empty, strlen(empty));
+ mu_assert(strlen(cmd) == strlen(empty), "strncpy failed");
+
+ rc = check_cmd(cmd, err);
+ mu_assert(rc < 0, "check_cmd failed");
+ mu_assert(strcmp(err, "closing connection\n") == 0,
+ "wrong err msg");
+
+
+ rc = check_cmd(NULL, err);
+ mu_assert(rc < 0, "check_cmd failed");
+ mu_assert(strcmp(err, "internal error\n") == 0,
+ "wrong err msg");
+
+
+ // Cleanup.
+ free(err);
+ free(cmd);
+
+ return NULL;
+}
+
+char *test_cmd_parts()
+{
+ struct bstrList *parts = NULL;
+
+ bstring create = bfromcstr("/create");
+
+ char *bacon = "/create bacon";
+ parts = cmd_parts(bacon);
+ mu_assert(parts != NULL, "cmd parts failed");
+ mu_assert(parts->qty == 2, "qty check failed");
+ mu_assert(bstricmp(parts->entry[0], create) == 0,
+ "equality check failed");
+
+ // Cleanup
+ bstrListDestroy(parts);
+
+ char *ham = "/create ham";
+ parts = cmd_parts(ham);
+ mu_assert(parts != NULL, "cmd parts failed");
+ mu_assert(parts->qty == 2, "qty check failed");
+ mu_assert(bstricmp(parts->entry[0], create) == 0,
+ "equality check failed");
+
+
+ // Cleanup
+ bdestroy(create);
+ bstrListDestroy(parts);
+
+ return NULL;
+}
+
+char *test_find_function()
+{
+ struct bstrList *parts = NULL;
+ int funk = 0;
+
+ char *bacon = "/create bacon";
+ parts = cmd_parts(bacon);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_CREATE, "find function create failed");
+
+ char *ham = "/create ham";
+ parts = cmd_parts(ham);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_CREATE, "find function create failed");
+
+ ham = "/CREate ham";
+ parts = cmd_parts(ham);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_CREATE, "find function create failed");
+
+ char *sample = "/sample bacon 42";
+ parts = cmd_parts(sample);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_SAMPLE, "find function sample failed");
+
+ sample = "/SAMPLE bacon 42";
+ parts = cmd_parts(sample);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_SAMPLE, "find function sample failed");
+
+ char *mean = "/mean bacon";
+ parts = cmd_parts(mean);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_MEAN, "find function mean failed");
+
+ char *dump = "/dump bacon";
+ parts = cmd_parts(dump);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_DUMP, "find function dump failed");
+
+ char *delete = "/delete bacon";
+ parts = cmd_parts(delete);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_DELETE, "find function delete failed");
+
+ char *list = "/list";
+ parts = cmd_parts(list);
+ mu_assert(parts != NULL, "cmp_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_LIST, "find function list failed");
+
+ list = "/LIST";
+ parts = cmd_parts(list);
+ mu_assert(parts != NULL, "cmp_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_LIST, "find function list failed");
+
+ char *store = "/store jowl";
+ parts = cmd_parts(store);
+ mu_assert(parts != NULL, "cmp_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_STORE, "find function list failed");
+
+ store = "/STore jowl";
+ parts = cmd_parts(store);
+ mu_assert(parts != NULL, "cmp_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_STORE, "find function list failed");
+
+ char *load = "/load jowl ears";
+ parts = cmd_parts(load);
+ mu_assert(parts != NULL, "cmp_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_LOAD, "find function list failed");
+
+ load = "/LoAD jowl ears";
+ parts = cmd_parts(load);
+ mu_assert(parts != NULL, "cmp_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_LOAD, "find function list failed");
+
+ char *nop = "/meant bacon";
+ parts = cmd_parts(nop);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ funk = find_function(parts);
+ mu_assert(funk == NS_NOP, "find function mean failed");
+
+ return NULL;
+}
+
+char *test_call_function()
+{
+ struct bstrList *parts = NULL;
+
+ char *msg = (char *) calloc(RSP_SIZE + 1, sizeof(char));
+ mu_assert(msg != NULL, "msg invalid");
+
+ char *bacon = "/create bacon";
+ parts = cmd_parts(bacon);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ int rc = call_function(NS_CREATE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *ham = "/create ham";
+ parts = cmd_parts(ham);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_CREATE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *beef = "/create beef";
+ parts = cmd_parts(beef);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_CREATE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *bacon_sample = "/sample bacon 4.2";
+ parts = cmd_parts(bacon_sample);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_SAMPLE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "Mean: 4.20\n") == 0, "call function failed");
+
+ bacon_sample = "/Sample bacon 6.9";
+ parts = cmd_parts(bacon_sample);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_SAMPLE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "Mean: 5.55\n") == 0, "call function failed");
+
+ char *bacon_mean = "/mean bacon";
+ parts = cmd_parts(bacon_mean);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_MEAN, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "Mean: 5.55\n") == 0, "call function failed");
+
+ char *bacon_dump = "/dump bacon";
+ parts = cmd_parts(bacon_dump);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_DUMP, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "sum: 11.100000, sumsq: 65.250000, n: 2, min: 4.200000, max: 6.900000, mean: 5.550000, stddev: 1.909188\n") == 0, "call function failed");
+
+ char *bacon_delete = "/delete bacon";
+ parts = cmd_parts(bacon_delete);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_DELETE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *list = "/list";
+ parts = cmd_parts(list);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_LIST, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "ham\nbeef\n") == 0
+ || strcmp(msg, "beef\nham\n") == 0,
+ "call function failed");
+
+
+ char *ham_store = "/store ham";
+ parts = cmd_parts(ham_store);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_STORE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+
+ char *beef_store = "/store beef";
+ parts = cmd_parts(beef_store);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_STORE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *roastedham_load = "/load ham roastedham";
+ parts = cmd_parts(roastedham_load);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_LOAD, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *grilledbeef_load = "/load beef grilledbeef";
+ parts = cmd_parts(grilledbeef_load);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_LOAD, parts, msg);
+ printf("LOAD FIAAAL %d %s\n", rc, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ // delete ham, roastedham, beef, and roastedbeef.
+ char *ham_delete = "/delete ham";
+ parts = cmd_parts(ham_delete);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_DELETE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *roastedham_delete = "/delete roastedham";
+ parts = cmd_parts(roastedham_delete);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_DELETE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *beef_delete = "/delete beef";
+ parts = cmd_parts(beef_delete);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_DELETE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ char *grilledbeef_delete = "/delete grilledbeef";
+ parts = cmd_parts(grilledbeef_delete);
+ mu_assert(parts != NULL, "cmd_parts failed");
+ rc = call_function(NS_DELETE, parts, msg);
+ mu_assert(rc == 0, "call function failed");
+ mu_assert(strcmp(msg, "OK\n") == 0, "call function failed");
+
+ return NULL;
+}
+
+char *test_process()
+{
+ char *out = (char *) calloc(RSP_SIZE + 1, sizeof(char));
+ mu_assert(out != NULL, "out invalid");
+
+ char *bacon = "/create bacon";
+ int rc = process(bacon, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *ham = "/create ham";
+ rc = process(ham, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *beef = "/create beef";
+ rc = process(beef, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *ham_sample = "/sample ham 4.3";
+ rc = process(ham_sample, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "Mean: 4.30\n") == 0, "process failed");
+
+ ham_sample = "/sample ham 6.0";
+ rc = process(ham_sample, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "Mean: 5.15\n") == 0, "process failed");
+
+ char *bacon_sample = "/sample bacon 4.2";
+ rc = process(bacon_sample, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "Mean: 4.20\n") == 0, "process failed");
+
+ bacon_sample = "/Sample bacon 6.9";
+ rc = process(bacon_sample, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "Mean: 5.55\n") == 0, "process failed");
+
+ char *bacon_mean = "/mean bacon";
+ rc = process(bacon_mean, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "Mean: 5.55\n") == 0, "process failed");
+
+ char *bacon_dump = "/dump bacon";
+ rc = process(bacon_dump, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "sum: 11.100000, sumsq: 65.250000, n: 2, min: 4.200000, max: 6.900000, mean: 5.550000, stddev: 1.909188\n") == 0, "process failed");
+
+ char *bacon_delete = "/delete bacon";
+ rc = process(bacon_delete, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *list = "/list";
+ rc = process(list, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "ham\nbeef\n") == 0
+ || strcmp(out, "beef\nham\n") == 0,
+ "process failed");
+
+ char *ham_store = "/store ham";
+ rc = process(ham_store, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *beef_store = "/store beef";
+ rc = process(beef_store, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *bacon_store = "/store bacon";
+ rc = process(bacon_store, out);
+ mu_assert(rc == -1, "process failed");
+ mu_assert(strcmp(out, "error: store failed\n") == 0, "process failed");
+
+ char *roastedham_load = "/load ham roastedham";
+ rc = process(roastedham_load, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *roastedham_delete = "/delete roastedham";
+ rc = process(roastedham_delete, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ roastedham_load = "/load ham roastedham";
+ rc = process(roastedham_load, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "OK\n") == 0, "process failed");
+
+ char *roastedham_dump = "/dump roastedham";
+ rc = process(roastedham_dump, out);
+ mu_assert(rc == 0, "process failed");
+ mu_assert(strcmp(out, "sum: 10.300000, sumsq: 54.490000, n: 2, min: 4.300000, max: 6.000000, mean: 5.150000, stddev: 1.202082\n") == 0, "process failed");
+
+ return NULL;
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_sanitize);
+ mu_run_test(test_check_cmd);
+ mu_run_test(test_cmd_parts);
+ mu_run_test(test_find_function);
+ mu_run_test(test_call_function);
+ mu_run_test(test_process);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
diff --git a/tests/protocol_tests.c b/tests/protocol_tests.c
new file mode 100644
index 0000000..62ebec3
--- /dev/null
+++ b/tests/protocol_tests.c
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <protocol.h>
+
+char *test_sscreate()
+{
+ int rc = 0;
+
+ rc = sscreate("/crimson");
+ mu_assert(rc == 0, "sscreate failed 0");
+
+ rc = sscreate("/vermilion");
+ mu_assert(rc == 0, "sscreate failed 1");
+
+ rc = sscreate("/crimson");
+ mu_assert(rc == 1, "sscreate failed 2");
+
+ rc = sscreate("/crimson/sky");
+ mu_assert(rc == 0, "sscreate failed 3");
+
+ // delete. check.
+ rc = ssdelete("/crimson/sky");
+ mu_assert(rc == 0, "ssdelete failed 0");
+
+ rc = sscreate("/crimson/sky");
+ mu_assert(rc == 2, "sscreate failed 4");
+
+ return NULL;
+}
+
+char *test_sssample()
+{
+ double mean = 0;
+
+ mean = sssample("/crimson", 3);
+ mu_assert(mean == 3.0, "sssample failed 0");
+
+ mean = sssample("/crimson", 9);
+ mu_assert(mean == 6.0, "sssample failed 1");
+
+ mean = sssample("/crimson", 12);
+ mu_assert(mean == 8.0, "sssample failed 2");
+
+ mean = sssample("/vermilion", 20);
+ mu_assert(mean == 20.0, "sssample failed 3");
+
+ mean = sssample("/vermilion", 27);
+ mu_assert(mean == 23.5, "sssample failed 4");
+
+ mean = sssample("/vermilion", 4);
+ mu_assert(mean == 17.0, "sssample failed 5");
+
+ mean = sssample("/ruby", 48);
+ mu_assert(mean == -1, "sssample failed 6");
+
+ mean = sssample("/crimson/sky", 42);
+ mu_assert(mean == 42.0, "sssample failed 7");
+
+ mean = sssample("/crimson", 10);
+ mu_assert(mean == 15.20, "sssample failed 8");
+
+ // delete check.
+ int rc = ssdelete("/crimson/sky");
+ mu_assert(rc == 0, "ssdeleted failed 0");
+
+ mean = sssample("/crimson/sky", 71);
+ mu_assert(mean == -1, "sssample failed 9");
+
+ rc = sscreate("/crimson/sky");
+ mu_assert(rc == 2, "sscreate failed 1");
+
+ mean = sssample("/crimson/sky", 71);
+ mu_assert(mean == 71.0, "sssample failed 9");
+
+
+
+ return NULL;
+}
+
+char *test_ssmean()
+{
+ double m = 0;
+
+ m = ssmean("/crimson");
+ mu_assert(m == 24.50, "ssmean failed 0");
+
+ m = ssmean("/vermilion");
+ mu_assert(m == 17, "ssmean failed 1");
+
+ m = ssmean("/ruby");
+ mu_assert(m == -1, "ssmean failed 2");
+
+ m = ssmean("/crimson/sky");
+ mu_assert(m == 71.0, "ssmean failed 3");
+
+ // delete check.
+ int rc = ssdelete("/crimson/sky");
+ mu_assert(rc == 0, "ssdeleted failed 0");
+
+ m = ssmean("/crimson/sky");
+ mu_assert(m == -1, "ssmean failed 4");
+
+ return NULL;
+}
+
+char *test_ssdump()
+{
+ char *dstr = NULL;
+
+ dstr = ssdump("/crimson");
+ mu_assert(dstr != NULL, "ssdump failed 0");
+ debug("DUMP: %s", dstr);
+
+ // clean up.
+ free(dstr);
+
+ dstr = ssdump("/vermilion");
+ mu_assert(dstr != NULL, "ssdump failed 1");
+ debug("DUMP: %s", dstr);
+
+ // clean up.
+ free(dstr);
+
+ dstr = ssdump("/ruby");
+ mu_assert(dstr == NULL, "ssdump failed 2");
+ debug("DUMP: %s", dstr);
+
+ dstr = ssdump("/crimson/sky");
+ mu_assert(dstr == NULL, "ssdump failed 2");
+
+ return NULL;
+}
+
+char *test_sslist()
+{
+ char *ks = sslist();
+ mu_assert(ks != NULL, "sslist failed");
+ mu_assert(strlen(ks) == 20, "length check failed");
+
+ return NULL;
+}
+
+char *test_ssstore()
+{
+
+ int rc = ssstore("/crimson");
+ mu_assert(rc == 0, "store /crimson failed");
+
+ rc = ssstore("/nonexistent");
+ mu_assert(rc == -1, "store /nonexistent failed");
+
+ return NULL;
+}
+
+char *test_ssload()
+{
+ int rc = ssload("/crimson", "/crimson");
+ mu_assert(rc == -2, "ssload failed 0");
+
+ rc = ssload("/crimson", "/amaranth");
+ mu_assert(rc == 0, "ssload failed 1");
+
+ return NULL;
+}
+
+char *test_ssdelete()
+{
+ int rc = 0;
+
+ rc = ssdelete("/crimson");
+ mu_assert(rc == 0, "delete failed 0");
+
+ rc = ssdelete("/vermilion");
+ mu_assert(rc == 0, "delete failed 1");
+
+ rc = ssdelete("/ruby");
+ mu_assert(rc == 0, "delete failed 2");
+
+ return NULL;
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_sscreate);
+ mu_run_test(test_sssample);
+ mu_run_test(test_ssmean);
+ mu_run_test(test_ssdump);
+ mu_run_test(test_sslist);
+ mu_run_test(test_ssstore);
+ mu_run_test(test_ssload);
+ mu_run_test(test_ssdelete);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
diff --git a/tests/runtests.sh b/tests/runtests.sh
new file mode 100644
index 0000000..ff579a3
--- /dev/null
+++ b/tests/runtests.sh
@@ -0,0 +1,19 @@
+echo "Running unit tests:"
+
+for i in tests/*_tests
+do
+ if test -f $i
+ then
+ if $VALGRIND ./$i 2>> tests/tests.log
+ then
+ echo $i PASS
+ else
+ echo "ERROR in test $i: here's tests/tests.log"
+ echo "------"
+ tail tests/tests.log
+ exit 1
+ fi
+ fi
+done
+
+echo ""
diff --git a/tests/stats_tests.c b/tests/stats_tests.c
new file mode 100644
index 0000000..72bee3f
--- /dev/null
+++ b/tests/stats_tests.c
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2010, Zed A. Shaw.
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <stats.h>
+#include <string.h>
+#include <math.h>
+
+static char *st_str = NULL;
+
+const int NUM_SAMPLES = 10;
+double samples[] = {
+ 6.1061334, 9.6783204, 1.2747090, 8.2395131, 0.3333483,
+ 6.9755066, 1.0626275, 7.6587523, 4.9382973, 9.5788115
+};
+
+Stats expect = {
+ .sumsq = 425.1641,
+ .sum = 55.84602,
+ .min = 0.333,
+ .max = 9.678,
+ .n = 10
+};
+
+double expect_mean = 5.584602;
+double expect_stddev = 3.547868;
+
+#define EQ(X,Y,N) (round((X) * pow(10, N)) == round((Y) * pow(10, N)))
+
+char *test_operations()
+{
+ int i = 0;
+ Stats *st = Stats_create();
+ mu_assert(st != NULL, "Fail to create stats.");
+
+ for (i = 0; i < NUM_SAMPLES; i++) {
+ Stats_sample(st, samples[i]);
+ }
+
+ Stats_dump(st);
+
+ mu_assert(EQ(st->sumsq, expect.sumsq, 3), "sumsq not valid");
+ mu_assert(EQ(st->sum, expect.sum, 3), "sum not valid");
+ mu_assert(EQ(st->min, expect.min, 3), "min not valid");
+ mu_assert(EQ(st->max, expect.max, 3), "max not valid");
+ mu_assert(EQ(st->n, expect.n, 3), "n not valid");
+ mu_assert(EQ(expect_mean, Stats_mean(st), 3), "mean not valid");
+ mu_assert(EQ(expect_stddev, Stats_stddev(st), 3),
+ "stddev not valid");
+
+ return NULL;
+}
+
+char *test_recreate()
+{
+ Stats *st = Stats_recreate(
+ expect.sum, expect.sumsq, expect.n, expect.min, expect.max);
+
+ mu_assert(st->sum == expect.sum, "sum not equal");
+ mu_assert(st->sumsq == expect.sumsq, "sumsq not equal");
+ mu_assert(st->n == expect.n, "n not equal");
+ mu_assert(st->min == expect.min, "min not equal");
+ mu_assert(st->max == expect.max, "max not equal");
+ mu_assert(EQ(expect_mean, Stats_mean(st), 3), "mean not valid");
+ mu_assert(EQ(expect_stddev, Stats_stddev(st), 3),
+ "stddev not valid");
+
+ return NULL;
+}
+
+char *test_stats_stringify()
+{
+ Stats *st = Stats_create();
+ mu_assert(st != NULL, "stats create failed");
+
+ // fill with dummy data.
+ st->sum = 8238.33892;
+ st->sumsq = 4260238.8292;
+ st->n = 28;
+ st->min = 28.3921;
+ st->max = 238.27;
+
+ st_str = Stats_stringify(st);
+ mu_assert(st_str != NULL, "stats stringify failed");
+
+ char *expected_st_str = "8238.34:4260238.83:28:28.39:238.27";
+ mu_assert(strncmp(st_str, expected_st_str, strlen(expected_st_str)) == 0,
+ "stringified str invalid");
+
+ // cleanup
+ free(st);
+
+ return NULL;
+}
+
+char *test_stats_unstringify()
+{
+ mu_assert(st_str != NULL, "st_str not initialized");
+
+ Stats *st = Stats_unstringify(st_str);
+ mu_assert(st != NULL, "stats unstringify failed");
+
+ mu_assert(st->sum == 8238.34, "stats sum incorrect");
+ mu_assert(st->sumsq == 4260238.83, "stats sumsq incorrect");
+ mu_assert(st->n == 28, "stats n incorrect");
+ mu_assert(st->min == 28.39, "stats min incorrect");
+ mu_assert(st->max == 238.27, "stats max incorrect");
+
+ // clean up
+ free(st);
+ free(st_str);
+
+ return NULL;
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_operations);
+ mu_run_test(test_recreate);
+
+ mu_run_test(test_stats_stringify);
+ mu_run_test(test_stats_unstringify);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);
diff --git a/tests/tstree_tests.c b/tests/tstree_tests.c
new file mode 100644
index 0000000..d8b5f9f
--- /dev/null
+++ b/tests/tstree_tests.c
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright © 2010, Zed A. Shaw.
+ * Copyright © 2020 rsiddharth <s@ricketyspace.net>
+ */
+
+#include "minunit.h"
+#include <tstree.h>
+#include <string.h>
+#include <assert.h>
+#include <bstrlib.h>
+
+TSTree *node = NULL;
+char *valueA = "VALUEA";
+char *valueB = "VALUEB";
+char *value2 = "VALUE2";
+char *value4 = "VALUE4";
+char *reverse = "VALUER";
+int traverse_count = 0;
+
+struct tagbstring test1 = bsStatic("TEST");
+struct tagbstring test2 = bsStatic("TEST2");
+struct tagbstring test3 = bsStatic("TSET");
+struct tagbstring test4 = bsStatic("T");
+
+char *test_insert()
+{
+ node = TSTree_insert(node, bdata(&test1), blength(&test1), valueA);
+ mu_assert(node != NULL, "Failed to insert into tst.");
+
+ node = TSTree_insert(node, bdata(&test2), blength(&test2), value2);
+ mu_assert(node != NULL,
+ "Failed to insert into tst with second name.");
+
+ node = TSTree_insert(node, bdata(&test3), blength(&test3), reverse);
+ mu_assert(node != NULL,
+ "Failed to insert into tst with reverse name");
+
+ node = TSTree_insert(node, bdata(&test4), blength(&test4), value4);
+ mu_assert(node != NULL,
+ "Failed to insert into tst with second name.");
+
+ return NULL;
+}
+
+char *test_search_exact()
+{
+ // tst returns the last one inserted
+ void *res = TSTree_search(node, bdata(&test1), blength(&test1));
+ mu_assert(res == valueA,
+ "Got the wrong value back, should get A not B.");
+
+ // tst does not find if not exact
+ res = TSTree_search(node, "TESTNO", strlen("TESTNO"));
+ mu_assert(res == NULL, "Should not find anything.");
+
+ return NULL;
+}
+
+char *test_search_prefix()
+{
+ void *res = TSTree_search_prefix(
+ node, bdata(&test2), blength(&test2));
+ debug("result: %p, expected: %p", res, valueA);
+ mu_assert(res == valueA, "Got wrong valueA by prefix.");
+
+
+ res = TSTree_search_prefix(node, bdata(&test3), blength(&test3));
+ debug("result: %p, expected: %p", res, valueA);
+ mu_assert(res == value4, "Got wrong value4 for prefix of 1.");
+
+ res = TSTree_search_prefix(node, "TE", strlen("TE"));
+ mu_assert(res == value4, "Should find for short prefix");
+
+ res = TSTree_search_prefix(node, "TE--", strlen("TE--"));
+ mu_assert(res == value4, "Should find for partial prefix.");
+
+ return NULL;
+}
+
+char *test_collect()
+{
+ DArray *arr = TSTree_collect(node, "TE", strlen("TE"));
+ mu_assert(DArray_count(arr) == 2, "Must collect TEST & TEST2");
+ DArray_destroy(arr);
+
+ arr = TSTree_collect(node, "T", strlen("T"));
+ mu_assert(DArray_count(arr) == 4, "Must collect all keys");
+ DArray_destroy(arr);
+
+ return NULL;
+}
+
+void TSTree_traverse_test_cb(void *value, void *data)
+{
+ assert(value != NULL && "Should not get NULL value.");
+ assert(data == valueA && "Expecting valueA as the data.");
+ traverse_count++;
+}
+
+char *test_traverse()
+{
+ traverse_count = 0;
+ TSTree_traverse(node, TSTree_traverse_test_cb, valueA);
+ debug("traverse count is: %d", traverse_count);
+ mu_assert(traverse_count == 4, "Didn't find 4 keys.");
+
+ return NULL;
+}
+
+char *test_destroy()
+{
+ TSTree_destroy(node);
+
+ return NULL;
+}
+
+char *all_tests()
+{
+ mu_suite_start();
+
+ mu_run_test(test_insert);
+ mu_run_test(test_search_exact);
+ mu_run_test(test_search_prefix);
+ mu_run_test(test_collect);
+ mu_run_test(test_traverse);
+ mu_run_test(test_destroy);
+
+ return NULL;
+}
+
+RUN_TESTS(all_tests);