add tests for simple clone functions

Sun, 09 Nov 2025 16:12:07 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 09 Nov 2025 16:12:07 +0100
changeset 1480
83146195a1db
parent 1479
ac1baaed2fd7
child 1481
1dda1eed1899

add tests for simple clone functions

resolves #757

src/list.c file | annotate | diff | comparison | revisions
tests/test_hash_map.c file | annotate | diff | comparison | revisions
tests/test_list.c file | annotate | diff | comparison | revisions
--- a/src/list.c	Sat Nov 08 23:45:19 2025 +0100
+++ b/src/list.c	Sun Nov 09 16:12:07 2025 +0100
@@ -867,6 +867,11 @@
         return 1;
     }
 
+    // set the sorted flag when we know it's sorted
+    if (orig_size == 0 && src->collection.sorted) {
+        dst->collection.sorted = true;
+    }
+
     return 0;
 }
 
@@ -1028,37 +1033,31 @@
                 other_elem = cxIteratorCurrent(other_iter);
                 d = src->collection.cmpfunc(src_elem, other_elem);
             }
-            if (d <= 0) {
-                // source element is smaller or equal, clone it
-                void **dst_mem = cxListEmplace(dst);
-                void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
-                void* dst_ptr = clone_func(target, src_elem, clone_allocator, data);
-                if (dst_ptr == NULL) {
-                    cx_list_pop_uninitialized_elements(dst, 1);
-                    return 1;
-                }
-                if (cxCollectionStoresPointers(dst)) {
-                    *dst_mem = dst_ptr;
-                }
+            void *clone_from;
+            if (d < 0) {
+                // source element is smaller clone it
+                clone_from = src_elem;
                 cxIteratorNext(src_iter);
-                // if the other element was equal, skip it
-                if (d == 0) {
-                    cxIteratorNext(other_iter);
-                }
+            } else if (d == 0) {
+                // both elements are equal, clone from the source, skip other
+                clone_from = src_elem;
+                cxIteratorNext(src_iter);
+                cxIteratorNext(other_iter);
             } else {
                 // the other element is smaller, clone it
-                void **dst_mem = cxListEmplace(dst);
-                void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
-                void* dst_ptr = clone_func(target, other_elem, clone_allocator, data);
-                if (dst_ptr == NULL) {
-                    cx_list_pop_uninitialized_elements(dst, 1);
-                    return 1;
-                }
-                if (cxCollectionStoresPointers(dst)) {
-                    *dst_mem = dst_ptr;
-                }
+                clone_from = other_elem;
                 cxIteratorNext(other_iter);
             }
+            void **dst_mem = cxListEmplace(dst);
+            void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem;
+            void* dst_ptr = clone_func(target, clone_from, clone_allocator, data);
+            if (dst_ptr == NULL) {
+                cx_list_pop_uninitialized_elements(dst, 1);
+                return 1;
+            }
+            if (cxCollectionStoresPointers(dst)) {
+                *dst_mem = dst_ptr;
+            }
         }
 
         // if dst was empty, it is now guaranteed to be sorted
--- a/tests/test_hash_map.c	Sat Nov 08 23:45:19 2025 +0100
+++ b/tests/test_hash_map.c	Sun Nov 09 16:12:07 2025 +0100
@@ -1155,6 +1155,109 @@
     cxMapFree(s2);
 }
 
