tests/test_mempool.c

changeset 1328
2cf66dee40b8
parent 1325
20caf6efaf07
--- a/tests/test_mempool.c	Fri May 23 12:44:24 2025 +0200
+++ b/tests/test_mempool.c	Fri May 23 13:36:11 2025 +0200
@@ -31,6 +31,19 @@
 
 #include "cx/mempool.h"
 
+#include <errno.h>
+
+static unsigned test_mempool_destructor_called;
+
+static void test_mempool_destructor(cx_attr_unused void *mem) {
+    test_mempool_destructor_called++;
+}
+
+static void test_mempool_destructor2(void *data, cx_attr_unused void *mem) {
+    int *ctr = data;
+    *ctr = *ctr + 1;
+}
+
 CX_TEST(test_mempool_create) {
     CxMempool *pool = cxMempoolCreateSimple(16);
     CX_TEST_DO {
@@ -51,21 +64,69 @@
     cxMempoolFree(pool);
 }
 
+static CX_TEST_SUBROUTINE(test_mempool_malloc_verify, CxMempool *pool) {
+    CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
+    CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
+    CX_TEST_ASSERT(pool->size == 2);
+    CX_TEST_ASSERT(pool->capacity == 4);
+    CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
+    CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
+    CX_TEST_ASSERT(pool->size == 4);
+    CX_TEST_ASSERT(pool->capacity == 4);
+    CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
+    int *i = cxMalloc(pool->allocator, sizeof(int));
+    CX_TEST_ASSERT(i != NULL);
+    *i = 4083914; // let asan / valgrind check
+    CX_TEST_ASSERT(pool->size == 6);
+    CX_TEST_ASSERT(pool->capacity >= 6);
+}
+
+CX_TEST(test_mempool_malloc0) {
+    CxMempool *pool = cxMempoolCreatePure(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_malloc_verify, pool);
+    }
+    cxMempoolFree(pool);
+}
+
 CX_TEST(test_mempool_malloc) {
     CxMempool *pool = cxMempoolCreateSimple(4);
     CX_TEST_DO {
-        CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
-        CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
-        CX_TEST_ASSERT(pool->size == 2);
-        CX_TEST_ASSERT(pool->capacity == 4);
-        CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
-        CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
-        CX_TEST_ASSERT(pool->size == 4);
-        CX_TEST_ASSERT(pool->capacity == 4);
-        CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
-        CX_TEST_ASSERT(cxMalloc(pool->allocator, sizeof(int)) != NULL);
-        CX_TEST_ASSERT(pool->size == 6);
-        CX_TEST_ASSERT(pool->capacity >= 6);
+        CX_TEST_CALL_SUBROUTINE(test_mempool_malloc_verify, pool);
+    }
+    cxMempoolFree(pool);
+}
+
+CX_TEST(test_mempool_malloc2) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_malloc_verify, pool);
+    }
+    cxMempoolFree(pool);
+}
+
+static CX_TEST_SUBROUTINE(test_mempool_calloc_verify, CxMempool *pool) {
+    int *test = cxCalloc(pool->allocator, 2, sizeof(int));
+    CX_TEST_ASSERT(test != NULL);
+    CX_TEST_ASSERT(test[0] == 0);
+    CX_TEST_ASSERT(test[1] == 0);
+#if __GNUC__ > 11
+// we want to explicitly test the overflow
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Walloc-size-larger-than="
+#endif
+    errno = 0;
+    CX_TEST_ASSERT(NULL == cxCalloc(pool->allocator, SIZE_MAX / 2, sizeof(int)));
+    CX_TEST_ASSERT(errno == EOVERFLOW);
+#if __GNUC__ > 11
+#pragma GCC diagnostic pop
+#endif
+}
+
+CX_TEST(test_mempool_calloc0) {
+    CxMempool *pool = cxMempoolCreatePure(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_calloc_verify, pool);
     }
     cxMempoolFree(pool);
 }
