Sat, 07 Dec 2024 23:59:54 +0100
change cx_strcat variants to allow handling of ENOMEM
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "cx/test.h" #if __GNUC__ // we want to perform overflow tests, but we don't want warnings about them #define __alloc_size__(...) // NOLINT(*-reserved-identifier) #endif #include "cx/allocator.h" #include <errno.h> CX_TEST(test_allocator_default) { CX_TEST_DO { CX_TEST_ASSERT(cxDefaultAllocator->cl != NULL); } } CX_TEST(test_allocator_default_malloc) { void *test = cxMalloc(cxDefaultAllocator, 16); CX_TEST_DO { CX_TEST_ASSERT(test != NULL); // we cannot assert sth. but valgrind will detect an error memcpy(test, "0123456789ABCDEF", 16); } free(test); } CX_TEST(test_allocator_default_calloc) { char *test = cxCalloc(cxDefaultAllocator, 8, 2); CX_TEST_DO { CX_TEST_ASSERT(test != NULL); for (int i = 0; i < 16; i++) { CX_TEST_ASSERT(test[i] == 0); } } free(test); } CX_TEST(test_allocator_default_realloc) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { test = cxRealloc(cxDefaultAllocator, test, 16); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_default_reallocarray) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { test = cxReallocArray(cxDefaultAllocator, test, 16, 2); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_default_reallocarray_overflow) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { void *fail = cxReallocArray(cxDefaultAllocator, test, SIZE_MAX/2, 4); CX_TEST_ASSERT(errno == ENOMEM); CX_TEST_ASSERT(fail == NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_default_free) { void *test = cxMalloc(cxDefaultAllocator, 16); CX_TEST_DO { // we cannot assert sth. but valgrind will detect an error cxFree(cxDefaultAllocator, test); CX_TEST_ASSERT(true); } } CX_TEST(test_allocator_reallocate) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { int ret = cxReallocate(cxDefaultAllocator, &test, 16); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_reallocate_low_level) { void *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { int ret = cx_reallocate(&test, 16); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_reallocatearray) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { int ret = cxReallocateArray(cxDefaultAllocator, &test, 16, 2); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_reallocatearray_overflow) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { int ret = cxReallocateArray(cxDefaultAllocator, &test, SIZE_MAX/2, 4); CX_TEST_ASSERT(ret != 0); CX_TEST_ASSERT(errno == ENOMEM); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_reallocatearray_low_level) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { int ret = cx_reallocatearray(&test, 16, 2); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CX_TEST(test_allocator_reallocatearray_low_level_overflow) { char *test = calloc(8, 1); memcpy(test, "Test", 5); CX_TEST_DO { int ret = cx_reallocatearray(&test, SIZE_MAX/2, 4); CX_TEST_ASSERT(ret != 0); CX_TEST_ASSERT(errno == ENOMEM); CX_TEST_ASSERT(test != NULL); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } static void *test_allocator_mock_failing_realloc( cx_attr_unused void *p, cx_attr_unused void *d, cx_attr_unused size_t n ) { return NULL; } CX_TEST(test_allocator_reallocate_fails) { // Mock an allocator that always returns NULL on realloc cx_allocator_class mock_cl; mock_cl.realloc = test_allocator_mock_failing_realloc; CxAllocator mock = {&mock_cl, NULL}; void *test = calloc(8, 1); memcpy(test, "Test", 5); void *original = test; CX_TEST_DO { int ret = cxReallocate(&mock, &test, 16); // non-zero return code because of the failure CX_TEST_ASSERT(ret != 0); // the test pointer was not changed and still points to the same memory CX_TEST_ASSERT(test == original); CX_TEST_ASSERT(0 == strcmp(test, "Test")); } free(test); } CxTestSuite *cx_test_suite_allocator(void) { CxTestSuite *suite = cx_test_suite_new("allocator"); cx_test_register(suite, test_allocator_default); cx_test_register(suite, test_allocator_default_malloc); cx_test_register(suite, test_allocator_default_calloc); cx_test_register(suite, test_allocator_default_realloc); cx_test_register(suite, test_allocator_default_reallocarray); cx_test_register(suite, test_allocator_default_reallocarray_overflow); cx_test_register(suite, test_allocator_default_free); cx_test_register(suite, test_allocator_reallocate); cx_test_register(suite, test_allocator_reallocate_fails); cx_test_register(suite, test_allocator_reallocate_low_level); cx_test_register(suite, test_allocator_reallocatearray); cx_test_register(suite, test_allocator_reallocatearray_overflow); cx_test_register(suite, test_allocator_reallocatearray_low_level); cx_test_register(suite, test_allocator_reallocatearray_low_level_overflow); return suite; }