--- a/tests/test_hash_map.c Wed Nov 05 22:39:39 2025 +0100 +++ b/tests/test_hash_map.c Wed Nov 05 23:04:46 2025 +0100 @@ -1063,6 +1063,98 @@ cx_testing_allocator_destroy(&talloc); } +CX_TEST(test_hash_map_union) { + CxMap *s1 = cxHashMapCreateSimple(sizeof(int)); + CxMap *s2 = cxHashMapCreateSimple(sizeof(int)); + const char *s1_keys[] = {"k1", "k2", "k3", "k4"}; + int s1_values[] = {1, 3, 4, 6}; + const char *s2_keys[] = {"k4", "k5", "k2", "k6"}; + int s2_values[] = {5, 9, 15, 23}; + for (unsigned int i = 0 ; i < 4 ; i++) { + cxMapPut(s1, s1_keys[i], &s1_values[i]); + cxMapPut(s2, s2_keys[i], &s2_values[i]); + } + CX_TEST_DO { + int c = 4; + CX_TEST_ASSERT(0 == cxMapUnion(s1, s2, test_hash_map_clone_func, NULL, &c)); + CX_TEST_ASSERT(cxMapSize(s1) == 6); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k1")) == 1); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k2")) == 3); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k3")) == 4); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k4")) == 6); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k5")) == 13); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k6")) == 27); + } + cxMapFree(s1); + cxMapFree(s2); +} + +CX_TEST(test_hash_map_union_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(sizeof(int)); + CxMap *s2 = cxHashMapCreateSimple(sizeof(int)); + const char *s1_keys[] = {"k1", "k2", "k3", "k4"}; + int s1_values[] = {1, 3, 4, 6}; + const char *s2_keys[] = {"k4", "k5", "k2", "k6"}; + int s2_values[] = {5, 9, 15, 23}; + for (unsigned int i = 0 ; i < 4 ; i++) { + cxMapPut(s1, s1_keys[i], &s1_values[i]); + cxMapPut(s2, s2_keys[i], &s2_values[i]); + } + CX_TEST_DO { + int c = 4; + CX_TEST_ASSERT(0 == cxMapClone(dst, s1, test_hash_map_clone_func, allocator, &c)); + CX_TEST_ASSERT(0 == cxMapUnion(dst, s2, test_hash_map_clone_func, allocator, &c)); + CX_TEST_ASSERT(cxMapSize(dst) == 6); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 5); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k2")) == 7); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 8); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k4")) == 10); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k5")) == 13); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k6")) == 27); + CX_TEST_ASSERT(cx_testing_allocator_used(&talloc)); + } + cxMapFree(dst); + cxMapFree(s1); + cxMapFree(s2); + cx_testing_allocator_destroy(&talloc); +} + +CX_TEST(test_hash_map_union_alloc_fail) { + CxMap *s1 = cxHashMapCreateSimple(sizeof(int)); + CxMap *s2 = cxHashMapCreateSimple(sizeof(int)); + const char *s1_keys[] = {"k1", "k2", "k3", "k4"}; + int s1_values[] = {1, 3, 4, 6}; + const char *s2_keys[] = {"k4", "k5", "k2", "k6"}; + int s2_values[] = {5, 9, 15, 23}; + for (unsigned int i = 0 ; i < 4 ; 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_enabled = true; + test_hash_map_clone_func_max_clones = 1; + CX_TEST_ASSERT(0 != cxMapUnion(s1, s2, test_hash_map_clone_func, NULL, &c)); + test_hash_map_clone_func_max_enabled = false; + CX_TEST_ASSERT(cxMapSize(s1) == 5); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k1")) == 1); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k2")) == 3); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k3")) == 4); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k4")) == 6); + // the concrete element which is affected might change when the hash function changes + CX_TEST_ASSERT(cxMapGet(s1, "k5") == NULL); + CX_TEST_ASSERT(*((int*)cxMapGet(s1, "k6")) == 27); + } + cxMapFree(s1); + cxMapFree(s2); +} + CX_TEST(test_empty_map_size) { CX_TEST_DO { CX_TEST_ASSERT(cxEmptyMap->collection.size == 0); @@ -1425,6 +1517,9 @@ cx_test_register(suite, test_hash_map_list_intersection_alloc_fail); cx_test_register(suite, test_hash_map_intersection_non_empty_target); cx_test_register(suite, test_hash_map_list_intersection_non_empty_target); + 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_empty_map_no_ops); cx_test_register(suite, test_empty_map_size); cx_test_register(suite, test_empty_map_get);