@@ -73,66 +134,121 @@
 CX_TEST(test_mempool_calloc) {
     CxMempool *pool = cxMempoolCreateSimple(4);
     CX_TEST_DO {
-        int *test = cxCalloc(pool->allocator, 2, sizeof(int));
-        CX_TEST_ASSERT(test != NULL);
-        CX_TEST_ASSERT(test[0] == 0);
-        CX_TEST_ASSERT(test[1] == 0);
+        CX_TEST_CALL_SUBROUTINE(test_mempool_calloc_verify, pool);
+    }
+    cxMempoolFree(pool);
+}
+
+CX_TEST(test_mempool_calloc2) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_calloc_verify, pool);
     }
     cxMempoolFree(pool);
 }
 
-static unsigned test_mempool_destructor_called;
+static CX_TEST_SUBROUTINE(test_mempool_realloc_verify, CxMempool *pool, enum cx_mempool_type type) {
+    // use realloc with NULL which shall behave as a malloc
+    int *data = cxRealloc(pool->allocator, NULL, 2*sizeof(int));
+    if (type == CX_MEMPOOL_TYPE_SIMPLE) {
+        cxMempoolSetDestructor(data, test_mempool_destructor);
+    } else if (type == CX_MEMPOOL_TYPE_ADVANCED) {
+        cxMempoolSetDestructor2(data, test_mempool_destructor2, &test_mempool_destructor_called);
+    }
+    *data = 13;
+
+    // shrink to actual sizeof(int)
+    data = cxRealloc(pool->allocator, data, sizeof(int));
+    CX_TEST_ASSERT(*data == 13);
+
+    // realloc with the same size (should not do anything)
+    data = cxRealloc(pool->allocator, data, sizeof(int));
+    CX_TEST_ASSERT(*data == 13);
 
-static void test_mempool_destructor(cx_attr_unused void *mem) {
-    test_mempool_destructor_called++;
+    // now try hard to trigger a memmove
+    int *rdata = data;
+    unsigned n = 1;
+    while (rdata == data) {
+        n <<= 1;
+        // eventually the memory should be moved elsewhere
+        CX_TEST_ASSERTM(n < 65536, "Reallocation attempt failed - test not executable");
+        rdata = cxRealloc(pool->allocator, data, n * sizeof(intptr_t));
+    }
+
+    CX_TEST_ASSERT(*rdata == 13);
+    // test if destructor is still intact
+    if (type != CX_MEMPOOL_TYPE_PURE) {
+        test_mempool_destructor_called = 0;
+        cxFree(pool->allocator, rdata);
+        CX_TEST_ASSERT(test_mempool_destructor_called == 1);
+    }
+}
+
+CX_TEST(test_mempool_realloc0) {
+    CxMempool *pool = cxMempoolCreatePure(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_realloc_verify, pool, CX_MEMPOOL_TYPE_PURE);
+    }
+    cxMempoolFree(pool);
 }
 
 CX_TEST(test_mempool_realloc) {
     CxMempool *pool = cxMempoolCreateSimple(4);
-    cxMempoolGlobalDestructor(pool, test_mempool_destructor);
     CX_TEST_DO {
-        CX_TEST_ASSERT(pool->destr == test_mempool_destructor);
-        int *data = cxMalloc(pool->allocator, sizeof(int));
-        *data = 13;
+        CX_TEST_CALL_SUBROUTINE(test_mempool_realloc_verify, pool, CX_MEMPOOL_TYPE_SIMPLE);
+    }
+    cxMempoolFree(pool);
+}
 
-        int *rdata = data;
-        unsigned n = 1;
-        while (rdata == data) {
-            n <<= 1;
-            // eventually the memory should be moved elsewhere
-            CX_TEST_ASSERTM(n < 65536, "Reallocation attempt failed - test not executable");
-            rdata = cxRealloc(pool->allocator, data, n * sizeof(intptr_t));
-        }
-
-        CX_TEST_ASSERT(*rdata == 13);
-        // test if destructor is still intact
-        test_mempool_destructor_called = 0;
-        cxFree(pool->allocator, rdata);
-        CX_TEST_ASSERT(test_mempool_destructor_called == 1);
+CX_TEST(test_mempool_realloc2) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_realloc_verify, pool, CX_MEMPOOL_TYPE_ADVANCED);
     }
     cxMempoolFree(pool);
 }
 