+CX_TEST(test_hash_map_simple_clones) {
+    CxMap *a = cxHashMapCreateSimple(sizeof(int));
+    cxMapPut(a, "k1", "v1");
+    cxMapPut(a, "k2", "v2");
+    cxMapPut(a, "k3", "v3");
+    cxMapPut(a, "k4", "v4");
+
+    CxMap *b = cxHashMapCreateSimple(sizeof(int));
+    cxMapPut(b, "k0", "v0");
+    cxMapPut(b, "k2", "v2");
+    cxMapPut(b, "k5", "v5");
+
+    CxMap *c = cxHashMapCreateSimple(sizeof(int));
+    cxMapPut(c, "k3", "v3");
+    cxMapPut(c, "k4", "v4");
+    cxMapPut(c, "k5", "v5");
+
+
+    CxHashKey k;
+    CxList *kl1 = cxArrayListCreateSimple(sizeof(CxHashKey), 4);
+    cxCollectionCompareFunc(kl1, cx_hash_key_cmp);
+    k = CX_HASH_KEY("k0");
+    cxListAdd(kl1, &k);
+    k = CX_HASH_KEY("k2");
+    cxListAdd(kl1, &k);
+    k = CX_HASH_KEY("k5");
+    cxListAdd(kl1, &k);
+
+    CxList *kl2 = cxArrayListCreateSimple(sizeof(CxHashKey), 4);
+    cxCollectionCompareFunc(kl2, cx_hash_key_cmp);
+    k = CX_HASH_KEY("k3");
+    cxListAdd(kl2, &k);
+    k = CX_HASH_KEY("k4");
+    cxListAdd(kl2, &k);
+    k = CX_HASH_KEY("k5");
+    cxListAdd(kl2, &k);
+
+    CxMap *d1 = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    cxDefineAdvancedDestructor(d1, cxFree, (void*)cxDefaultAllocator);
+    CxMap *d2 = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    cxDefineAdvancedDestructor(d2, cxFree, (void*)cxDefaultAllocator);
+
+    CX_TEST_DO {
+        CX_TEST_ASSERT(0 == cxMapCloneSimple(d1, a));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k0"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k1"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k2"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k3"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k4"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k5"));
+
+        CX_TEST_ASSERT(0 == cxMapListDifferenceSimple(d2, d1, kl1));
+        CX_TEST_ASSERT(!cxMapContains(d2, "k0"));
+        CX_TEST_ASSERT(cxMapContains(d2, "k1"));
+        CX_TEST_ASSERT(!cxMapContains(d2, "k2"));
+        CX_TEST_ASSERT(cxMapContains(d2, "k3"));
+        CX_TEST_ASSERT(cxMapContains(d2, "k4"));
+        CX_TEST_ASSERT(!cxMapContains(d2, "k5"));
+
+        cxMapClear(d1);
+        CX_TEST_ASSERT(0 == cxMapListIntersectionSimple(d1, d2, kl2));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k0"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k1"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k2"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k3"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k4"));
+
+        CX_TEST_ASSERT(0 == cxMapUnionSimple(d1, b));
+        CX_TEST_ASSERT(cxMapContains(d1, "k0"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k1"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k2"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k3"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k4"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k5"));
+
+        cxMapClear(d2);
+        CX_TEST_ASSERT(0 == cxMapDifferenceSimple(d2, d1, a));
+        CX_TEST_ASSERT(cxMapContains(d2, "k0"));
+        CX_TEST_ASSERT(!cxMapContains(d2, "k1"));
+        CX_TEST_ASSERT(!cxMapContains(d2, "k2"));
+        CX_TEST_ASSERT(!cxMapContains(d2, "k3"));
+        CX_TEST_ASSERT(!cxMapContains(d2, "k4"));
+        CX_TEST_ASSERT(cxMapContains(d2, "k5"));
+
+        cxMapClear(d1);
+        CX_TEST_ASSERT(0 == cxMapIntersectionSimple(d1, d2, c));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k0"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k1"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k2"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k3"));
+        CX_TEST_ASSERT(!cxMapContains(d1, "k4"));
+        CX_TEST_ASSERT(cxMapContains(d1, "k5"));
+    }
+
+    cxMapFree(a);
+    cxMapFree(b);
+    cxMapFree(c);
+    cxListFree(kl1);
+    cxListFree(kl2);
+    cxMapFree(d1);
+    cxMapFree(d2);
+}
+
 CX_TEST(test_empty_map_size) {
     CX_TEST_DO {
         CX_TEST_ASSERT(cxEmptyMap->collection.size == 0);
@@ -1520,6 +1623,7 @@
     cx_test_register(suite, test_hash_map_union);
     cx_test_register(suite, test_hash_map_union_ptr);
     cx_test_register(suite, test_hash_map_union_alloc_fail);
+    cx_test_register(suite, test_hash_map_simple_clones);
     cx_test_register(suite, test_empty_map_no_ops);
     cx_test_register(suite, test_empty_map_size);
     cx_test_register(suite, test_empty_map_get);
--- a/tests/test_list.c	Sat Nov 08 23:45:19 2025 +0100
+++ b/tests/test_list.c	Sun Nov 09 16:12:07 2025 +0100
@@ -2976,6 +2976,83 @@
     }
 }
 
