aboutsummaryrefslogtreecommitdiff
path: root/mem/test/mem_test.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2023-07-13 08:22:18 -0700
committer3gg <3gg@shellblade.net>2023-07-13 08:22:18 -0700
commit9f254f0c7b03236be615b1235cf3fc765d6000ea (patch)
treef0b878ef2b431b909d9efd45c1f9ec8ed8ca54f8 /mem/test/mem_test.c
parentfc5886c75ab2626acbc0d7b3db475d17d2cbe01f (diff)
Add mem allocator, remove listpool.
Diffstat (limited to 'mem/test/mem_test.c')
-rw-r--r--mem/test/mem_test.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/mem/test/mem_test.c b/mem/test/mem_test.c
new file mode 100644
index 0000000..6ab4c7c
--- /dev/null
+++ b/mem/test/mem_test.c
@@ -0,0 +1,232 @@
1#include "mem.h"
2
3#include "test.h"
4
5#define NUM_BLOCKS 10
6
7DEF_MEM(test_mem, int, NUM_BLOCKS);
8
9static int count(test_mem* mem) {
10 int count = 0;
11 mem_foreach(mem, n, { count++; });
12 return count;
13}
14
15static int sum(test_mem* mem) {
16 int sum = 0;
17 mem_foreach(mem, n, { sum += *n; });
18 return sum;
19}
20
21// Create a statically-backed allocator.
22TEST_CASE(mem_create) {
23 test_mem mem;
24 mem_make(&mem);
25}
26
27// Create a dynamically-backed allocator.
28TEST_CASE(mem_create_dyn) {
29 DEF_MEM_DYN(dyn_mem, int);
30
31 dyn_mem mem;
32 mem_make_dyn(&mem, NUM_BLOCKS, sizeof(int));
33}
34
35// Allocate N chunks of 1 block each.
36TEST_CASE(mem_fully_allocate) {
37 test_mem mem;
38 mem_make(&mem);
39
40 for (int i = 0; i < NUM_BLOCKS; ++i) {
41 const int* block = mem_alloc(&mem, 1);
42 TEST_TRUE(block != 0);
43 }
44}
45
46// Allocate N chunks of 1 block each, then free them.
47TEST_CASE(mem_fill_then_free) {
48 test_mem mem;
49 mem_make(&mem);
50
51 int* blocks[NUM_BLOCKS] = {0};
52 for (int i = 0; i < NUM_BLOCKS; i++) {
53 blocks[i] = mem_alloc(&mem, 1);
54 TEST_TRUE(blocks[i] != 0);
55 }
56
57 for (int i = 0; i < NUM_BLOCKS; i++) {
58 mem_free(&mem, &blocks[i]);
59 TEST_EQUAL(blocks[i], 0); // Pointer should be set to 0 on free.
60 }
61
62 TEST_EQUAL(count(&mem), 0);
63}
64
65// Attempt to allocate blocks past the maximum allocator size.
66// The allocator should handle the failed allocations gracefully.
67TEST_CASE(mem_allocate_beyond_max_size) {
68 test_mem mem;
69 mem_make(&mem);
70
71 // Fully allocate the mem.
72 for (int i = 0; i < NUM_BLOCKS; ++i) {
73 TEST_TRUE(mem_alloc(&mem, 1) != 0);
74 }
75
76 // Past the end.
77 for (int i = 0; i < NUM_BLOCKS; ++i) {
78 TEST_EQUAL(mem_alloc(&mem, 1), 0);
79 }
80}
81
82// Free blocks should always remain zeroed out.
83// This tests the invariant right after creating the allocator.
84TEST_CASE(mem_zero_free_blocks_after_creation) {
85 test_mem mem;
86 mem_make(&mem);
87
88 const int zero = 0;
89 for (int i = 0; i < NUM_BLOCKS; ++i) {
90 const int* block = (const int*)(mem.blocks) + i;
91 TEST_EQUAL(memcmp(block, &zero, sizeof(int)), 0);
92 }
93}
94
95// Free blocks should always remain zeroed out.
96// This tests the invariant after freeing a block.
97TEST_CASE(mem_zero_free_block_after_free) {
98 test_mem mem;
99 mem_make(&mem);
100
101 int* val = mem_alloc(&mem, 1);
102 TEST_TRUE(val != 0);
103 *val = 177;
104
105 int* old_val = val;
106 mem_free(&mem, &val); // val pointer is set to 0.
107 TEST_EQUAL(*old_val, 0); // Block is zeroed out after free.
108}
109
110// Traverse an empty allocator.
111TEST_CASE(mem_traverse_empty) {
112 test_mem mem;
113 mem_make(&mem);
114
115 TEST_EQUAL(count(&mem), 0);
116}
117
118// Traverse a partially full allocator.
119TEST_CASE(mem_traverse_partially_full) {
120 const int N = NUM_BLOCKS / 2;
121
122 test_mem mem;
123 mem_make(&mem);
124
125 for (int i = 0; i < N; ++i) {
126 int* val = mem_alloc(&mem, 1);
127 TEST_TRUE(val != 0);
128 *val = i + 1;
129 }
130
131 TEST_EQUAL(sum(&mem), (N) * (N + 1) / 2);
132}
133
134// Traverse a full allocator.
135TEST_CASE(mem_traverse_full) {
136 test_mem mem;
137 mem_make(&mem);
138
139 for (int i = 0; i < NUM_BLOCKS; ++i) {
140 int* val = mem_alloc(&mem, 1);
141 TEST_TRUE(val != 0);
142 *val = i + 1;
143 }
144
145 TEST_EQUAL(sum(&mem), (NUM_BLOCKS) * (NUM_BLOCKS + 1) / 2);
146}
147
148// Get the ith (allocated) chunk.
149TEST_CASE(mem_get_block) {
150 test_mem mem;
151 mem_make(&mem);
152
153 for (int i = 0; i < NUM_BLOCKS; ++i) {
154 int* block = mem_alloc(&mem, 1);
155 TEST_TRUE(block != 0);
156 *block = i;
157 TEST_EQUAL(mem_get_chunk_handle(&mem, block), (size_t)i);
158 }
159
160 for (int i = 0; i < NUM_BLOCKS; ++i) {
161 TEST_EQUAL(*mem_get_chunk(&mem, i), i);
162 }
163}
164
165// Test merging.
166// 1. Allocate chunks of variable sizes.
167// 2. Free them in a different order.
168// 3. Then we should be able to allocate 1 chunk of N blocks.
169TEST_CASE(mem_fragmentation) {
170 test_mem mem;
171 mem_make(&mem);
172
173 int* blocks[NUM_BLOCKS] = {0};
174 int next_block = 0;
175
176#define ALLOC(num_blocks) \
177 blocks[next_block] = mem_alloc(&mem, num_blocks); \
178 TEST_TRUE(blocks[next_block] != 0); \
179 next_block++;
180
181#define FREE(block_idx) mem_free(&mem, &blocks[block_idx])
182
183 // 5 total allocations of variable chunk sizes.
184 ALLOC(2); // 2; idx = 0
185 ALLOC(3); // 5; idx = 1
186 ALLOC(1); // 6; idx = 2
187 ALLOC(3); // 9; idx = 3
188 ALLOC(1); // 10; idx = 4
189
190 // Free the 5 allocations in a different order.
191 FREE(1);
192 FREE(3);
193 FREE(4);
194 FREE(2);
195 FREE(0);
196
197 // Should be able to allocate 1 chunk of N blocks.
198 const void* chunk = mem_alloc(&mem, NUM_BLOCKS);
199 TEST_TRUE(chunk != 0);
200}
201
202// Clear and re-use an allocator.
203TEST_CASE(mem_clear_then_reuse) {
204 test_mem mem;
205 mem_make(&mem);
206
207 // Allocate chunks, contents not important.
208 for (int i = 0; i < NUM_BLOCKS; ++i) {
209 int* chunk = mem_alloc(&mem, 1);
210 TEST_TRUE(chunk != 0);
211 }
212
213 mem_clear(&mem);
214
215 // Allocate chunks and assign values 0..N.
216 for (int i = 0; i < NUM_BLOCKS; ++i) {
217 int* chunk = mem_alloc(&mem, 1);
218 TEST_TRUE(chunk != 0);
219 *chunk = i + 1;
220 }
221
222 TEST_EQUAL(sum(&mem), NUM_BLOCKS * (NUM_BLOCKS + 1) / 2);
223}
224
225// Stress test.
226//
227// 1. Allocate the mem, either fully or partially. If fully, attempt to
228// allocate some items past the end.
229//
230// 2. Free all allocated items in some random order.
231
232int main() { return 0; }