+static CX_TEST_SUBROUTINE(test_mempool_free_verify, CxMempool *pool) {
+    void *mem1, *mem2;
+    mem1 = cxMalloc(pool->allocator, 16);
+    cxFree(pool->allocator, mem1);
+    CX_TEST_ASSERT(pool->size == 0);
+
+    mem1 = cxMalloc(pool->allocator, 16);
+    mem1 = cxMalloc(pool->allocator, 16);
+    mem1 = cxMalloc(pool->allocator, 16);
+    mem2 = cxMalloc(pool->allocator, 16);
+    mem2 = cxMalloc(pool->allocator, 16);
+
+    CX_TEST_ASSERT(pool->size == 5);
+    // a realloc with size zero shall behave like a free
+    void *freed = cxRealloc(pool->allocator, mem1, 0);
+    CX_TEST_ASSERT(freed == NULL);
+    CX_TEST_ASSERT(pool->size == 4);
+    cxFree(pool->allocator, mem2);
+    CX_TEST_ASSERT(pool->size == 3);
+}
+
+CX_TEST(test_mempool_free0) {
+    CxMempool *pool = cxMempoolCreatePure(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_free_verify, pool);
+    }
+    cxMempoolFree(pool);
+}
 
 CX_TEST(test_mempool_free) {
     CxMempool *pool = cxMempoolCreateSimple(4);
-    void *mem1, *mem2;
     CX_TEST_DO {
-        mem1 = cxMalloc(pool->allocator, 16);
-        cxFree(pool->allocator, mem1);
-        CX_TEST_ASSERT(pool->size == 0);
+        CX_TEST_CALL_SUBROUTINE(test_mempool_free_verify, pool);
+    }
+    cxMempoolFree(pool);
+}
 
-        mem1 = cxMalloc(pool->allocator, 16);
-        mem1 = cxMalloc(pool->allocator, 16);
-        mem1 = cxMalloc(pool->allocator, 16);
-        mem2 = cxMalloc(pool->allocator, 16);
-        mem2 = cxMalloc(pool->allocator, 16);
-
-        CX_TEST_ASSERT(pool->size == 5);
-        cxFree(pool->allocator, mem1);
-        CX_TEST_ASSERT(pool->size == 4);
-        cxFree(pool->allocator, mem2);
-        CX_TEST_ASSERT(pool->size == 3);
+CX_TEST(test_mempool_free2) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_free_verify, pool);
     }
     cxMempoolFree(pool);
 }
@@ -154,14 +270,124 @@
     }
 }
 