+CX_TEST(test_list_simple_clones) {
+
+    int a[] = {1, 2, 5, 8, 10};
+    int b[] = {1, 3, 5, 7, 9, 11};
+    int c[] = {2, 4, 6, 8, 10, 12};
+    int d[] = {4, 8, 12};
+
+    CxList *la = cxArrayListCreateSimple(sizeof(int), 8);
+    cxCollectionCompareFunc(la, cx_cmp_int);
+    cxListInsertSortedArray(la, a, cx_nmemb(a));
+    CxList *lb = cxArrayListCreateSimple(sizeof(int), 8);
+    cxCollectionCompareFunc(lb, cx_cmp_int);
+    cxListInsertSortedArray(lb, b, cx_nmemb(b));
+    CxList *lc = cxArrayListCreateSimple(sizeof(int), 8);
+    cxCollectionCompareFunc(lc, cx_cmp_int);
+    cxListInsertSortedArray(lc, c, cx_nmemb(c));
+    CxList *ld = cxArrayListCreateSimple(sizeof(int), 8);
+    cxCollectionCompareFunc(ld, cx_cmp_int);
+    cxListInsertSortedArray(ld, d, cx_nmemb(d));
+
+    CxList *d1 = cxArrayListCreateSimple(CX_STORE_POINTERS, 8);
+    cxCollectionCompareFunc(d1, cx_cmp_int);
+    cxDefineAdvancedDestructor(d1, cxFree, (void*) cxDefaultAllocator);
+    CxList *d2 = cxArrayListCreateSimple(CX_STORE_POINTERS, 8);
+    cxCollectionCompareFunc(d2, cx_cmp_int);
+    cxDefineAdvancedDestructor(d2, cxFree, (void*) cxDefaultAllocator);
+
+    CX_TEST_DO {
+        // clone a into d1
+        CX_TEST_ASSERT(0 == cxListCloneSimple(d1, la));
+        CX_TEST_ASSERT(0 == cxListCompare(d1, la));
+        CX_TEST_ASSERT(cxCollectionSorted(d1));
+
+        // union of a (in d1) and b
+        CX_TEST_ASSERT(0 == cxListUnionSimple(d2, d1, lb));
+        CX_TEST_ASSERT(cxCollectionSorted(d2));
+        CxList *expected_union = cxArrayListCreateSimple(sizeof(int), 8);
+        {
+            int expected[] = {1, 2, 3, 5, 7, 8, 9, 10, 11};
+            cxListInsertSortedArray(expected_union, expected, cx_nmemb(expected));
+        }
+        CX_TEST_ASSERT(0 == cxListCompare(d2, expected_union));
+        cxListFree(expected_union);
+
+        // intersection of (a union b) and c
+        cxListClear(d1);
+        CX_TEST_ASSERT(0 == cxListIntersectionSimple(d1, d2, lc));
+        CX_TEST_ASSERT(cxCollectionSorted(d1));
+        CxList *expected_intersection = cxArrayListCreateSimple(sizeof(int), 8);
+        {
+            int expected[] = {2, 8, 10};
+            cxListInsertSortedArray(expected_intersection, expected, cx_nmemb(expected));
+        }
+        CX_TEST_ASSERT(0 == cxListCompare(d1, expected_intersection));
+        cxListFree(expected_intersection);
+
+        // difference of ((a union b) intersect c) minus d
+        cxListClear(d2);
+        CX_TEST_ASSERT(0 == cxListDifferenceSimple(d2, d1, ld));
+        CX_TEST_ASSERT(cxCollectionSorted(d2));
+        CxList *expected_difference = cxArrayListCreateSimple(sizeof(int), 8);
+        {
+            int expected[] = {2, 10};
+            cxListInsertSortedArray(expected_difference, expected, cx_nmemb(expected));
+        }
+        CX_TEST_ASSERT(0 == cxListCompare(d2, expected_difference));
+        cxListFree(expected_difference);
+    }
+
+    cxListFree(d1);
+    cxListFree(d2);
+    cxListFree(la);
+    cxListFree(lb);
+    cxListFree(lc);
+    cxListFree(ld);
+}
+
 CX_TEST(test_list_pointer_list_supports_null) {
     CxList *list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS);
     int x = 47;
@@ -3443,6 +3520,7 @@
     cx_test_register(suite, test_list_intersection_sorted);
     cx_test_register(suite, test_list_intersection_unsorted_alloc_fail);
     cx_test_register(suite, test_list_intersection_sorted_alloc_fail);
+    cx_test_register(suite, test_list_simple_clones);
 
     return suite;
 }

mercurial