tests/test_tree.c

branch
feature/tree_add
changeset 871
e29c1f96646d
parent 866
1f636de4a63f
--- a/tests/test_tree.c	Tue Aug 20 13:53:18 2024 +0200
+++ b/tests/test_tree.c	Tue Aug 20 18:01:03 2024 +0200
@@ -60,37 +60,29 @@
 
 static void *create_tree_node_file(
         void const *dptr,
-        void const *nptr,
         void *allocator) {
     if (allocator == NULL) allocator = cxDefaultAllocator;
 
-    char const *data = dptr;
-    tree_node_file const *existing = nptr;
-
-    if (existing != NULL && strcmp(existing->path, data) == 0) {
-        return (void *) existing;
-    } else {
-        tree_node_file *node = cxMalloc(allocator, sizeof(tree_node_file));
-
-        node->path = data;
+    tree_node_file *node = cxMalloc(allocator, sizeof(tree_node_file));
+    node->path = dptr;
 
-        // intentionally write garbage into the pointers, it's part of the test
-        node->parent = (void *) 0xf00ba1;
-        node->next = (void *) 0xf00ba2;
-        node->prev = (void *) 0xf00ba3;
-        node->children = (void *) 0xf00ba4;
-        node->last_child = (void *) 0xf00ba5;
+    // intentionally write garbage into the pointers, it's part of the test
+    node->parent = (void *) 0xf00ba1;
+    node->next = (void *) 0xf00ba2;
+    node->prev = (void *) 0xf00ba3;
+    node->children = (void *) 0xf00ba4;
+    node->last_child = (void *) 0xf00ba5;
 
-        return node;
-    }
+    return node;
 }
 
