add tests for cxMapDifference() and cxMapListDifference()

Sun, 26 Oct 2025 12:44:33 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 26 Oct 2025 12:44:33 +0100
changeset 1448
0f0fe7311b76
parent 1447
aaf85b3e9601
child 1449
bbca398783ed

add tests for cxMapDifference() and cxMapListDifference()

resolves #746

src/Makefile file | annotate | diff | comparison | revisions
src/cx/hash_key.h file | annotate | diff | comparison | revisions
src/cx/map.h file | annotate | diff | comparison | revisions
src/hash_key.c file | annotate | diff | comparison | revisions
tests/Makefile file | annotate | diff | comparison | revisions
tests/test_hash_map.c file | annotate | diff | comparison | revisions
--- a/src/Makefile	Sun Oct 26 12:01:28 2025 +0100
+++ b/src/Makefile	Sun Oct 26 12:44:33 2025 +0100
@@ -89,7 +89,7 @@
 	$(CC) -o $@ $(CFLAGS)  -c $<
 
 $(build_dir)/hash_key$(OBJ_EXT): hash_key.c cx/hash_key.h cx/common.h \
- cx/string.h cx/allocator.h
+ cx/string.h cx/allocator.h cx/compare.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS)  -c $<
 
@@ -127,7 +127,8 @@
 	$(CC) -o $@ $(CFLAGS)  -c $<
 
 $(build_dir)/map$(OBJ_EXT): map.c cx/map.h cx/common.h cx/collection.h \
- cx/allocator.h cx/iterator.h cx/compare.h cx/string.h cx/hash_key.h
+ cx/allocator.h cx/iterator.h cx/compare.h cx/string.h cx/hash_key.h \
+ cx/list.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS)  -c $<
 
--- a/src/cx/hash_key.h	Sun Oct 26 12:01:28 2025 +0100
+++ b/src/cx/hash_key.h	Sun Oct 26 12:44:33 2025 +0100
@@ -227,12 +227,14 @@
 /**
  * Compare function for hash keys.
  *
- * @param left the first key
- * @param right the second key
+ * The pointers are untyped to be compatible with the cx_compare_func signature.
+ *
+ * @param left (@c CxHashKey*) the first key
+ * @param right (@c CxHashKey*) the second key
  * @return zero when the keys equal, non-zero when they differ
  */
 cx_attr_nodiscard cx_attr_nonnull
-CX_EXPORT int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right);
+CX_EXPORT int cx_hash_key_cmp(const void *left, const void *right);
 
 #ifdef __cplusplus
 } // extern "C"
--- a/src/cx/map.h	Sun Oct 26 12:01:28 2025 +0100
+++ b/src/cx/map.h	Sun Oct 26 12:44:33 2025 +0100
@@ -528,7 +528,8 @@
  * Clones entries of a map if their key is not present in a list.
  *
  * Note that the list must contain keys of type @c CxKey
- * (or pointers to such keys).
+ * (or pointers to such keys) and must use @c cx_hash_key_cmp
+ * as the compare function.
  * Generic key types cannot be processed in this case.
  *
  * @param dst the destination map
--- a/src/hash_key.c	Sun Oct 26 12:01:28 2025 +0100
+++ b/src/hash_key.c	Sun Oct 26 12:44:33 2025 +0100
@@ -159,7 +159,9 @@
     return key;
 }
 
