add tests for tree edit distance and json parsing

This commit is contained in:
Akos Horvath 2024-02-26 16:39:22 +01:00
parent 42779798fa
commit 836241c014

View File

@ -1,11 +1,24 @@
#include "util.h" #include <stdint.h>
#include "client.h"
#include "wm.h"
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <setjmp.h> #include <setjmp.h>
#include <cmocka.h> #include <cmocka.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#include <json-c/json_tokener.h>
#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 // store a copy of the nodes created in test2_setup, to check if any
// of the tests modified them // of the tests modified them
@ -323,6 +336,187 @@ static void test_wm_treenode_remove_client2(void **state)
wm_treenode_free(client_node3); 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) static void test_wm_postorder_traversal(void **state)
{ {
TreeNode *node1 = (TreeNode*)*state; TreeNode *node1 = (TreeNode*)*state;
@ -392,14 +586,14 @@ static void test_wm_is_treenode_keyroot(void **state)
assert_non_null(postorder); assert_non_null(postorder);
assert_int_equal(postorder->size, 8); 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[0], postorder), false);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[1]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[1], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[2]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[2], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[3]), false); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[3], postorder), false);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[4]), false); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[4], postorder), false);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[5]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[5], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[6]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[6], postorder), true);
assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[7]), true); assert_int_equal(wm_is_treenode_keyroot(postorder->nodes[7], postorder), true);
wm_nodearray_free(postorder); wm_nodearray_free(postorder);
} }
@ -425,10 +619,59 @@ static void test_wm_treenode_all_lmds(void **state)
wm_nodearray_free(lmds); 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) static void test_wm_treenode_all_keyroots(void **state)
{ {
TreeNode *node1 = (TreeNode*)*state; TreeNode *node1 = (TreeNode*)*state;
NodeArray *postorder = wm_postorder_traversal(node1);
NodeArray *keyroots = wm_treenode_all_keyroots(node1); NodeArray *keyroots = wm_treenode_all_keyroots(node1);
assert_non_null(keyroots); 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[3]->id, 3);
assert_int_equal(keyroots->nodes[4]->id, 1); 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); 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) static int test2_setup(void **state)
{ {
TreeNode *node1 = wm_treenode_new(NODE_VERTICAL, NULL); 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(node7);
assert_non_null(node8); assert_non_null(node8);
wm_nodearray_push(node1->children, node2); wm_treenode_add_child(node1, node2);
wm_nodearray_push(node1->children, node3); wm_treenode_add_child(node1, node3);
wm_nodearray_push(node2->children, node4); wm_treenode_add_child(node2, node4);
wm_nodearray_push(node2->children, node5); wm_treenode_add_child(node2, node5);
wm_nodearray_push(node2->children, node6); wm_treenode_add_child(node2, node6);
wm_nodearray_push(node3->children, node7); wm_treenode_add_child(node3, node7);
wm_nodearray_push(node3->children, node8); wm_treenode_add_child(node3, node8);
node1->id = 1; node1->id = 1;
node2->id = 2; node2->id = 2;
@ -543,6 +896,117 @@ static int test2_teardown(void **state)
return 0; 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) int main(void)
{ {
const struct CMUnitTest test_group1[] = { 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_find_client_nodes),
cmocka_unit_test(test_wm_treenode_remove_client1), cmocka_unit_test(test_wm_treenode_remove_client1),
cmocka_unit_test(test_wm_treenode_remove_client2), 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[] = { const struct CMUnitTest test_group2[] = {
@ -560,9 +1029,12 @@ int main(void)
cmocka_unit_test(test_wm_treenode_lmd), cmocka_unit_test(test_wm_treenode_lmd),
cmocka_unit_test(test_wm_is_treenode_keyroot), cmocka_unit_test(test_wm_is_treenode_keyroot),
cmocka_unit_test(test_wm_treenode_all_lmds), 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),
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); cmocka_run_group_tests(test_group2, test2_setup, test2_teardown);
} }