-static int tree_node_file_search(void const *nptr, void const *data) {
-    tree_node_file const *node = nptr;
-    size_t len1 = strlen(node->path);
-    size_t len2 = strlen(data);
+static int tree_node_file_search(void const *l, void const *r) {
+    tree_node_file const *left = l;
+    tree_node_file const *right = r;
+    size_t len1 = strlen(left->path);
+    size_t len2 = strlen(right->path);
     if (len1 <= len2) {
-        if (memcmp(node->path, data, len1) == 0) {
+        if (memcmp(left->path, right->path, len1) == 0) {
             return (int) (len2 - len1);
         } else {
             return -1;
@@ -429,38 +421,38 @@
     CX_TEST_DO {
         for (unsigned i = 0 ; i <= 10 ; i++) {
             s = testdata[i];
-            r = cx_tree_search(&root, &s, test_tree_search_function,
+            r = cx_tree_search_data(&root, &s, test_tree_search_function,
                                (void **) &n, tree_children(tree_node));
             CX_TEST_ASSERT(r == 0);
             CX_TEST_ASSERT(n == testnodes[i]);
         }
 
         s = -5;
-        r = cx_tree_search(&root, &s, test_tree_search_function,
+        r = cx_tree_search_data(&root, &s, test_tree_search_function,
                            (void **) &n, tree_children(tree_node));
         CX_TEST_ASSERT(r < 0);
         CX_TEST_ASSERT(n == NULL);
 
         s = 26;
-        r = cx_tree_search(&root, &s, test_tree_search_function,
+        r = cx_tree_search_data(&root, &s, test_tree_search_function,
                            (void **) &n, tree_children(tree_node));
         CX_TEST_ASSERT(r > 0);
         CX_TEST_ASSERT(n == &ba);
 
         s = 35;
-        r = cx_tree_search(&root, &s, test_tree_search_function,
+        r = cx_tree_search_data(&root, &s, test_tree_search_function,
                            (void **) &n, tree_children(tree_node));
         CX_TEST_ASSERT(r > 0);
         CX_TEST_ASSERT(n == &cb);
 
         s = 38;
-        r = cx_tree_search(&root, &s, test_tree_search_function,
+        r = cx_tree_search_data(&root, &s, test_tree_search_function,
                            (void **) &n, tree_children(tree_node));
         CX_TEST_ASSERT(r > 0);
         CX_TEST_ASSERT(n == &cba);
 
         s = 42;
-        r = cx_tree_search(&root, &s, test_tree_search_function,
+        r = cx_tree_search_data(&root, &s, test_tree_search_function,
                            (void **) &n, tree_children(tree_node));
         CX_TEST_ASSERT(r > 0);
         CX_TEST_ASSERT(n == &cc);
@@ -1064,60 +1056,58 @@
     cx_tree_link(&usr, &lib, tree_node_file_layout);
 
     CX_TEST_DO {
-        void *newroot = &root;
-
-        tree_node_file *foo = cx_tree_add(
+        tree_node_file *foo;
+        int result;
+        result = cx_tree_add(
                 "/home/foo/",
                 tree_node_file_search,
                 create_tree_node_file, alloc,
-                &newroot, tree_node_file_layout
+                (void **) &foo, &root,
+                tree_node_file_layout
         );
-        CX_TEST_ASSERT(newroot == &root);
-        tree_node_file *bar = cx_tree_add(
-                "/home/foo/bar/",
+        CX_TEST_ASSERT(result == 0);
+        CX_TEST_ASSERT(foo != NULL);
+        char const *bar_path = "/home/foo/bar/";
+        void *failed;
+        size_t added = cx_tree_add_array(
+                bar_path, 1, sizeof(char const *),
                 tree_node_file_search,
                 create_tree_node_file, alloc,
-                &newroot, tree_node_file_layout
+                &failed, &root,
+                tree_node_file_layout
         );
-        CX_TEST_ASSERT(newroot == &root);
-
+        CX_TEST_ASSERT(added == 1);
+        CX_TEST_ASSERT(failed == NULL);
+        tree_node_file *bar = foo->children;
+        CX_TEST_ASSERT(bar != NULL);
         CX_TEST_ASSERT(bar->parent == foo);
+        CX_TEST_ASSERT(bar->path == bar_path);
         CX_TEST_ASSERT(foo->parent == &home);
 
-        CX_TEST_ASSERT(talloc.alloc_total == 2);
+        tree_node_file *new_node;
+        result = cx_tree_add(
+                "newroot",
+                tree_node_file_search,
+                create_tree_node_file, alloc,
+                (void **) &new_node, &root,
+                tree_node_file_layout
+        );
+        CX_TEST_ASSERT(0 != result);
+        CX_TEST_ASSERT(NULL != new_node);
+        CX_TEST_ASSERT(new_node->children == NULL);
+        CX_TEST_ASSERT(new_node->parent == NULL);
 
-        cxFree(&talloc.base, foo);
-        cxFree(&talloc.base, bar);
+        CX_TEST_ASSERT(talloc.alloc_total == 3);
+
+        cxFree(alloc, foo);
+        cxFree(alloc, bar);
+        cxFree(alloc, new_node);
 
         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
     }
     cx_testing_allocator_destroy(&talloc);
 }
 
-CX_TEST(test_tree_add_one_to_empty_tree) {
-    tree_node_file *node = NULL;
-    CX_TEST_DO {
-        tree_node_file *root = NULL;
-        char const *data = "/home/foo/bar/";
-
-        node = cx_tree_add(
-                data,
-                tree_node_file_search,
-                create_tree_node_file, NULL,
-                (void **) &root, tree_node_file_layout
-        );
-
-        CX_TEST_ASSERT(root != NULL);
-        CX_TEST_ASSERT(root->path == data);
-        CX_TEST_ASSERT(root->next == NULL);
-        CX_TEST_ASSERT(root->prev == NULL);
-        CX_TEST_ASSERT(root->children == NULL);
-        CX_TEST_ASSERT(root->last_child == NULL);
-        CX_TEST_ASSERT(root->parent == NULL);
-    }
-    free(node);
-}
-
 CX_TEST(test_tree_add_one_existing) {
     CxTestingAllocator talloc;
     cx_testing_allocator_init(&talloc);
@@ -1136,28 +1126,109 @@
     cx_tree_link(&usr, &lib, tree_node_file_layout);
 
     CX_TEST_DO {
-        void *newroot = &root;
-
         tree_node_file *node;
-        node = cx_tree_add(
+        int result = cx_tree_add(
                 "/usr/lib/",
                 tree_node_file_search,
                 create_tree_node_file, alloc,
-                &newroot, tree_node_file_layout
+                (void **) &node, &root,
+                tree_node_file_layout
+        );
+        CX_TEST_ASSERT(result == 0);
+        CX_TEST_ASSERT(node != &lib);
+        CX_TEST_ASSERT(node->prev == &lib);
+        CX_TEST_ASSERT(lib.next == node);
+        CX_TEST_ASSERT(node->parent == &usr);
+        CX_TEST_ASSERT(talloc.alloc_total == 1);
+        cxFree(alloc, node);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
+
+CX_TEST(test_tree_add_one_no_match) {
+    tree_node_file root = {0};
+    root.path = "/mnt/";
+
+    CX_TEST_DO {
+        tree_node_file *node = NULL;
+        int result = cx_tree_add(
+                "/usr/lib/",
+                tree_node_file_search,
+                create_tree_node_file, NULL,
+                (void **) &node, &root,
+                tree_node_file_layout
         );
-        CX_TEST_ASSERT(newroot == &root);
-        CX_TEST_ASSERT(node == &lib);
+        CX_TEST_ASSERT(result != 0);
+        CX_TEST_ASSERT(node != NULL);
+        CX_TEST_ASSERT(node->parent == NULL);
+        CX_TEST_ASSERT(node->children == NULL);
+        free(node);
+        node = NULL;
+        size_t added = cx_tree_add_array(
+                "/", 1, sizeof(char const *),
+                tree_node_file_search,
+                create_tree_node_file, NULL,
+                (void **) &node, &root,
+                tree_node_file_layout
+        );
+        CX_TEST_ASSERT(added == 0);
+        CX_TEST_ASSERT(node != NULL);
+        CX_TEST_ASSERT(node->parent == NULL);
+        CX_TEST_ASSERT(node->children == NULL);
+        free(node);
+    }
+}
 
-        node = cx_tree_add(
-                "/home/",
+CX_TEST(test_tree_add_duplicate_root) {
+    tree_node_file root = {0};
+    root.path = "/";
+    CX_TEST_DO {
+        tree_node_file *node;
+        int result = cx_tree_add(
+                "/",
+                tree_node_file_search,
+                create_tree_node_file, NULL,
+                (void **) &node, &root,
+                tree_node_file_layout
+        );
+        CX_TEST_ASSERT(result == 0);
+        CX_TEST_ASSERT(root.children == node);
+        CX_TEST_ASSERT(node->parent == &root);
+    }
+}
+
+CX_TEST(test_tree_add_zero) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CxAllocator *alloc = &talloc.base;
+
+    tree_node_file root = {0};
+    root.path = "/";
+    CX_TEST_DO {
+        void *failed;
+
+        size_t processed = cx_tree_add_array(
+                (void *) 0xc0ffee, 0, sizeof(void *),
                 tree_node_file_search,
                 create_tree_node_file, alloc,
-                &newroot, tree_node_file_layout
+                &failed, &root, tree_node_file_layout
         );
-        CX_TEST_ASSERT(newroot == &root);
-        CX_TEST_ASSERT(node == &home);
+        CX_TEST_ASSERT(failed == NULL);
+        CX_TEST_ASSERT(processed == 0);
+        CX_TEST_ASSERT(talloc.alloc_total == 0);
 
+        CxIterator iter = cxIterator(NULL, sizeof(void *), 0);
+        processed = cx_tree_add_iter(
+                cxIteratorRef(iter),
+                tree_node_file_search,
+                create_tree_node_file, alloc,
+                &failed, &root, tree_node_file_layout
+        );
+        CX_TEST_ASSERT(processed == 0);
+        CX_TEST_ASSERT(failed == NULL);
         CX_TEST_ASSERT(talloc.alloc_total == 0);
+
         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
     }
     cx_testing_allocator_destroy(&talloc);
@@ -1183,7 +1254,7 @@
     cx_tree_link(&usr, &lib, tree_node_file_layout);
 
     CX_TEST_DO {
-        void *newroot = &root;
+        void *failed;
 
         char const *paths[] = {
                 "/home/foo/",
@@ -1196,10 +1267,10 @@
                 paths, 4, sizeof(char const *),
                 tree_node_file_search,
                 create_tree_node_file, alloc,
-                &newroot, tree_node_file_layout
+                &failed, &root, tree_node_file_layout
         );
-        CX_TEST_ASSERT(newroot == &root);
 
+        CX_TEST_ASSERT(failed == NULL);
         CX_TEST_ASSERT(processed == 4);
         CX_TEST_ASSERT(talloc.alloc_total == 4);
 
@@ -1219,83 +1290,180 @@
         tree_node_file *libfoo = lib.children;
         CX_TEST_ASSERT(libfoo != NULL && libfoo->path == paths[3]);
 
-        cxFree(&talloc.base, foo);
-        cxFree(&talloc.base, bar);
-        cxFree(&talloc.base, lib64);
-        cxFree(&talloc.base, libfoo);
+        cxFree(alloc, foo);
+        cxFree(alloc, bar);
+        cxFree(alloc, lib64);
+        cxFree(alloc, libfoo);
+
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
+
+CX_TEST(test_tree_add_many_with_dupl_and_no_match) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CxAllocator *alloc = &talloc.base;
+
+    tree_node_file root = {0};
+    root.path = "/mnt/";
+
+    CX_TEST_DO {
+        tree_node_file *failed;
+
+        char const *paths[] = {
+                "/mnt/sdcard/",
+                "/mnt/foo/",
+                "/mnt/sdcard/",
+                "/home/",
+                "/usr/"
+        };
+
+        size_t processed = cx_tree_add_array(
+                paths, 5, sizeof(char const *),
+                tree_node_file_search,
+                create_tree_node_file, alloc,
+                (void **) &failed, &root, tree_node_file_layout
+        );
+
+        CX_TEST_ASSERT(processed == 3);
+        CX_TEST_ASSERT(failed != NULL);
+        CX_TEST_ASSERT(failed->parent == NULL);
+        CX_TEST_ASSERT(failed->children == NULL);
+        CX_TEST_ASSERT(strcmp(failed->path, "/home/") == 0);
+        cxFree(alloc, failed);
+
+        CX_TEST_ASSERT(root.children != root.last_child);
+        CX_TEST_ASSERT(strcmp(root.children->path, "/mnt/sdcard/") == 0);
+        CX_TEST_ASSERT(strcmp(root.last_child->path, "/mnt/sdcard/") == 0);
+        CX_TEST_ASSERT(strcmp(root.children->next->path, "/mnt/foo/") == 0);
+        CX_TEST_ASSERT(strcmp(root.last_child->prev->path, "/mnt/foo/") == 0);
+
+        CxTreeIterator iter = cx_tree_iterator(
+                &root, true,
+                offsetof(tree_node_file, children),
+                offsetof(tree_node_file, next)
+        );
+        cx_foreach(tree_node_file *, node, iter) {
+            if (iter.exiting) {
+                if (node != &root) {
+                    cxFree(alloc, node);
+                }
+            }
+        }
 
         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
     }
     cx_testing_allocator_destroy(&talloc);
 }
 
-CX_TEST(test_tree_add_many_skip_duplicates) {
-    // adds many elements to an existing tree
+static CX_TEST_SUBROUTINE(test_tree_add_create_from_array_impl,
+                          CxAllocator *alloc, char const **paths) {
+    tree_node_file root = {0};
+    root.path = "/";
+
+    void *failed;
+    size_t processed = cx_tree_add_array(
+            paths, 10, sizeof(char const *),
+            tree_node_file_search,
+            create_tree_node_file, alloc,
+            &failed, &root, tree_node_file_layout
+    );
+
+    CX_TEST_ASSERT(failed == NULL);
+    CX_TEST_ASSERT(processed == 10);
+
+    char const *exp_order[] = {
+            "/",
+            "/usr/",
+            "/usr/lib/",
+            "/usr/lib/libbumm.so",
+            "/home/",
+            "/home/foo/",
+            "/var/",
+            "/var/www/",
+            "/var/www/vhosts/",
+            "/var/www/vhosts/live/",
+            "/var/www/vhosts/live/htdocs/"
+    };
+    unsigned exp_depth[] = {
+            1,
+            2,
+            3,
+            4,
+            2,
+            3,
+            2,
+            3,
+            4,
+            5,
+            6
+    };
+
+    CxTreeIterator iter = cx_tree_iterator(
+            &root, true,
+            offsetof(tree_node_file, children),
+            offsetof(tree_node_file, next)
+    );
+    cx_foreach(tree_node_file *, node, iter) {
+        if (iter.exiting) {
+            if (node != &root) {
+                cxFree(alloc, node);
+            }
+        } else {
+            CX_TEST_ASSERT(iter.counter <= 11);
+            CX_TEST_ASSERT(iter.depth == exp_depth[iter.counter - 1]);
+            CX_TEST_ASSERT(strcmp(node->path, exp_order[iter.counter - 1]) == 0);
+        }
+    }
+}
+
+CX_TEST(test_tree_add_create_from_array) {
+    // creates an entirely new tree from an array
+
     CxTestingAllocator talloc;
     cx_testing_allocator_init(&talloc);
     CxAllocator *alloc = &talloc.base;
 
-    tree_node_file root = {0};
-    root.path = "/";
-    tree_node_file usr = {0};
-    usr.path = "/usr/";
-    cx_tree_link(&root, &usr, tree_node_file_layout);
-    tree_node_file home = {0};
-    home.path = "/home/";
-    cx_tree_link(&root, &home, tree_node_file_layout);
-    tree_node_file lib = {0};
-    lib.path = "/usr/lib/";
-    cx_tree_link(&usr, &lib, tree_node_file_layout);
-
     CX_TEST_DO {
-        void *newroot = &root;
-
         char const *paths[] = {
-                "/home/foo/",
-                "/home/foo/bar",
+                "/usr/",
+                "/home/",
                 "/usr/lib/",
-                "/usr/lib/foo.so"
+                "/usr/lib/libbumm.so",
+                "/var/",
+                "/var/www/",
+                "/var/www/vhosts/",
+                "/var/www/vhosts/live/",
+                "/var/www/vhosts/live/htdocs/",
+                "/home/foo/"
         };
 
-        size_t processed = cx_tree_add_array(
-                paths, 4, sizeof(char const *),
-                tree_node_file_search,
-                create_tree_node_file, alloc,
-                &newroot, tree_node_file_layout
-        );
-        CX_TEST_ASSERT(newroot == &root);
-
-        CX_TEST_ASSERT(processed == 4);
-        CX_TEST_ASSERT(talloc.alloc_total == 3);
+        char const *scrambled_paths[] = {
+                "/usr/",
+                "/home/",
+                "/var/www/vhosts/live/",
+                "/usr/lib/",
+                "/var/",
+                "/var/www/",
+                "/usr/lib/libbumm.so",
+                "/var/www/vhosts/",
+                "/var/www/vhosts/live/htdocs/",
+                "/home/foo/"
+        };
 
-        CX_TEST_ASSERT(home.children == home.last_child);
-        tree_node_file *foo = home.children;
-        CX_TEST_ASSERT(foo != NULL && foo->path == paths[0]);
-        CX_TEST_ASSERT(foo->children == foo->last_child);
-        tree_node_file *bar = foo->children;
-        CX_TEST_ASSERT(bar != NULL && bar->path == paths[1]);
-        CX_TEST_ASSERT(usr.children == usr.last_child);
-        CX_TEST_ASSERT(usr.children == &lib);
-        CX_TEST_ASSERT(lib.children != NULL);
-        tree_node_file *libfoo = lib.children;
-        CX_TEST_ASSERT(libfoo != NULL && libfoo->path == paths[3]);
-
-        cxFree(&talloc.base, foo);
-        cxFree(&talloc.base, bar);
-        cxFree(&talloc.base, libfoo);
+        // no matter how the original array is sorted,
+        // the resulting tree should be the same
+        CX_TEST_CALL_SUBROUTINE(test_tree_add_create_from_array_impl, alloc,
+                                paths);
+        CX_TEST_CALL_SUBROUTINE(test_tree_add_create_from_array_impl, alloc,
+                                scrambled_paths);
 
         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
     }
     cx_testing_allocator_destroy(&talloc);
 }
 
-CX_TEST(test_tree_add_create_from_array) {
-    // creates an entirely new tree from an array
-    CX_TEST_DO {
-
-    }
-}
-
 
 CxTestSuite *cx_test_suite_tree_low_level(void) {
     CxTestSuite *suite = cx_test_suite_new("tree (low level)");
@@ -1322,9 +1490,11 @@
     cx_test_register(suite, test_tree_iterator_continue_with_exit);
     cx_test_register(suite, test_tree_add_one);
     cx_test_register(suite, test_tree_add_one_existing);
-    cx_test_register(suite, test_tree_add_one_to_empty_tree);
+    cx_test_register(suite, test_tree_add_one_no_match);
+    cx_test_register(suite, test_tree_add_duplicate_root);
+    cx_test_register(suite, test_tree_add_zero);
     cx_test_register(suite, test_tree_add_many);
-    cx_test_register(suite, test_tree_add_many_skip_duplicates);
+    cx_test_register(suite, test_tree_add_many_with_dupl_and_no_match);
     cx_test_register(suite, test_tree_add_create_from_array);
 
     return suite;

mercurial