+CX_TEST(test_mempool_destroy2) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        int *data = cxMalloc(pool->allocator, sizeof(int));
+        int ctr = 0;
+        *data = 47;
+        cxMempoolSetDestructor2(data, test_mempool_destructor2, &ctr);
+        CX_TEST_ASSERT(*data == 47);
+        cxFree(pool->allocator, data);
+        CX_TEST_ASSERT(ctr == 1);
+        data = cxMalloc(pool->allocator, sizeof(int));
+        cxMempoolSetDestructor2(data, test_mempool_destructor2, &ctr);
+        cxMempoolFree(pool);
+        CX_TEST_ASSERT(ctr == 2);
+    }
+}
+
+CX_TEST(test_mempool_remove_destructor) {
+    CxMempool *pool = cxMempoolCreateSimple(4);
+    CX_TEST_DO {
+        int *data = cxMalloc(pool->allocator, sizeof(int));
+        *data = 13;
+        cxMempoolSetDestructor(data, test_mempool_destructor);
+        CX_TEST_ASSERT(*data == 13);
+        cxMempoolRemoveDestructor(data);
+        CX_TEST_ASSERT(*data == 13);
+        test_mempool_destructor_called = 0;
+        cxFree(pool->allocator, data);
+        CX_TEST_ASSERT(test_mempool_destructor_called == 0);
+        data = cxMalloc(pool->allocator, sizeof(int));
+        *data = 99;
+        cxMempoolSetDestructor(data, test_mempool_destructor);
+        cxMempoolRemoveDestructor(data);
+        CX_TEST_ASSERT(*data == 99);
+        cxMempoolFree(pool);
+        CX_TEST_ASSERT(test_mempool_destructor_called == 0);
+    }
+}
+
+CX_TEST(test_mempool_remove_destructor2) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        int *data = cxMalloc(pool->allocator, sizeof(int));
+        int ctr = 0;
+        *data = 47;
+        cxMempoolSetDestructor2(data, test_mempool_destructor2, &ctr);
+        CX_TEST_ASSERT(*data == 47);
+        cxMempoolRemoveDestructor2(data);
+        CX_TEST_ASSERT(*data == 47);
+        cxFree(pool->allocator, data);
+        CX_TEST_ASSERT(ctr == 0);
+        data = cxMalloc(pool->allocator, sizeof(int));
+        *data = 99;
+        cxMempoolSetDestructor2(data, test_mempool_destructor2, &ctr);
+        cxMempoolRemoveDestructor2(data);
+        CX_TEST_ASSERT(*data == 99);
+        cxMempoolFree(pool);
+        CX_TEST_ASSERT(ctr == 0);
+    }
+}
+
+static CX_TEST_SUBROUTINE(test_mempool_global_destructors_verify, CxMempool *pool) {
+    int *data = cxMalloc(pool->allocator, sizeof(int));
+    int ctr = 0;
+    cxMempoolGlobalDestructor(pool, test_mempool_destructor);
+    cxMempoolGlobalDestructor2(pool, test_mempool_destructor2, &ctr);
+    test_mempool_destructor_called = 0;
+    cxFree(pool->allocator, data);
+    CX_TEST_ASSERT(ctr == 1);
+    CX_TEST_ASSERT(test_mempool_destructor_called == 1);
+    data = cxMalloc(pool->allocator, sizeof(int));
+    cxMempoolFree(pool);
+    CX_TEST_ASSERT(ctr == 2);
+    CX_TEST_ASSERT(test_mempool_destructor_called == 2);
+}
+
+CX_TEST(test_mempool_global_destructors0) {
+    CxMempool *pool = cxMempoolCreatePure(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_global_destructors_verify, pool);
+    }
+}
+
+CX_TEST(test_mempool_global_destructors) {
+    CxMempool *pool = cxMempoolCreateSimple(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_global_destructors_verify, pool);
+    }
+}
+
+CX_TEST(test_mempool_global_destructors2) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        CX_TEST_CALL_SUBROUTINE(test_mempool_global_destructors_verify, pool);
+    }
+}
+
 CX_TEST(test_mempool_register) {
+    CxMempool *pool = cxMempoolCreateAdvanced(4);
+    CX_TEST_DO {
+        int *data = cxMalloc(pool->allocator, sizeof(int));
+        test_mempool_destructor_called = 0;
+        cxMempoolSetDestructor2(data, test_mempool_destructor2, &test_mempool_destructor_called);
+        int donotfree = 0;
+        cxMempoolRegister(pool, &donotfree, test_mempool_destructor);
+        cxMempoolFree(pool);
+        CX_TEST_ASSERT(test_mempool_destructor_called == 2);
+    }
+}
+
+CX_TEST(test_mempool_register2) {
     CxMempool *pool = cxMempoolCreateSimple(4);
     CX_TEST_DO {
         int *data = cxMalloc(pool->allocator, sizeof(int));
         test_mempool_destructor_called = 0;
         cxMempoolSetDestructor(data, test_mempool_destructor);
         int donotfree = 0;
-        cxMempoolRegister(pool, &donotfree, test_mempool_destructor);
+        cxMempoolRegister2(pool, &donotfree, test_mempool_destructor2, &test_mempool_destructor_called);
         cxMempoolFree(pool);
         CX_TEST_ASSERT(test_mempool_destructor_called == 2);
     }
@@ -222,50 +448,75 @@
     CxMempool *src = cxMempoolCreateSimple(4);
     CxMempool *dest = cxMempoolCreateSimple(4);
     CX_TEST_DO {
+        int *a = cxMalloc(src->allocator, sizeof(int));
         int *b = cxMalloc(src->allocator, sizeof(int));
-        b = cxMalloc(src->allocator, sizeof(int));
         int *c = malloc(sizeof(int));
         cxMempoolRegister(src, c, free);
+        int *d = malloc(sizeof(int));
+        cxMempoolRegister(src, d, free);
         CX_TEST_ASSERT(src->size == 2);
-        CX_TEST_ASSERT(src->registered_size == 1);
+        CX_TEST_ASSERT(src->registered_size == 2);
 
-        int result = cxMempoolTransferObject(src, dest, b);
+        int result = cxMempoolTransferObject(src, dest, a);
         CX_TEST_ASSERT(result == 0);
         CX_TEST_ASSERT(src->size == 1);
+        CX_TEST_ASSERT(src->registered_size == 2);
         CX_TEST_ASSERT(dest->size == 1);
-        result = cxMempoolTransferObject(src, dest, b);
+        CX_TEST_ASSERT(dest->registered_size == 0);
+        result = cxMempoolTransferObject(src, dest, a);
+        CX_TEST_ASSERT(result != 0);
+        CX_TEST_ASSERT(src->size == 1);
+        CX_TEST_ASSERT(src->registered_size == 2);
+        CX_TEST_ASSERT(dest->size == 1);
+        CX_TEST_ASSERT(dest->registered_size == 0);
+
+        // can also transfer foreign memory this way
+        result = cxMempoolTransferObject(src, dest, c);
+        CX_TEST_ASSERT(result == 0);
+        CX_TEST_ASSERT(src->size == 1);
+        CX_TEST_ASSERT(src->registered_size == 1);
+        CX_TEST_ASSERT(dest->size == 1);
+        CX_TEST_ASSERT(dest->registered_size == 1);
+
+        // src==dest is an error
+        result = cxMempoolTransferObject(dest, dest, b);
         CX_TEST_ASSERT(result != 0);
         CX_TEST_ASSERT(src->size == 1);
         CX_TEST_ASSERT(dest->size == 1);
 
-        // can also transfer foreign memory this way
-        CX_TEST_ASSERT(src->registered_size == 1);
-        CX_TEST_ASSERT(dest->registered_size == 0);
-        result = cxMempoolTransferObject(src, dest, c);
-        CX_TEST_ASSERT(result == 0);
-        CX_TEST_ASSERT(src->registered_size == 0);
-        CX_TEST_ASSERT(dest->registered_size == 1);
-
-        result = cxMempoolTransferObject(dest, dest, b);
-        CX_TEST_ASSERT(result != 0);
-        CX_TEST_ASSERT(src->size == 1);
-        CX_TEST_ASSERT(dest->size == 1);
+        // check that we don't die when we free memory that's still in the source pool
+        cxFree(src->allocator, b);
     }
     cxMempoolFree(src);
     cxMempoolFree(dest);
-    // let valgrind check that everything worked
+    // let valgrind check that everything else worked
 }
 
 CxTestSuite *cx_test_suite_mempool(void) {
     CxTestSuite *suite = cx_test_suite_new("mempool");
 
     cx_test_register(suite, test_mempool_create);
+    cx_test_register(suite, test_mempool_malloc0);
+    cx_test_register(suite, test_mempool_calloc0);
+    cx_test_register(suite, test_mempool_realloc0);
+    cx_test_register(suite, test_mempool_free0);
     cx_test_register(suite, test_mempool_malloc);
     cx_test_register(suite, test_mempool_calloc);
     cx_test_register(suite, test_mempool_realloc);
     cx_test_register(suite, test_mempool_free);
     cx_test_register(suite, test_mempool_destroy);
+    cx_test_register(suite, test_mempool_malloc2);
+    cx_test_register(suite, test_mempool_calloc2);
+    cx_test_register(suite, test_mempool_realloc2);
+    cx_test_register(suite, test_mempool_free2);
+    cx_test_register(suite, test_mempool_destroy2);
+    cx_test_register(suite, test_mempool_remove_destructor);
+    cx_test_register(suite, test_mempool_remove_destructor2);
+    cx_test_register(suite, test_mempool_global_destructors0);
+    cx_test_register(suite, test_mempool_global_destructors);
+    cx_test_register(suite, test_mempool_global_destructors2);
     cx_test_register(suite, test_mempool_register);
+    cx_test_register(suite, test_mempool_register2);
     cx_test_register(suite, test_mempool_transfer);
     cx_test_register(suite, test_mempool_transfer_object);
 

mercurial