From 836241c014be4c6b31c7f72244463bc220b55b4a Mon Sep 17 00:00:00 2001 From: Akos Horvath Date: Mon, 26 Feb 2024 16:39:22 +0100 Subject: [PATCH] add tests for tree edit distance and json parsing --- src/tests.c | 510 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 491 insertions(+), 19 deletions(-) diff --git a/src/tests.c b/src/tests.c index 4c28577..2921115 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1,11 +1,24 @@ -#include "util.h" -#include "client.h" -#include "wm.h" +#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "client.h" +#include "wm.h" + +typedef struct { + char logfilepath[PATH_MAX]; + char *jsonstr; + char *logjsonstr; +} TestGroup1State; // store a copy of the nodes created in test2_setup, to check if any // of the tests modified them @@ -323,6 +336,187 @@ static void test_wm_treenode_remove_client2(void **state) wm_treenode_free(client_node3); } +static void test_wm_ptrarray_2d_index(void **state) +{ + size_t x = 5; + size_t y = 10; + + UIntArray *uintarr = wm_uintarray_new(); + + _Static_assert(sizeof(uint64_t) == sizeof(uint64_t*), "static assert failed"); + for (size_t i = 0; i < x; i++) { + wm_uintarray_push(uintarr, (uint64_t)calloc(y, sizeof(uintptr_t))); + } + + for (size_t i = 0; i < uintarr->size; i++) { + for (size_t j = 0; j < y; j++) { + ((uint64_t**)uintarr->elements)[i][j] = i * j; + } + } + + for (size_t i = 0; i < uintarr->size; i++) { + for (size_t j = 0; j < y; j++) { + assert_int_equal(wm_uintarray_2d_index(uintarr, i, j), i * j); + } + } + + for (size_t i = 0; i < uintarr->size; i++) { + free((void*)uintarr->elements[i]); + } + + wm_uintarray_free(uintarr); +} + +static void test_wm_treenode_to_str(void **state) +{ + TreeNode *node = wm_treenode_new(NODE_CLIENT, (void*)54321); + node->pos.x = 1; + node->pos.y = 2; + node->pos.w = 3; + node->pos.h = 4; + node->id = 5; + node->client = &(Client) {.name = "client_name", .is_pid_set = true, .pid = 12341234}; + char *str = wm_treenode_to_str(node); + + assert_non_null(strstr(str, "\"type\":\"NODE_CLIENT\",")); + assert_non_null(strstr(str, "\"parent\":54321,")); + assert_non_null(strstr(str, "\"children\":[],")); + assert_non_null(strstr(str, "\"pos\":{")); + assert_non_null(strstr(str, "\"x\":1,")); + assert_non_null(strstr(str, "\"y\":2,")); + assert_non_null(strstr(str, "\"w\":3,")); + assert_non_null(strstr(str, "\"h\":4")); + assert_non_null(strstr(str, "\"client\":{")); + assert_non_null(strstr(str, "\"name\":\"client_name\"")); + assert_non_null(strstr(str, "\"is_pid_set\":true")); + assert_non_null(strstr(str, "\"pid\":12341234")); + assert_non_null(strstr(str, "\"id\":5")); + + wm_treenode_free(node); + free(str); +} + +static void test_wm_json_obj_to_treenode(void **state) +{ + TestGroup1State *_state = (TestGroup1State*)(*state); + struct json_object *parsed_json = json_tokener_parse(_state->jsonstr); + assert_non_null(parsed_json); + + TreeNode *node = wm_json_obj_to_treenode(parsed_json); + assert_int_equal(node->type, NODE_VERTICAL); + assert_int_equal(node->parent, 0); + assert_int_equal(node->children->size, 2); + assert_int_equal(node->pos.x, 1); + assert_int_equal(node->pos.y, 2); + assert_int_equal(node->pos.w, 3); + assert_int_equal(node->pos.h, 4); + assert_null(node->client); + + TreeNode *child1 = node->children->nodes[0]; + + assert_int_equal(child1->type, NODE_CLIENT); + // assert_int_equal(child1->parent, 0); + assert_int_equal(child1->children->size, 0); + assert_int_equal(child1->pos.x, 2); + assert_int_equal(child1->pos.y, 2); + assert_int_equal(child1->pos.w, 798); + assert_int_equal(child1->pos.h, 896); + assert_non_null(child1->client); + assert_string_equal(child1->client->name, "child1_client_name"); + assert_true(child1->client->is_pid_set); + assert_int_equal(child1->client->pid, 19058); + assert_int_equal(child1->id, 10); + + TreeNode *child2 = node->children->nodes[1]; + + assert_int_equal(child2->type, NODE_CLIENT); + // assert_int_equal(child2->parent, 0); + assert_int_equal(child2->children->size, 0); + assert_int_equal(child2->pos.x, 800); + assert_int_equal(child2->pos.y, 2); + assert_int_equal(child2->pos.w, 798); + assert_int_equal(child2->pos.h, 896); + assert_non_null(child2->client); + assert_string_equal(child2->client->name, "child2_client_name"); + assert_true(child2->client->is_pid_set); + assert_int_equal(child2->client->pid, 19063); + assert_int_equal(child2->id, 11); + + json_object_put(parsed_json); + + free(node->children->nodes[0]->client->name); + free(node->children->nodes[0]->client); + wm_treenode_free(node->children->nodes[0]); + + free(node->children->nodes[1]->client->name); + free(node->children->nodes[1]->client); + wm_treenode_free(node->children->nodes[1]); + + wm_treenode_free(node); +} + +static void test_wm_read_log(void **state) +{ + TestGroup1State *_state = (TestGroup1State*)(*state); + + UIntArray *entries = wm_read_log(_state->logfilepath); + assert_int_equal(entries->size, 1); + + LogEntry *entry = (void*)wm_uintarray_at(entries, 0); + assert_int_equal(entry->timestamp, 1705601049); + assert_string_equal(entry->function_name, "wm_kb_spawn"); + assert_true(entry->after); + + // TODO check nodes + + free(entry->workspaces->nodes[2]->children->nodes[0]->client->name); + free(entry->workspaces->nodes[2]->children->nodes[0]->client); + wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[0]); + + free(entry->workspaces->nodes[2]->children->nodes[1]->client->name); + free(entry->workspaces->nodes[2]->children->nodes[1]->client); + wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[1]); + + wm_treenode_free(entry->workspaces->nodes[2]); + wm_nodearray_free(entry->workspaces); + free(entry); + wm_uintarray_free(entries); +} + +uint64_t simple_update_cost_function(TreeNode* node1, TreeNode* node2) +{ + return 10; +} + +static void test_wm_logentries_calculate_distances(void **state) +{ + TestGroup1State *_state = (TestGroup1State*)(*state); + + UIntArray *entries = wm_read_log(_state->logfilepath); + + TreeNode *node = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *child = wm_treenode_new(NODE_CLIENT, NULL); + wm_treenode_add_child(node, child); + + wm_logentries_calculate_distances(entries, node, &simple_update_cost_function); + + LogEntry *entry = (void*)wm_uintarray_at(entries, 0); + + // TODO check node distances + + free(entry->workspaces->nodes[2]->children->nodes[0]->client->name); + free(entry->workspaces->nodes[2]->children->nodes[0]->client); + + free(entry->workspaces->nodes[2]->children->nodes[1]->client->name); + free(entry->workspaces->nodes[2]->children->nodes[1]->client); + + wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[0]); + wm_treenode_free(entry->workspaces->nodes[2]->children->nodes[1]); + wm_logentries_free(entries); + wm_treenode_free(node); + wm_treenode_free(child); +} + static void test_wm_postorder_traversal(void **state) { TreeNode *node1 = (TreeNode*)*state; @@ -392,14 +586,14 @@ static void test_wm_is_treenode_keyroot(void **state) assert_non_null(postorder); assert_int_equal(postorder->size, 8); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[0]), false); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[1]), true); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[2]), true); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[3]), false); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[4]), false); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[5]), true); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[6]), true); - assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[7]), true); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[0], postorder), false); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[1], postorder), true); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[2], postorder), true); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[3], postorder), false); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[4], postorder), false); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[5], postorder), true); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[6], postorder), true); + assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[7], postorder), true); wm_nodearray_free(postorder); } @@ -425,10 +619,59 @@ static void test_wm_treenode_all_lmds(void **state) wm_nodearray_free(lmds); } +static void test_wm_treenode_all_lmds_index(void **state) +{ + TreeNode *node1 = (TreeNode*)*state; + + NodeArray *postorder = wm_postorder_traversal(node1); + UIntArray *lmds = wm_treenode_all_lmds_index(node1); + + assert_non_null(lmds); + + assert_int_equal(lmds->size, 8); + + assert_int_equal(wm_uintarray_at(lmds, 0), 0); + assert_int_equal(wm_uintarray_at(lmds, 1), 1); + assert_int_equal(wm_uintarray_at(lmds, 2), 2); + assert_int_equal(wm_uintarray_at(lmds, 3), 0); + assert_int_equal(wm_uintarray_at(lmds, 4), 4); + assert_int_equal(wm_uintarray_at(lmds, 5), 5); + assert_int_equal(wm_uintarray_at(lmds, 6), 4); + assert_int_equal(wm_uintarray_at(lmds, 7), 0); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 0)], + wm_treenode_lmd(postorder->nodes[0])); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 1)], + wm_treenode_lmd(postorder->nodes[1])); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 2)], + wm_treenode_lmd(postorder->nodes[2])); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 3)], + wm_treenode_lmd(postorder->nodes[3])); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 4)], + wm_treenode_lmd(postorder->nodes[4])); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 5)], + wm_treenode_lmd(postorder->nodes[5])); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 6)], + wm_treenode_lmd(postorder->nodes[6])); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(lmds, 7)], + wm_treenode_lmd(postorder->nodes[7])); + + wm_nodearray_free(postorder); + wm_uintarray_free(lmds); +} + static void test_wm_treenode_all_keyroots(void **state) { TreeNode *node1 = (TreeNode*)*state; + NodeArray *postorder = wm_postorder_traversal(node1); NodeArray *keyroots = wm_treenode_all_keyroots(node1); assert_non_null(keyroots); @@ -440,9 +683,119 @@ static void test_wm_treenode_all_keyroots(void **state) assert_int_equal(keyroots->nodes[3]->id, 3); assert_int_equal(keyroots->nodes[4]->id, 1); + assert_true(wm_is_treenode_keyroot(keyroots->nodes[0], postorder)); + assert_true(wm_is_treenode_keyroot(keyroots->nodes[1], postorder)); + assert_true(wm_is_treenode_keyroot(keyroots->nodes[2], postorder)); + assert_true(wm_is_treenode_keyroot(keyroots->nodes[3], postorder)); + assert_true(wm_is_treenode_keyroot(keyroots->nodes[4], postorder)); + + wm_nodearray_free(postorder); wm_nodearray_free(keyroots); } +static void test_wm_treenode_all_keyroots_index(void **state) +{ + TreeNode *node1 = (TreeNode*)*state; + + NodeArray *postorder = wm_postorder_traversal(node1); + UIntArray *keyroot_indexes = wm_treenode_all_keyroots_index(node1); + + assert_non_null(keyroot_indexes); + assert_int_equal(keyroot_indexes->size, 5); + + assert_int_equal(keyroot_indexes->elements[0], 1); + assert_int_equal(keyroot_indexes->elements[1], 2); + assert_int_equal(keyroot_indexes->elements[2], 5); + assert_int_equal(keyroot_indexes->elements[3], 6); + assert_int_equal(keyroot_indexes->elements[4], 7); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 0)], + postorder->nodes[1]); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 1)], + postorder->nodes[2]); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 2)], + postorder->nodes[5]); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 3)], + postorder->nodes[6]); + + assert_ptr_equal(postorder->nodes[wm_uintarray_at(keyroot_indexes, 4)], + postorder->nodes[7]); + + assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at( + keyroot_indexes, 0)], postorder)); + + assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at( + keyroot_indexes, 1)], postorder)); + + assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at( + keyroot_indexes, 2)], postorder)); + + assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at( + keyroot_indexes, 3)], postorder)); + + assert_true(wm_is_treenode_keyroot(postorder->nodes[wm_uintarray_at( + keyroot_indexes, 4)], postorder)); + + wm_nodearray_free(postorder); + wm_uintarray_free(keyroot_indexes); +} + +static void test_wm_tree_edit_distance(void **state) +{ + TreeNode *a_root = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *a_child1 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *a_child2 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *a_child3 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *a_child4 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *a_child5 = wm_treenode_new(NODE_VERTICAL, NULL); + + TreeNode *b_root = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *b_child1 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *b_child2 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *b_child3 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *b_child4 = wm_treenode_new(NODE_VERTICAL, NULL); + TreeNode *b_child5 = wm_treenode_new(NODE_VERTICAL, NULL); + + wm_treenode_add_child(a_root, a_child1); + wm_treenode_add_child(a_root, a_child2); + + wm_treenode_add_child(a_child1, a_child3); + wm_treenode_add_child(a_child1, a_child4); + + wm_treenode_add_child(a_child4, a_child5); + + wm_treenode_add_child(b_root, b_child1); + wm_treenode_add_child(b_root, b_child2); + + wm_treenode_add_child(b_child1, b_child3); + wm_treenode_add_child(b_child3, b_child4); + wm_treenode_add_child(b_child3, b_child5); + + size_t dist = wm_tree_edit_distance(a_root, b_root, (TreeEditDistanceCosts) { + .insert_cost = 10, + .remove_cost = 10, + .update_cost_function = &simple_update_cost_function, + }); + + assert_int_equal(dist, 70); + + wm_treenode_free(a_root); + wm_treenode_free(a_child1); + wm_treenode_free(a_child2); + wm_treenode_free(a_child3); + wm_treenode_free(a_child4); + wm_treenode_free(a_child5); + wm_treenode_free(b_root); + wm_treenode_free(b_child1); + wm_treenode_free(b_child2); + wm_treenode_free(b_child3); + wm_treenode_free(b_child4); + wm_treenode_free(b_child5); +} + static int test2_setup(void **state) { TreeNode *node1 = wm_treenode_new(NODE_VERTICAL, NULL); @@ -463,13 +816,13 @@ static int test2_setup(void **state) assert_non_null(node7); assert_non_null(node8); - wm_nodearray_push(node1->children, node2); - wm_nodearray_push(node1->children, node3); - wm_nodearray_push(node2->children, node4); - wm_nodearray_push(node2->children, node5); - wm_nodearray_push(node2->children, node6); - wm_nodearray_push(node3->children, node7); - wm_nodearray_push(node3->children, node8); + wm_treenode_add_child(node1, node2); + wm_treenode_add_child(node1, node3); + wm_treenode_add_child(node2, node4); + wm_treenode_add_child(node2, node5); + wm_treenode_add_child(node2, node6); + wm_treenode_add_child(node3, node7); + wm_treenode_add_child(node3, node8); node1->id = 1; node2->id = 2; @@ -543,6 +896,117 @@ static int test2_teardown(void **state) return 0; } +static int test1_setup(void **state) +{ + *state = calloc(1, sizeof(TestGroup1State)); + TestGroup1State *_state = (TestGroup1State*)(*state); + _state->jsonstr = "{" + "\"type\":\"NODE_VERTICAL\"," + "\"parent\":0," + "\"children\":[" + "{" + "\"type\":\"NODE_CLIENT\"," + "\"parent\":105965433127232," + "\"children\":[]," + "\"pos\":{" + "\"x\":2," + "\"y\":2," + "\"w\":798," + "\"h\":896" + "}," + "\"client\":{" + "\"address\":106102872080672," + "\"name\":\"child1_client_name\"," + "\"is_pid_set\":true," + "\"pid\":19058" + "}," + "\"id\":10" + "}," + "{" + "\"type\":\"NODE_CLIENT\"," + "\"parent\":105965433127232," + "\"children\":[]," + "\"pos\":{" + "\"x\":800," + "\"y\":2," + "\"w\":798," + "\"h\":896" + "}," + "\"client\":{" + "\"address\":106102872080800," + "\"name\":\"child2_client_name\"," + "\"is_pid_set\":true," + "\"pid\":19063" + "}," + "\"id\":11" + "}" + "]," + "\"pos\":{" + "\"x\":1," + "\"y\":2," + "\"w\":3," + "\"h\":4" + "}," + "\"client\":{" + "\"address\":1234," + "\"name\":\"client_name\"," + "\"is_pid_set\":true," + "\"pid\":1234" + "}," + "\"id\":5" + "}"; + + size_t logjsonstr_size = strlen(_state->jsonstr) + 1024; + _state->logjsonstr = calloc(logjsonstr_size, 1); + snprintf(_state->logjsonstr, logjsonstr_size, "{\"1705601049:wm_kb_spawn:after\": {" + "\"2\":" + "%s" + "}}", _state->jsonstr); + + const char *basedir = "./"; + assert_true(access(basedir, W_OK) >= 0); + + srand(time(NULL)); + bool ok = false; + int tries = 0; + + while (!ok && tries < 1000) { + snprintf(_state->logfilepath, PATH_MAX, "%s%ld.json", basedir, random()); + + if (access(_state->logfilepath, F_OK) < 0) + ok = true; + tries++; + } + + if (!ok) + return 1; + + int fd = creat(_state->logfilepath, 0664); + if (fd < 0) perror("creat"); + assert_true(fd >= 0); + + assert_true(write(fd, _state->logjsonstr, strlen(_state->logjsonstr)) > 0); + assert_true(close(fd) >= 0); + + return 0; +} + +static int test1_teardown(void **state) +{ + TestGroup1State *_state = (TestGroup1State*)(*state); + int ret = unlink(_state->logfilepath); + + if (ret < 0) { + perror("unlink"); + return 1; + } + + free(_state->logjsonstr); + free(*state); + + return 0; +} + int main(void) { const struct CMUnitTest test_group1[] = { @@ -553,6 +1017,11 @@ int main(void) cmocka_unit_test(test_wm_find_client_nodes), cmocka_unit_test(test_wm_treenode_remove_client1), cmocka_unit_test(test_wm_treenode_remove_client2), + cmocka_unit_test(test_wm_ptrarray_2d_index), + cmocka_unit_test(test_wm_treenode_to_str), + cmocka_unit_test(test_wm_json_obj_to_treenode), + cmocka_unit_test(test_wm_read_log), + cmocka_unit_test(test_wm_logentries_calculate_distances), }; const struct CMUnitTest test_group2[] = { @@ -560,9 +1029,12 @@ int main(void) cmocka_unit_test(test_wm_treenode_lmd), cmocka_unit_test(test_wm_is_treenode_keyroot), cmocka_unit_test(test_wm_treenode_all_lmds), + cmocka_unit_test(test_wm_treenode_all_lmds_index), cmocka_unit_test(test_wm_treenode_all_keyroots), + cmocka_unit_test(test_wm_treenode_all_keyroots_index), + cmocka_unit_test(test_wm_tree_edit_distance), }; - cmocka_run_group_tests(test_group1, NULL, NULL); + cmocka_run_group_tests(test_group1, test1_setup, test1_teardown); cmocka_run_group_tests(test_group2, test2_setup, test2_teardown); }