#include "listpool.h" #include "test.h" #define NUM_BLOCKS 10 DEF_LISTPOOL(test_pool, int, NUM_BLOCKS); static int count(test_pool* pool) { int count = 0; listpool_foreach(pool, n, { count++; }); return count; } static int sum(test_pool* pool) { int sum = 0; listpool_foreach(pool, n, { sum += *n; }); return sum; } // Create a pool. TEST_CASE(listpool_create) { test_pool pool; listpool_make(&pool); } // Allocate all N blocks. TEST_CASE(listpool_fully_allocate) { test_pool pool; listpool_make(&pool); for (int i = 0; i < NUM_BLOCKS; ++i) { const int* block = listpool_alloc(&pool); TEST_TRUE(block != 0); } } // Allocate all N blocks, then free them. TEST_CASE(listpool_fill_then_free) { test_pool pool; listpool_make(&pool); int* blocks[NUM_BLOCKS] = {0}; for (int i = 0; i < NUM_BLOCKS; i++) { blocks[i] = listpool_alloc(&pool); TEST_TRUE(blocks[i] != 0); } for (int i = 0; i < NUM_BLOCKS; i++) { listpool_free(&pool, &blocks[i]); TEST_EQUAL(blocks[i], 0); // Pointer should be set to 0 on free. } TEST_EQUAL(count(&pool), 0); } // Attempt to allocate blocks past the maximum pool size. // The pool should handle the failed allocations gracefully. TEST_CASE(listpool_allocate_beyond_max_size) { test_pool pool; listpool_make(&pool); // Fully allocate the pool. for (int i = 0; i < NUM_BLOCKS; ++i) { TEST_TRUE(listpool_alloc(&pool) != 0); } // Past the end. for (int i = 0; i < NUM_BLOCKS; ++i) { TEST_EQUAL(listpool_alloc(&pool), 0); } } // Free blocks should always remain zeroed out. // This tests the invariant right after creating the pool. TEST_CASE(listpool_zero_free_blocks_after_creation) { test_pool pool; listpool_make(&pool); const int zero = 0; for (int i = 0; i < NUM_BLOCKS; ++i) { const int* block = (const int*)(pool.blocks) + i; TEST_EQUAL(memcmp(block, &zero, sizeof(int)), 0); } } // Free blocks should always remain zeroed out. // This tests the invariant after freeing a block. TEST_CASE(listpool_zero_free_block_after_free) { test_pool pool; listpool_make(&pool); int* val = listpool_alloc(&pool); TEST_TRUE(val != 0); *val = 177; int* old_val = val; listpool_free(&pool, &val); // val pointer is set to 0. TEST_EQUAL(*old_val, 0); // Block is zeroed out after free. } // Traverse an empty pool. TEST_CASE(listpool_traverse_empty) { test_pool pool; listpool_make(&pool); TEST_EQUAL(count(&pool), 0); } // Traverse a partially full pool. TEST_CASE(listpool_traverse_partially_full) { const int N = NUM_BLOCKS / 2; test_pool pool; listpool_make(&pool); for (int i = 0; i < N; ++i) { int* val = listpool_alloc(&pool); TEST_TRUE(val != 0); *val = i + 1; } TEST_EQUAL(sum(&pool), (N) * (N + 1) / 2); } // Traverse a full pool. TEST_CASE(listpool_traverse_full) { test_pool pool; listpool_make(&pool); for (int i = 0; i < NUM_BLOCKS; ++i) { int* val = listpool_alloc(&pool); TEST_TRUE(val != 0); *val = i + 1; } TEST_EQUAL(sum(&pool), (NUM_BLOCKS) * (NUM_BLOCKS + 1) / 2); } // Remove a value from the list. TEST_CASE(listpool_remove_value) { test_pool pool; listpool_make(&pool); int* x = listpool_alloc(&pool); int* y = listpool_alloc(&pool); TEST_TRUE(x != 0); TEST_TRUE(y != 0); *x = 155; *y = 177; listpool_remove(&pool, 155); // x TEST_EQUAL(count(&pool), 1); TEST_EQUAL(sum(&pool), *y); } // Stress test. // // 1. Allocate the pool, either fully or partially. If fully, attempt to // allocate some items past the end. // // 2. Free all allocated items in some random order. int main() { return 0; }