/* Unit tests of arena.c Copyright © 2021-2024 Samuel Lidén Borell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "testalloc.h" #define TEST_AALLOC #include "../arena.c" #undef TEST_AALLOC #include "unittest.h" #include "alltests.h" #include "testcommon.h" #include #define MAXCALLS 100 static struct CSlulConfig *cfg; enum MockedFunction { END_OF_CALLS, MALLOC, REALLOC, FREE }; static const char funcnames[][8] = { "", "malloc", "realloc", "free" }; struct MockedCall { enum MockedFunction func; /** For free and realloc calls, this is the index of the alloc call */ size_t ptrindex; /** For malloc and realloc calls, this is the size */ size_t size; /** Whether to simulate a failure */ int fail; }; #define EXPECT_MALLOC(size) { MALLOC, 0, (size), 0 }, #define EXPECT_REALLOC(p, size) { REALLOC, (p), (size), 0 }, #define EXPECT_FREE(p) { FREE, (p), 0, 0 }, #define FAILING_MALLOC(size) { MALLOC, 0, (size), 1 }, #define FAILING_REALLOC(p, size) { REALLOC, (p), (size), 1 }, #define CALLS_END { END_OF_CALLS, 0, 0, 0 } /** Index of current call */ static int call; /** Expected calls (and mock statuses) for this test case */ static const struct MockedCall *calls; /** Logs return values of malloc for checking parameter to free */ static void *returns[MAXCALLS]; static void start(const struct MockedCall *testcalls) { calls = testcalls; memset(returns, 0, sizeof(returns)); call = 0; } static void verify(void) { tsoftassert(calls[call].func == END_OF_CALLS); } static void *mocked_malloc(size_t size) { void *ptr; if (calls[call].func != MALLOC || calls[call].size != size) { char s[200]; sprintf(s, "[Call %d] Unexpected malloc(%u) call, expected %s(<%d>, %u)", call+1, (unsigned)size, funcnames[calls[call].func], (int)calls[call].ptrindex, (unsigned)calls[call].size); tfail(s); } ptr = calls[call].fail ? NULL : malloc(size); returns[call++] = ptr; return ptr; } static void mocked_free(void *ptr) { char s[200]; int i, ok = 1; if (calls[call].func != FREE || returns[calls[call].ptrindex-1] != ptr) { int match = -1; if (ptr) { for (i = 0; i < call; i++) { if (returns[i] == ptr) { match = i+1; break; } } } sprintf(s, "[Call %d] Unexpected free(<%d> %p) call, expected %s(<%d>, %u)", call+1, match, ptr, funcnames[calls[call].func], (int)calls[call].ptrindex, (unsigned)calls[call].size); tsoftfail(s); ok = 0; } for (i = 0; i < call; i++) { if (returns[i] == ptr) { free(ptr); returns[i] = NULL; if (ok) call++; return; } } sprintf(s, "[Call %d] Invalid call to free: %p\n", call+1, ptr); tsoftfail(s); call++; } static void *mocked_realloc(void *ptr, size_t size) { char s[200]; void *newptr; int i; if (calls[call].func != REALLOC || returns[calls[call].ptrindex-1] != ptr) { int match = -1; if (ptr) { for (i = 0; i < call; i++) { if (returns[i] == ptr) { match = i+1; } } } sprintf(s, "[Call %d] Unexpected realloc(<%d> %p, %u) call, expected %s(<%d>, %u)", call+1, match, ptr, (unsigned)size, funcnames[calls[call].func], (int)calls[call].ptrindex, (unsigned)calls[call].size); tsoftfail(s); /* We cannot determine the original size in this case, so we need to do a plain realloc rather than malloc+memcpy+free */ return calls[call].fail ? NULL : realloc(ptr, size); } /* Expected call */ for (i = 0; i < call; i++) { if (returns[i] == ptr) { /* Make sure that the code handles moved allocations! */ if (calls[call].fail) { newptr = NULL; } else { newptr = malloc(size); assert(newptr); memcpy(newptr, ptr, calls[calls[call].ptrindex-1].size); free(ptr); returns[i] = NULL; } returns[call] = newptr; call++; return newptr; } } sprintf(s, "[Call %d] Invalid call to realloc: %p %u\n", call+1, ptr, (unsigned)size); tsoftfail(s); call++; return NULL; } static void msghandler(const struct CSlulState *state) { if (state->errorcode != CSLUL_E_OUTOFMEMORY) { char s[200]; sprintf(s, "Unexpected error %d was reported.", (int)state->errorcode); tsoftfail(s); } } static void create_config_nocheck(void) { static const struct CSlulInitParams prm = { sizeof(struct CSlulInitParams), '/', mocked_malloc, mocked_free, mocked_realloc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; cfg = tcfg(cslul_config_create(&prm)); } static struct CSlul *create_ctx_nocheck(void) { create_config_nocheck(); tassert(cfg != NULL); return tctx(cslul_create(cfg)); } static struct CSlul *create_ctx(void) { struct CSlul *ctx; create_config_nocheck(); tassert(cfg != NULL); cslul_config_set_message_handler(cfg, msghandler); ctx = tctx(cslul_create(cfg)); tassert(ctx != NULL); return ctx; } static void before_test(void) { cfg = NULL; } static void test_align(void) { struct AlignTest { uint32 dummy; char arr[8]; }; struct { struct AlignTest a1; /* In case of platforms where uint32_t has < 4 byte alignment (I don't know of any other way to do this in plain old C89) */ unsigned char dummy2; struct AlignTest a2; unsigned short dummy3; struct AlignTest a3; } t = { { 0, "dummy.." }, 0xFF, { 0, "dummy.." }, 0xFFFF, { 0, "dummy.." } }; struct AlignTest *p; /* Make sure the struct that we test on has 4 byte alignment for "size_t dummy" */ if (((uintptr)&t.a1.dummy & 0x3) == 0) p = &t.a1; else if (((uintptr)&t.a2.dummy & 0x3) == 0) p = &t.a2; else if (((uintptr)&t.a3.dummy & 0x3) == 0) p = &t.a3; else tfail("Strange alignment behavior. Can't run test"); /* These might get optimized out. (void) prevents a warning */ (void)tsoftassert(ALIGN(&p->arr[0], 1) == (uintptr)&p->arr[0]); (void)tsoftassert(ALIGN(&p->arr[1], 1) == (uintptr)&p->arr[1]); (void)tsoftassert(ALIGN(&p->arr[0], 4) == (uintptr)&p->arr[0]); (void)tsoftassert(ALIGN(&p->arr[1], 4) == (uintptr)&p->arr[4]); (void)tsoftassert(ALIGN(&p->arr[2], 4) == (uintptr)&p->arr[4]); (void)tsoftassert(ALIGN(&p->arr[3], 4) == (uintptr)&p->arr[4]); (void)tsoftassert(ALIGN(&p->arr[4], 4) == (uintptr)&p->arr[4]); (void)tsoftassert(ALIGN(&p->arr[5], 4) == (uintptr)&p->arr[8]); (void)tsoftassert(p->dummy == 0); /* prevent variable/field from being unused */ (void)tsoftassert(p->arr[0] == 'd'); /* dito */ } static void test_empty_arena(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) /* For initialization of builtins */ EXPECT_FREE(5) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; start(testcalls); ctx = create_ctx(); cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_aalloc_alignment(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) EXPECT_FREE(5) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; void *ptr; start(testcalls); ctx = create_ctx(); tsoftassert(aalloc(ctx, 7, 1) != NULL); tsoftassert((ptr = aalloc(ctx, 10, 4)) != NULL); tsoftassert(((uintptr)ptr & 3) == 0); /** Allocate once more to be sure we have an unaligned address */ tsoftassert(aalloc(ctx, 7, 1) != NULL); tsoftassert((ptr = aalloc(ctx, 10, 4)) != NULL); tsoftassert(((uintptr)ptr & 3) == 0); cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_aalloc_newblock(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_FREE(5) EXPECT_FREE(6) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; int i; start(testcalls); ctx = create_ctx(); for (i = 0; i < 41; i++) { tsoftassert(aalloc(ctx, 100, 1) != NULL); } cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_aalloc_growlist(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) /* 16 blocks should be allocated until the resize */ EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_REALLOC(4, 32*sizeof(struct ArenaBlock)) EXPECT_FREE(5) EXPECT_FREE(6) EXPECT_FREE(7) EXPECT_FREE(8) EXPECT_FREE(9) EXPECT_FREE(10) EXPECT_FREE(11) EXPECT_FREE(12) EXPECT_FREE(13) EXPECT_FREE(14) EXPECT_FREE(15) EXPECT_FREE(16) EXPECT_FREE(17) EXPECT_FREE(18) EXPECT_FREE(19) EXPECT_FREE(20) EXPECT_FREE(21) EXPECT_FREE(22) /* the realloc */ EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; int i; start(testcalls); ctx = create_ctx(); for (i = 0; i < 40*16+1; i++) { tsoftassert(aalloc(ctx, 100, 1) != NULL); } cslul_free(ctx); cslul_config_free(cfg); verify(); } #define LARGE 3900 /* builtins + 3900 > 4096 */ static void test_aalloc_largealloc(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) /* For initialization of builtins */ /* Large blocks are allocated as if they were arenas. 16 blocks should be allocated until the resize */ EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_MALLOC(LARGE) EXPECT_REALLOC(4, 32*sizeof(struct ArenaBlock)) EXPECT_MALLOC(LARGE) EXPECT_FREE(5) EXPECT_FREE(6) EXPECT_FREE(7) EXPECT_FREE(8) EXPECT_FREE(9) EXPECT_FREE(10) EXPECT_FREE(11) EXPECT_FREE(12) EXPECT_FREE(13) EXPECT_FREE(14) EXPECT_FREE(15) EXPECT_FREE(16) EXPECT_FREE(17) EXPECT_FREE(18) EXPECT_FREE(19) EXPECT_FREE(20) EXPECT_FREE(21) EXPECT_FREE(23) EXPECT_FREE(22) /* the realloc */ EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; int i; start(testcalls); ctx = create_ctx(); for (i = 0; i < 17; i++) { tsoftassert(aalloc(ctx, LARGE, 4) != NULL); } cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_create_outofmem1(void) { static const struct MockedCall testcalls[] = { FAILING_MALLOC(sizeof(struct CSlulConfig)) CALLS_END }; start(testcalls); create_config_nocheck(); tassert(cfg == NULL); verify(); } static void test_create_outofmem2(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) FAILING_MALLOC(sizeof(struct CSlul)) EXPECT_FREE(1) CALLS_END }; start(testcalls); tassert(create_ctx_nocheck() == NULL); cslul_config_free(cfg); verify(); } static void test_create_outofmem3(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) FAILING_MALLOC(sizeof(struct ArenaHolder)) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; start(testcalls); tassert(create_ctx_nocheck() == NULL); cslul_config_free(cfg); verify(); } static void test_create_outofmem4(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) FAILING_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; start(testcalls); tassert(create_ctx_nocheck() == NULL); cslul_config_free(cfg); verify(); } static void test_create_outofmem5(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) FAILING_MALLOC(4096) /* Initialization of builtins */ EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; start(testcalls); tassert(create_ctx_nocheck() == NULL); cslul_config_free(cfg); verify(); } static void test_block_outofmem(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) /* For initialization of builtins */ FAILING_MALLOC(4096) EXPECT_FREE(5) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; struct ArenaBlock *block; start(testcalls); ctx = create_ctx(); block = ctx->arenas->last-1; block->free = block->end; tassert(aalloc(ctx, 1, 1) == NULL); cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_grow_outofmem(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) /* For initialization of builtins */ /* 16 blocks should be allocated until the resize */ EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) EXPECT_MALLOC(4096) FAILING_REALLOC(4, 32*sizeof(struct ArenaBlock)) EXPECT_FREE(21) /* "half-allocated" block gets free'd */ EXPECT_FREE(5) EXPECT_FREE(6) EXPECT_FREE(7) EXPECT_FREE(8) EXPECT_FREE(9) EXPECT_FREE(10) EXPECT_FREE(11) EXPECT_FREE(12) EXPECT_FREE(13) EXPECT_FREE(14) EXPECT_FREE(15) EXPECT_FREE(16) EXPECT_FREE(17) EXPECT_FREE(18) EXPECT_FREE(19) EXPECT_FREE(20) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; int i; struct ArenaBlock *block; start(testcalls); ctx = create_ctx(); /* Fill the initial block to start in a deterministic state */ block = ctx->arenas->last-1; block->free = block->end; /* Fill 15 more blocks to have 16 in total */ for (i = 0; i < 40*15; i++) { tsoftassert(aalloc(ctx, 100, 1) != NULL); } /* This will trigger a resize of the block pointer array, which this test simulates a failure of. */ tsoftassert(aalloc(ctx, 100, 1) == NULL); cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_large_outofmem(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) /* For initialization of builtins */ FAILING_MALLOC(LARGE) EXPECT_FREE(5) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; start(testcalls); ctx = create_ctx(); tsoftassert(aalloc(ctx, LARGE, 4) == NULL); cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_strdup_empty(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) EXPECT_FREE(5) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; char *dup; start(testcalls); ctx = create_ctx(); dup = aalloc_memzdup(ctx, "", 0); tassert(dup != NULL); tsoftassert(dup[0] == '\0'); cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_strdup_nonempty(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) EXPECT_FREE(5) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; char *dup; start(testcalls); ctx = create_ctx(); dup = aalloc_memzdup(ctx, "abc", 3); tassert(dup != NULL); tsoftassert(!strcmp(dup, "abc")); cslul_free(ctx); cslul_config_free(cfg); verify(); } static void test_strdup_outofmem(void) { static const struct MockedCall testcalls[] = { EXPECT_MALLOC(sizeof(struct CSlulConfig)) EXPECT_MALLOC(sizeof(struct CSlul)) EXPECT_MALLOC(sizeof(struct ArenaHolder)) EXPECT_MALLOC(16*sizeof(struct ArenaBlock)) EXPECT_MALLOC(4096) /* For initialization of builtins */ FAILING_MALLOC(4096) EXPECT_FREE(5) EXPECT_FREE(4) EXPECT_FREE(3) EXPECT_FREE(2) EXPECT_FREE(1) CALLS_END }; struct CSlul *ctx; struct ArenaBlock *block; start(testcalls); ctx = create_ctx(); block = ctx->arenas->last-1; block->free = block->end; /* Full block => Force allocation */ tsoftassert(aalloc_memzdup(ctx, "abc", 3) == NULL); cslul_free(ctx); cslul_config_free(cfg); verify(); } const TestFunctionInfo tests_arena[] = { TEST_BEFORE(before_test) TEST_INFO(test_align) TEST_INFO(test_empty_arena) TEST_INFO(test_aalloc_alignment) TEST_INFO(test_aalloc_newblock) TEST_INFO(test_aalloc_growlist) TEST_INFO(test_aalloc_largealloc) TEST_INFO(test_create_outofmem1) TEST_INFO(test_create_outofmem2) TEST_INFO(test_create_outofmem3) TEST_INFO(test_create_outofmem4) TEST_INFO(test_create_outofmem5) TEST_INFO(test_block_outofmem) TEST_INFO(test_grow_outofmem) TEST_INFO(test_large_outofmem) TEST_INFO(test_strdup_empty) TEST_INFO(test_strdup_nonempty) TEST_INFO(test_strdup_outofmem) TEST_END };