v1.2.4 - Major rewrite
This commit is contained in:
249
test.c
249
test.c
@@ -1,64 +1,211 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define malloc(s) (printf("%s:%d:%s: malloc(%lu)\n",\
|
||||
__FILE__, __LINE__, __func__, (s)), malloc((s)));
|
||||
#define free(p) (printf("%s:%d:%s: free(%p)\n",\
|
||||
__FILE__, __LINE__, __func__, (p)), free((p)));
|
||||
#endif // DEBUG
|
||||
#include <string.h>
|
||||
|
||||
#define ARENA_IMPLEMENTATION
|
||||
#define ARENA_REGION_GROWTH_FACTOR 2
|
||||
#define ARENA_NO_ASSERT
|
||||
#include "arena.h"
|
||||
|
||||
void arena_print(Arena arena)
|
||||
// Helper to count active regions in the linked list
|
||||
static int count_regions(const Arena *arena)
|
||||
{
|
||||
int i = 0;
|
||||
for (Arena_Region *cur = arena.head; cur != NULL; cur = cur->next) {
|
||||
printf("Arena_Region #%d:\n", ++i);
|
||||
printf(" count = %ld\n", cur->count);
|
||||
printf(" capacity = %ld\n", cur->capacity);
|
||||
printf(" next = %p\n", cur->next);
|
||||
printf(" data = %p\n", cur->data);
|
||||
int count = 0;
|
||||
for (Arena__Region *r = arena->head; r != NULL; r = r->next) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Helper to verify pointer alignment
|
||||
static bool is_aligned(const void *ptr)
|
||||
{
|
||||
return ((uintptr_t)ptr % ARENA_ALIGNMENT) == 0;
|
||||
}
|
||||
|
||||
static void test_edge_case_inputs(void)
|
||||
{
|
||||
printf("Running edge case and invalid input tests...\n");
|
||||
Arena arena = arena_create(1024);
|
||||
|
||||
// 0-byte allocations
|
||||
assert(arena_alloc(&arena, 0) == NULL);
|
||||
assert(arena_calloc(&arena, 0, 10) == NULL);
|
||||
assert(arena_calloc(&arena, 10, 0) == NULL);
|
||||
|
||||
// NULL pointer handling
|
||||
assert(arena_alloc(NULL, 100) == NULL);
|
||||
assert(arena_calloc(NULL, 1, 1) == NULL);
|
||||
assert(arena_copy(NULL, "data", 4) == NULL);
|
||||
assert(arena_copy(&arena, NULL, 4) == NULL);
|
||||
assert(arena_strdup(NULL, "test") == NULL);
|
||||
assert(arena_strdup(&arena, NULL) == NULL);
|
||||
assert(arena_sprintf(NULL, "test") == NULL);
|
||||
assert(arena_sprintf(&arena, NULL) == NULL);
|
||||
|
||||
// Snapshot/Rewind/Trim edge cases on NULL or empty
|
||||
arena_trim(NULL); // Should not crash
|
||||
arena_reset(NULL); // Should not crash
|
||||
arena_free(NULL); // Should not crash
|
||||
|
||||
ArenaMarker null_marker = arena_snapshot(NULL);
|
||||
assert(null_marker.region == NULL && null_marker.count == 0);
|
||||
arena_rewind(NULL, null_marker); // Should not crash
|
||||
|
||||
arena_free(&arena);
|
||||
}
|
||||
|
||||
static void test_overflows(void)
|
||||
{
|
||||
printf("Running integer overflow tests...\n");
|
||||
Arena arena = arena_create(1024);
|
||||
|
||||
// calloc multiplication overflow (count * size > SIZE_MAX)
|
||||
size_t massive_count = SIZE_MAX / 2;
|
||||
size_t massive_size = 4;
|
||||
assert(arena_calloc(&arena, massive_count, massive_size) == NULL);
|
||||
|
||||
// arena_alloc size wrap-around on alignment
|
||||
// arena__align(SIZE_MAX) will overflow back to a small number
|
||||
size_t overflow_align_size = SIZE_MAX - (ARENA_ALIGNMENT / 2);
|
||||
assert(arena_alloc(&arena, overflow_align_size) == NULL);
|
||||
|
||||
arena_free(&arena);
|
||||
}
|
||||
|
||||
static void test_geometric_growth_and_stress(void)
|
||||
{
|
||||
printf("Running geometric growth and stress tests...\n");
|
||||
|
||||
size_t initial_capacity = 64;
|
||||
Arena arena = arena_create(initial_capacity);
|
||||
|
||||
// Exhaust the first region to force geometric growth
|
||||
assert(arena_alloc(&arena, 32) != NULL); // Head allocated with capacity 64
|
||||
assert(arena.head->capacity == 64);
|
||||
|
||||
assert(arena_alloc(&arena, 32) != NULL); // Head is now full
|
||||
|
||||
// Trigger next region (should be 64 * 2 = 128)
|
||||
assert(arena_alloc(&arena, 32) != NULL);
|
||||
assert(arena.tail->capacity == 128);
|
||||
|
||||
// Trigger next region (should be 128 * 2 = 256)
|
||||
assert(arena_alloc(&arena, 128) != NULL);
|
||||
assert(arena.tail->capacity == 256);
|
||||
|
||||
// Stress test: Allocate 10,000 small blocks
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
void *ptr = arena_alloc(&arena, 7); // Odd size to test alignment scaling
|
||||
assert(ptr != NULL);
|
||||
assert(is_aligned(ptr));
|
||||
}
|
||||
|
||||
// Verify regions were created
|
||||
int regions = count_regions(&arena);
|
||||
assert(regions > 3);
|
||||
|
||||
// Huge allocation bypassing standard growth
|
||||
// Requesting 1MB when the next capacity might only be ~4KB
|
||||
void *huge = arena_alloc(&arena, 1024 * 1024);
|
||||
assert(huge != NULL);
|
||||
assert(arena.tail->capacity >= 1024 * 1024);
|
||||
|
||||
arena_free(&arena);
|
||||
}
|
||||
|
||||
static void test_snapshot_rewind_trim_logic(void)
|
||||
{
|
||||
printf("Running snapshot, rewind, and trim logic tests...\n");
|
||||
Arena arena = arena_create(128);
|
||||
|
||||
// 1. Snapshot empty
|
||||
ArenaMarker empty_marker = arena_snapshot(&arena);
|
||||
assert(empty_marker.region == NULL);
|
||||
|
||||
// 2. Allocate across 3 regions
|
||||
arena_alloc(&arena, 100); // Region 1 (Cap: 128)
|
||||
arena_snapshot(&arena);
|
||||
|
||||
arena_alloc(&arena, 200); // Region 2 (Cap: 256)
|
||||
ArenaMarker mark_r2 = arena_snapshot(&arena);
|
||||
|
||||
arena_alloc(&arena, 400); // Region 3 (Cap: 512)
|
||||
assert(count_regions(&arena) == 3);
|
||||
|
||||
// 3. Rewind to Region 2
|
||||
arena_rewind(&arena, mark_r2);
|
||||
assert(arena.tail == mark_r2.region);
|
||||
assert(arena.tail->count == mark_r2.count);
|
||||
|
||||
// Check that Region 3's count was reset to 0
|
||||
assert(arena.tail->next != NULL);
|
||||
assert(arena.tail->next->count == 0);
|
||||
|
||||
// 4. Trim trailing regions (Should drop Region 3)
|
||||
arena_trim(&arena);
|
||||
assert(arena.tail->next == NULL);
|
||||
assert(count_regions(&arena) == 2);
|
||||
|
||||
// 5. Rewind to completely empty
|
||||
arena_rewind(&arena, empty_marker);
|
||||
assert(arena.tail == NULL || (arena.tail == arena.head && arena.tail->count == 0));
|
||||
|
||||
// Trim when virtually empty (should retain the head region if it exists)
|
||||
arena_trim(&arena);
|
||||
if (arena.head != NULL) {
|
||||
assert(arena.head->next == NULL);
|
||||
}
|
||||
|
||||
arena_free(&arena);
|
||||
}
|
||||
|
||||
static void test_massive_string_formatting(void)
|
||||
{
|
||||
printf("Running massive string formatting tests...\n");
|
||||
Arena arena = arena_create(1024);
|
||||
|
||||
// Create a 50,000 character string requirement
|
||||
size_t massive_len = 50000;
|
||||
char *massive_input = malloc(massive_len + 1);
|
||||
assert(massive_input != NULL);
|
||||
memset(massive_input, 'X', massive_len);
|
||||
massive_input[massive_len] = '\0';
|
||||
|
||||
char *result = arena_sprintf(&arena, "PREFIX_%s_SUFFIX", massive_input);
|
||||
assert(result != NULL);
|
||||
|
||||
size_t expected_length = 7 + massive_len + 7;
|
||||
assert(strlen(result) == expected_length);
|
||||
assert(strncmp(result, "PREFIX_XXX", 10) == 0);
|
||||
assert(strcmp(result + expected_length - 7, "_SUFFIX") == 0);
|
||||
|
||||
free(massive_input);
|
||||
arena_free(&arena);
|
||||
}
|
||||
|
||||
static void test_zero_capacity_fallback(void)
|
||||
{
|
||||
printf("Running zero-capacity fallback tests...\n");
|
||||
|
||||
// Passing 0 should fall back to ARENA_REGION_CAPACITY implicitly on first alloc
|
||||
Arena arena = arena_create(0);
|
||||
|
||||
void *ptr = arena_alloc(&arena, 10);
|
||||
assert(ptr != NULL);
|
||||
assert(arena.head->capacity >= ARENA_REGION_CAPACITY);
|
||||
|
||||
arena_free(&arena);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("struct Arena_Region {\n");
|
||||
printf(" Arena_Region *next; (%zu)\n", offsetof(Arena_Region, next));
|
||||
printf(" size_t count; (%zu)\n", offsetof(Arena_Region, count));
|
||||
printf(" size_t capacity; (%zu)\n", offsetof(Arena_Region, capacity));
|
||||
printf(" uint8_t data[]; (%zu)\n", offsetof(Arena_Region, data));
|
||||
printf("};\n");
|
||||
test_edge_case_inputs();
|
||||
test_overflows();
|
||||
test_geometric_growth_and_stress();
|
||||
test_snapshot_rewind_trim_logic();
|
||||
test_massive_string_formatting();
|
||||
test_zero_capacity_fallback();
|
||||
|
||||
printf("Alignment: %zu\n", ARENA_ALIGNMENT);
|
||||
printf("================================================================\n");
|
||||
|
||||
Arena a = {0};
|
||||
|
||||
arena_alloc(&a, 4012);
|
||||
arena_alloc(&a, 100);
|
||||
arena_alloc(&a, 2301);
|
||||
arena_alloc(&a, 4015);
|
||||
arena_alloc(&a, 10000);
|
||||
|
||||
arena_reset(&a);
|
||||
|
||||
arena_alloc(&a, 1000);
|
||||
|
||||
arena_print(a);
|
||||
|
||||
char *str = arena_sprintf(&a, "Formatted string %x", 0xCAFE);
|
||||
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
str = arena_sprintf(&a, "Formatted string %d", i);
|
||||
}
|
||||
|
||||
printf("%s\n", str);
|
||||
|
||||
arena_print(a);
|
||||
arena_free(&a);
|
||||
return 0;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user