Fri, 24 Oct 2025 21:15:14 +0200
add test coverage for allocation failures in clone-function
| tests/test_hash_map.c | file | annotate | diff | comparison | revisions | |
| tests/test_list.c | file | annotate | diff | comparison | revisions |
--- a/tests/test_hash_map.c Fri Oct 24 20:52:10 2025 +0200 +++ b/tests/test_hash_map.c Fri Oct 24 21:15:14 2025 +0200 @@ -513,8 +513,11 @@ cx_testing_allocator_destroy(&talloc); } +static unsigned test_hash_map_clone_func_max_clones; static void *test_hash_map_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { + if (test_hash_map_clone_func_max_clones == 0) return NULL; + test_hash_map_clone_func_max_clones--; if (dst == NULL) { dst = cxMalloc(al, sizeof(int)); } @@ -535,6 +538,7 @@ } CX_TEST_DO { int c = 4; + test_hash_map_clone_func_max_clones = 100; // no limit size_t cloned = cxMapClone(dst, src, test_hash_map_clone_func, NULL, &c); CX_TEST_ASSERT(cloned == 3); CX_TEST_ASSERT(cxMapSize(dst) == 5); @@ -552,6 +556,38 @@ cxMapFree(src); } +CX_TEST(test_hash_map_clone_alloc_fail) { + CxMap *dst = cxHashMapCreateSimple(sizeof(int)); + CxMap *src = cxHashMapCreateSimple(sizeof(int)); + const char *exist_keys[] = {"k1", "k2", "k3"}; + int exists[] = {1, 3, 4}; + const char *source_keys[] = {"k4", "k2", "k5"}; + int source[] = {7, 9, 15}; + for (unsigned int i = 0 ; i < 3 ; i++) { + cxMapPut(dst, exist_keys[i], &exists[i]); + cxMapPut(src, source_keys[i], &source[i]); + } + CX_TEST_DO { + int c = 4; + test_hash_map_clone_func_max_clones = 2; + size_t cloned = cxMapClone(dst, src, test_hash_map_clone_func, NULL, &c); + CX_TEST_ASSERT(cloned == 2); + CX_TEST_ASSERT(cxMapSize(dst) == 4); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k1")) == 1); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k2")) == 13); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k3")) == 4); + // the concrete element which is affected might change when the hash function changes + CX_TEST_ASSERT(cxMapGet(dst, "k4") == NULL); + CX_TEST_ASSERT(*((int*)cxMapGet(dst, "k5")) == 19); + CX_TEST_ASSERT(cxMapSize(src) == 3); + CX_TEST_ASSERT(*((int*)cxMapGet(src, "k4")) == 7); + CX_TEST_ASSERT(*((int*)cxMapGet(src, "k2")) == 9); + CX_TEST_ASSERT(*((int*)cxMapGet(src, "k5")) == 15); + } + cxMapFree(dst); + cxMapFree(src); +} + CX_TEST(test_hash_map_clone_ptr) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); @@ -571,6 +607,7 @@ } CX_TEST_DO { int c = 4; + test_hash_map_clone_func_max_clones = 100; // no limit size_t cloned = cxMapClone(dst, src, test_hash_map_clone_func, allocator, &c); CX_TEST_ASSERT(cloned == 3); CX_TEST_ASSERT(cxMapSize(dst) == 5); @@ -938,6 +975,7 @@ cx_test_register(suite, test_hash_map_simple_destructor_pointers); cx_test_register(suite, test_hash_map_advanced_destructor_pointers); 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_empty_map_no_ops); cx_test_register(suite, test_empty_map_size);
--- a/tests/test_list.c Fri Oct 24 20:52:10 2025 +0200 +++ b/tests/test_list.c Fri Oct 24 21:15:14 2025 +0200 @@ -2526,7 +2526,11 @@ free(testdata); }) +static unsigned test_clone_func_max_clones; static void *test_clone_func(void *dest, const void *src, const CxAllocator *al, void *data) { + if (test_clone_func_max_clones == 0) return NULL; + test_clone_func_max_clones--; + if (dest == NULL) { dest = cxMalloc(al, sizeof(int)); } @@ -2534,7 +2538,6 @@ *((int*) dest) = *(int*) src + *(int*) data; (*(int*) data)++; - // TODO: create a separate test with another clone func which simulates a failure after a few allocations return dest; } @@ -2570,6 +2573,7 @@ // perform the test int expected_data[12] = array_init(1, 2, 3, 4, 1, 3, 5, 7, 9, 11, 13, 15); int c = 0; + test_clone_func_max_clones = 100; // no limit size_t cloned = cxListClone(target, source, test_clone_func, testing_alloc, &c); CX_TEST_ASSERT(cloned == 8); CX_TEST_ASSERT(c == 8); @@ -2586,6 +2590,55 @@ cx_testing_allocator_destroy(&talloc); } +static CX_TEST_SUBROUTINE(verify_clone_alloc_fail, CxList *target, CxList *source, bool target_isptrlist) { + // testing allocator for the target elements if target_isptrlist is true + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + CxAllocator *testing_alloc = &talloc.base; + + // register a destructor for the target list if it is storing pointers + if (target_isptrlist) { + cxDefineAdvancedDestructor(target, cxFree, testing_alloc); + } + + // fill the source list + int source_data[8] = array_init(1, 2, 3, 4, 5, 6, 7, 8); + for (unsigned i = 0 ; i < 8 ; i++) { + cxListAdd(source, &source_data[i]); + } + + // add some initial data to the target + int initial_data[4] = array_init(1, 2, 3, 4); + for (unsigned i = 0; i < 4; i++) { + if (target_isptrlist) { + int *x = cxMalloc(testing_alloc, sizeof(int)); + *x = initial_data[i]; + cxListAdd(target, x); + } else { + cxListAdd(target, &initial_data[i]); + } + } + + // perform the test + int expected_data[9] = array_init(1, 2, 3, 4, 1, 3, 5, 7, 9); + int c = 0; + test_clone_func_max_clones = 5; + size_t cloned = cxListClone(target, source, test_clone_func, testing_alloc, &c); + CX_TEST_ASSERT(cloned == 5); + CX_TEST_ASSERT(c == 5); + CX_TEST_ASSERT(cxListSize(target) == 9); + CX_TEST_ASSERT(cxListSize(source) == 8); + for (unsigned i = 0 ; i < 9 ; i++) { + CX_TEST_ASSERT(*(int*)cxListAt(target, i) == expected_data[i]); + } + for (unsigned i = 0 ; i < 8 ; i++) { + CX_TEST_ASSERT(*(int*)cxListAt(source, i) == source_data[i]); + } + cxListFree(target); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + cx_testing_allocator_destroy(&talloc); +} + roll_out_test_combos(clone_into_arl, { CxList *target = cxArrayListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int), 8); CX_TEST_CALL_SUBROUTINE(verify_clone, target, list, false); @@ -2606,6 +2659,26 @@ CX_TEST_CALL_SUBROUTINE(verify_clone, target, list, true); }) +roll_out_test_combos(clone_alloc_fail_into_arl, { + CxList *target = cxArrayListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int), 8); + CX_TEST_CALL_SUBROUTINE(verify_clone_alloc_fail, target, list, false); +}) + +roll_out_test_combos(clone_alloc_fail_into_ll, { + CxList *target = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int)); + CX_TEST_CALL_SUBROUTINE(verify_clone_alloc_fail, target, list, false); +}) + +roll_out_test_combos(clone_alloc_fail_into_parl, { + CxList *target = cxArrayListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS, 8); + CX_TEST_CALL_SUBROUTINE(verify_clone_alloc_fail, target, list, true); +}) + +roll_out_test_combos(clone_alloc_fail_into_pll, { + CxList *target = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS); + CX_TEST_CALL_SUBROUTINE(verify_clone_alloc_fail, target, list, true); +}) + CX_TEST(test_list_pointer_list_supports_null) { CxList *list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, CX_STORE_POINTERS); int x = 47; @@ -2786,6 +2859,14 @@ cx_test_register(suite, test_list_parl_clone_into_parl); cx_test_register(suite, test_list_arl_clone_into_pll); cx_test_register(suite, test_list_parl_clone_into_pll); + cx_test_register(suite, test_list_arl_clone_alloc_fail_into_arl); + cx_test_register(suite, test_list_parl_clone_alloc_fail_into_arl); + cx_test_register(suite, test_list_arl_clone_alloc_fail_into_ll); + cx_test_register(suite, test_list_parl_clone_alloc_fail_into_ll); + cx_test_register(suite, test_list_arl_clone_alloc_fail_into_parl); + cx_test_register(suite, test_list_parl_clone_alloc_fail_into_parl); + cx_test_register(suite, test_list_arl_clone_alloc_fail_into_pll); + cx_test_register(suite, test_list_parl_clone_alloc_fail_into_pll); return suite; } @@ -2913,6 +2994,14 @@ cx_test_register(suite, test_list_pll_clone_into_parl); cx_test_register(suite, test_list_ll_clone_into_pll); cx_test_register(suite, test_list_pll_clone_into_pll); + cx_test_register(suite, test_list_ll_clone_alloc_fail_into_arl); + cx_test_register(suite, test_list_pll_clone_alloc_fail_into_arl); + cx_test_register(suite, test_list_ll_clone_alloc_fail_into_ll); + cx_test_register(suite, test_list_pll_clone_alloc_fail_into_ll); + cx_test_register(suite, test_list_ll_clone_alloc_fail_into_parl); + cx_test_register(suite, test_list_pll_clone_alloc_fail_into_parl); + cx_test_register(suite, test_list_ll_clone_alloc_fail_into_pll); + cx_test_register(suite, test_list_pll_clone_alloc_fail_into_pll); return suite; } @@ -3014,6 +3103,14 @@ cx_test_register(suite, test_list_pkvl_clone_into_parl); cx_test_register(suite, test_list_kvl_clone_into_pll); cx_test_register(suite, test_list_pkvl_clone_into_pll); + cx_test_register(suite, test_list_kvl_clone_alloc_fail_into_arl); + cx_test_register(suite, test_list_pkvl_clone_alloc_fail_into_arl); + cx_test_register(suite, test_list_kvl_clone_alloc_fail_into_ll); + cx_test_register(suite, test_list_pkvl_clone_alloc_fail_into_ll); + cx_test_register(suite, test_list_kvl_clone_alloc_fail_into_parl); + cx_test_register(suite, test_list_pkvl_clone_alloc_fail_into_parl); + cx_test_register(suite, test_list_kvl_clone_alloc_fail_into_pll); + cx_test_register(suite, test_list_pkvl_clone_alloc_fail_into_pll); return suite; }