tests/util_allocator.c

changeset 770
ed710122af44
parent 653
e081643aae2a
child 775
d3f451440eef
equal deleted inserted replaced
769:b53e0e003d7e 770:ed710122af44
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "util_allocator.h"
30 #include "cx/test.h"
31
32 static void cx_testing_allocator_track(CxTestingAllocator *alloc, void *ptr) {
33 for (size_t i = 0; i < alloc->tracked_count; i++) {
34 if (alloc->tracked[i] == ptr) return; // is already tracked
35 }
36
37 if (alloc->tracked_count == alloc->tracked_capacity) {
38 size_t newcapa = alloc->tracked_capacity + 64;
39 void *newarr = realloc(alloc->tracked, newcapa * sizeof(void *));
40 if (newarr == NULL) abort();
41 alloc->tracked = newarr;
42 alloc->tracked_capacity = newcapa;
43 }
44
45 alloc->tracked[alloc->tracked_count] = ptr;
46 alloc->tracked_count++;
47 }
48
49 static bool cx_testing_allocator_untrack(CxTestingAllocator *alloc, void *ptr) {
50 for (size_t i = 0; i < alloc->tracked_count; i++) {
51 if (alloc->tracked[i] == ptr) {
52 size_t last = alloc->tracked_count - 1;
53 if (i < last) {
54 alloc->tracked[i] = alloc->tracked[last];
55 }
56 alloc->tracked_count--;
57 return true;
58 }
59 }
60 return false;
61 }
62
63 static void *cx_malloc_testing(void *d, size_t n) {
64 CxTestingAllocator *data = d;
65 void *ptr = malloc(n);
66 data->alloc_total++;
67 if (ptr == NULL) {
68 data->alloc_failed++;
69 } else {
70 cx_testing_allocator_track(data, ptr);
71 }
72 return ptr;
73 }
74
75 static void *cx_realloc_testing(void *d, void *mem, size_t n) {
76 CxTestingAllocator *data = d;
77 void *ptr = realloc(mem, n);
78 if (ptr == mem) {
79 return ptr;
80 } else {
81 data->alloc_total++;
82 if (ptr == NULL) {
83 data->alloc_failed++;
84 } else {
85 data->free_total++;
86 if (!cx_testing_allocator_untrack(data, mem)) {
87 data->free_failed++;
88 }
89 cx_testing_allocator_track(data, ptr);
90 }
91 return ptr;
92 }
93 }
94
95 static void *cx_calloc_testing(void *d, size_t nelem, size_t n) {
96 CxTestingAllocator *data = d;
97 void *ptr = calloc(nelem, n);
98 data->alloc_total++;
99 if (ptr == NULL) {
100 data->alloc_failed++;
101 } else {
102 cx_testing_allocator_track(data, ptr);
103 }
104 return ptr;
105 }
106
107 static void cx_free_testing(void *d, void *mem) {
108 CxTestingAllocator *data = d;
109 data->free_total++;
110 if (cx_testing_allocator_untrack(data, mem)) {
111 free(mem);
112 } else {
113 data->free_failed++;
114 // do not even attempt to free mem, because it is likely to segfault
115 }
116 }
117
118 cx_allocator_class cx_testing_allocator_class = {
119 cx_malloc_testing,
120 cx_realloc_testing,
121 cx_calloc_testing,
122 cx_free_testing
123 };
124
125
126 void cx_testing_allocator_init(CxTestingAllocator *alloc) {
127 alloc->base.cl = &cx_testing_allocator_class;
128 alloc->base.data = alloc;
129 alloc->alloc_failed = 0;
130 alloc->alloc_total = 0;
131 alloc->free_failed = 0;
132 alloc->free_total = 0;
133 size_t initial_capa = 16;
134 alloc->tracked_capacity = initial_capa;
135 alloc->tracked_count = 0;
136 alloc->tracked = calloc(sizeof(void *), initial_capa);
137 }
138
139 void cx_testing_allocator_destroy(CxTestingAllocator *alloc) {
140 free(alloc->tracked);
141 }
142
143 bool cx_testing_allocator_used(CxTestingAllocator const *alloc) {
144 return alloc->alloc_total > 0;
145 }
146
147 bool cx_testing_allocator_verify(CxTestingAllocator const *alloc) {
148 return alloc->tracked_count == 0 && alloc->alloc_failed == 0 && alloc->free_failed == 0
149 && alloc->alloc_total == alloc->free_total;
150 }
151
152 // SELF-TEST
153
154 CX_TEST(test_util_allocator_expect_free) {
155 CxTestingAllocator talloc;
156 cx_testing_allocator_init(&talloc);
157 CxAllocator *alloc = &talloc.base;
158 CX_TEST_DO {
159 CX_TEST_ASSERTM(cx_testing_allocator_verify(&talloc),
160 "Fresh testing allocator fails to verify.");
161 CX_TEST_ASSERTM(!cx_testing_allocator_used(&talloc),
162 "Fresh testing allocator already used.");
163 void *ptr = cxMalloc(alloc, 16);
164 CX_TEST_ASSERTM(!cx_testing_allocator_verify(&talloc),
165 "Testing allocator verifies with unfreed memory.");
166 CX_TEST_ASSERT(cx_testing_allocator_used(&talloc));
167 CX_TEST_ASSERT(ptr != NULL);
168
169 cxFree(alloc, ptr);
170 CX_TEST_ASSERTM(cx_testing_allocator_verify(&talloc),
171 "Testing allocator fails to verify after everything freed.");
172 }
173 cx_testing_allocator_destroy(&talloc);
174 }
175
176 CX_TEST(test_util_allocator_detect_double_free) {
177 CxTestingAllocator talloc;
178 cx_testing_allocator_init(&talloc);
179 CxAllocator *alloc = &talloc.base;
180 CX_TEST_DO {
181 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
182 void *ptr = cxMalloc(alloc, 16);
183 CX_TEST_ASSERT(ptr != NULL);
184 cxFree(alloc, ptr);
185 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
186 cxFree(alloc, ptr);
187 CX_TEST_ASSERTM(!cx_testing_allocator_verify(&talloc),
188 "Testing allocator does not detect double-free.");
189 }
190 cx_testing_allocator_destroy(&talloc);
191 }
192
193 CX_TEST(test_util_allocator_free_untracked) {
194 CxTestingAllocator talloc;
195 cx_testing_allocator_init(&talloc);
196 CxAllocator *alloc = &talloc.base;
197 void *ptr = malloc(16);
198 CX_TEST_DO {
199 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
200 cxFree(alloc, ptr);
201 CX_TEST_ASSERTM(!cx_testing_allocator_verify(&talloc),
202 "Testing allocator does not detect free of untracked memory.");
203 }
204 free(ptr);
205 cx_testing_allocator_destroy(&talloc);
206 }
207
208 CX_TEST(test_util_allocator_full_lifecycle_with_realloc) {
209 CxTestingAllocator talloc;
210 cx_testing_allocator_init(&talloc);
211 CxAllocator *alloc = &talloc.base;
212 CX_TEST_DO {
213 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
214 void *ptr = cxMalloc(alloc, 16);
215 CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
216 CX_TEST_ASSERT(ptr != NULL);
217 CX_TEST_ASSERT(talloc.tracked_count == 1);
218 ptr = cxRealloc(alloc, ptr, 256);
219 CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc));
220 CX_TEST_ASSERT(ptr != NULL);
221 CX_TEST_ASSERT(talloc.tracked_count == 1);
222 cxFree(alloc, ptr);
223 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
224 }
225 cx_testing_allocator_destroy(&talloc);
226 }
227
228 CX_TEST(test_util_allocator_calloc_initializes) {
229 CxTestingAllocator talloc;
230 cx_testing_allocator_init(&talloc);
231 CxAllocator *alloc = &talloc.base;
232 CX_TEST_DO {
233 const char zeros[16] = {0};
234 void *ptr = cxCalloc(alloc, 16, 1);
235 CX_TEST_ASSERT(memcmp(ptr, zeros, 16) == 0);
236 cxFree(alloc, ptr);
237 CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
238 }
239 cx_testing_allocator_destroy(&talloc);
240 }
241
242 CxTestSuite *cx_test_suite_testing_allocator(void) {
243 CxTestSuite *suite = cx_test_suite_new("testing allocator self-test");
244
245 cx_test_register(suite, test_util_allocator_expect_free);
246 cx_test_register(suite, test_util_allocator_detect_double_free);
247 cx_test_register(suite, test_util_allocator_free_untracked);
248 cx_test_register(suite, test_util_allocator_full_lifecycle_with_realloc);
249 cx_test_register(suite, test_util_allocator_calloc_initializes);
250
251 return suite;
252 }
253

mercurial