-int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) {
+int cx_hash_key_cmp(const void *l, const void *r) {
+    const CxHashKey *left = l;
+    const CxHashKey *right = r;
     int d;
     d = cx_vcmp_uint64(left->hash, right->hash);
     if (d != 0) return d;
--- a/tests/Makefile	Sun Oct 26 12:01:28 2025 +0100
+++ b/tests/Makefile	Sun Oct 26 12:44:33 2025 +0100
@@ -77,9 +77,10 @@
 
 $(TEST_DIR)/test_hash_map$(OBJ_EXT): test_hash_map.c ../src/cx/test.h \
  ../src/cx/common.h util_allocator.h ../src/cx/allocator.h \
- ../src/cx/hash_map.h ../src/cx/map.h ../src/cx/collection.h \
+ ../src/cx/array_list.h ../src/cx/list.h ../src/cx/collection.h \
  ../src/cx/allocator.h ../src/cx/iterator.h ../src/cx/compare.h \
- ../src/cx/string.h ../src/cx/hash_key.h
+ ../src/cx/list.h ../src/cx/hash_map.h ../src/cx/map.h ../src/cx/string.h \
+ ../src/cx/hash_key.h
 	@echo "Compiling $<"
 	$(CC) -o $@ $(CFLAGS) -I../src -c $<
 
--- a/tests/test_hash_map.c	Sun Oct 26 12:01:28 2025 +0100
+++ b/tests/test_hash_map.c	Sun Oct 26 12:44:33 2025 +0100
@@ -28,6 +28,8 @@
 
 #include "cx/test.h"
 #include "util_allocator.h"
+#include "cx/array_list.h"
+#include "cx/list.h"
 
 #include "cx/hash_map.h"
 
@@ -626,6 +628,97 @@
     cx_testing_allocator_destroy(&talloc);
 }
 
+CX_TEST(test_hash_map_difference) {
+    CxMap *dst = cxHashMapCreateSimple(sizeof(int));
+
+    CxMap *s1 = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    CxMap *s2 = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    const char *s1_keys[] = {"k1", "k2", "k3"};
+    int s1_values[] = {1, 3, 4};
+    const char *s2_keys[] = {"k4", "k2", "k5"};
+    int s2_values[] = {7, 9, 15};
+    for (unsigned int i = 0 ; i < 3 ; i++) {
+        cxMapPut(s1, s1_keys[i], &s1_values[i]);
+        cxMapPut(s2, s2_keys[i], &s2_values[i]);
+    }
+    CX_TEST_DO {
+        int c = 4;
+        test_hash_map_clone_func_max_clones = 100; // no limit
+        CX_TEST_ASSERT(0 == cxMapDifference(dst, s1, s2, test_hash_map_clone_func, NULL, &c));
+        CX_TEST_ASSERT(cxMapSize(dst) == 2);
+        CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 5);
+        CX_TEST_ASSERT(cxMapGet(dst, "k2") == NULL);
+        CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 8);
+    }
+    cxMapFree(dst);
+    cxMapFree(s1);
+    cxMapFree(s2);
+}
+
+CX_TEST(test_hash_map_list_difference) {
+    CxMap *dst = cxHashMapCreateSimple(sizeof(int));
+    CxMap *src = cxHashMapCreateSimple(sizeof(int));
+    CxList *keys = cxArrayListCreate(NULL, cx_hash_key_cmp, sizeof(CxHashKey), 4);
+
+    const char *src_keys[] = {"k1", "k2", "k3"};
+    int src_values[] = {1, 3, 4};
+    for (unsigned int i = 0 ; i < 3 ; i++) {
+        cxMapPut(src, src_keys[i], &src_values[i]);
+    }
+    const char *k[] = {"k4", "k2", "k5"};
+    for (unsigned int i = 0 ; i < 3 ; i++) {
+        CxHashKey key = CX_HASH_KEY(k[i]);
+        cxListAdd(keys, &key);
+    }
+    CX_TEST_DO {
+        int c = 4;
+        test_hash_map_clone_func_max_clones = 100; // no limit
+        CX_TEST_ASSERT(0 == cxMapListDifference(dst, src, keys, test_hash_map_clone_func, NULL, &c));
+        CX_TEST_ASSERT(cxMapSize(dst) == 2);
+        CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 5);
+        CX_TEST_ASSERT(cxMapGet(dst, "k2") == NULL);
+        CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 8);
+    }
+    cxMapFree(dst);
+    cxMapFree(src);
+    cxListFree(keys);
+}
+
+CX_TEST(test_hash_map_difference_ptr) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CxAllocator *allocator = &talloc.base;
+    CxMap *dst = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    cxDefineAdvancedDestructor(dst, cxFree, allocator);
+
+    CxMap *s1 = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    CxMap *s2 = cxHashMapCreateSimple(CX_STORE_POINTERS);
+    const char *s1_keys[] = {"k1", "k2", "k3"};
+    int s1_values[] = {1, 3, 4};
+    const char *s2_keys[] = {"k4", "k2", "k5"};
+    int s2_values[] = {7, 9, 15};
+    for (unsigned int i = 0 ; i < 3 ; i++) {
+        cxMapPut(s1, s1_keys[i], &s1_values[i]);
+        cxMapPut(s2, s2_keys[i], &s2_values[i]);
+    }
+    CX_TEST_DO {
+        int c = 4;
+        test_hash_map_clone_func_max_clones = 100; // no limit
+        CX_TEST_ASSERT(0 == cxMapDifference(dst, s1, s2, test_hash_map_clone_func, allocator, &c));
+        CX_TEST_ASSERT(cxMapSize(dst) == 2);
+        CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 5);
+        CX_TEST_ASSERT(cxMapGet(dst, "k2") == NULL);
+        CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 8);
+
+        cxMapClear(dst);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+    }
+    cxMapFree(dst);
+    cxMapFree(s1);
+    cxMapFree(s2);
+    cx_testing_allocator_destroy(&talloc);
+}
+
 CX_TEST(test_empty_map_size) {
     CX_TEST_DO {
         CX_TEST_ASSERT(cxEmptyMap->collection.size == 0);
@@ -974,6 +1067,9 @@
     cx_test_register(suite, test_hash_map_clone);
     cx_test_register(suite, test_hash_map_clone_alloc_fail);
     cx_test_register(suite, test_hash_map_clone_ptr);
+    cx_test_register(suite, test_hash_map_difference);
+    cx_test_register(suite, test_hash_map_difference_ptr);
+    cx_test_register(suite, test_hash_map_list_difference);
     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);

mercurial