1 /* $OpenBSD: lhash_test.c,v 1.2 2024/05/08 15:13:23 jsing Exp $ */
2 /*
3 * Copyright (c) 2024 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <stdint.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21
22 #include <openssl/lhash.h>
23
24 /*
25 * Need to add test coverage for:
26 * - custom hash function
27 * - custom comparison function
28 */
29
30 static void
test_doall_count(void * arg1,void * arg2)31 test_doall_count(void *arg1, void *arg2)
32 {
33 int *count = arg2;
34
35 (*count)++;
36 }
37
38 static int
test_lhash(void)39 test_lhash(void)
40 {
41 const char *a = "a", *b = "b", *c = "c", *d = "d";
42 const char *a2 = "a", *b2 = "b";
43 _LHASH *lh;
44 int count;
45 int failed = 1;
46
47 if ((lh = lh_new(NULL, NULL)) == NULL)
48 goto failure;
49
50 /*
51 * Another amazing API... both a successful insert and a failure will
52 * return NULL. The only way you can tell the difference is to follow
53 * with a call to lh_error().
54 */
55 if (lh_retrieve(lh, "a") != NULL || lh_error(lh) != 0) {
56 fprintf(stderr, "FAIL: retrieved a before insert\n");
57 goto failure;
58 }
59 if (lh_insert(lh, (void *)a) != NULL || lh_error(lh) != 0) {
60 fprintf(stderr, "FAIL: insert a\n");
61 goto failure;
62 }
63 if (lh_retrieve(lh, "a") != a) {
64 fprintf(stderr, "FAIL: failed to retrieve a\n");
65 goto failure;
66 }
67
68 if (lh_retrieve(lh, "b") != NULL || lh_error(lh) != 0) {
69 fprintf(stderr, "FAIL: retrieved b before insert\n");
70 goto failure;
71 }
72 if (lh_insert(lh, (void *)b) != NULL || lh_error(lh) != 0) {
73 fprintf(stderr, "FAIL: insert b\n");
74 goto failure;
75 }
76 if (lh_retrieve(lh, "b") != b) {
77 fprintf(stderr, "FAIL: failed to retrieve b\n");
78 goto failure;
79 }
80
81 if (lh_retrieve(lh, "c") != NULL || lh_error(lh) != 0) {
82 fprintf(stderr, "FAIL: retrieved c before insert\n");
83 goto failure;
84 }
85 if (lh_insert(lh, (void *)c) != NULL || lh_error(lh) != 0) {
86 fprintf(stderr, "FAIL: insert c\n");
87 goto failure;
88 }
89 if (lh_retrieve(lh, "c") != c) {
90 fprintf(stderr, "FAIL: failed to retrieve c\n");
91 goto failure;
92 }
93
94 if (lh_retrieve(lh, "d") != NULL || lh_error(lh) != 0) {
95 fprintf(stderr, "FAIL: retrieved d before insert\n");
96 goto failure;
97 }
98 if (lh_insert(lh, (void *)d) != NULL || lh_error(lh) != 0) {
99 fprintf(stderr, "FAIL: insert d\n");
100 goto failure;
101 }
102 if (lh_retrieve(lh, "d") != d) {
103 fprintf(stderr, "FAIL: failed to retrieve d\n");
104 goto failure;
105 }
106
107 if (lh_num_items(lh) != 4) {
108 fprintf(stderr, "FAIL: lh_num_items() = %ld, want 4\n",
109 lh_num_items(lh));
110 goto failure;
111 }
112
113 /* Insert should replace. */
114 if (lh_insert(lh, (void *)a2) != a || lh_error(lh) != 0) {
115 fprintf(stderr, "FAIL: replace a\n");
116 goto failure;
117 }
118 if (lh_retrieve(lh, "a") != a2) {
119 fprintf(stderr, "FAIL: failed to retrieve a2\n");
120 goto failure;
121 }
122 if (lh_insert(lh, (void *)b2) != b || lh_error(lh) != 0) {
123 fprintf(stderr, "FAIL: replace b\n");
124 goto failure;
125 }
126 if (lh_retrieve(lh, "b") != b2) {
127 fprintf(stderr, "FAIL: failed to retrieve b2\n");
128 goto failure;
129 }
130
131 if (lh_num_items(lh) != 4) {
132 fprintf(stderr, "FAIL: lh_num_items() = %ld, want 4\n",
133 lh_num_items(lh));
134 goto failure;
135 }
136
137 /* Do all. */
138 count = 0;
139 lh_doall_arg(lh, test_doall_count, &count);
140 if (count != 4) {
141 fprintf(stderr, "FAIL: lh_doall_arg failed (count = %d)\n",
142 count);
143 goto failure;
144 }
145
146 /* Delete. */
147 if (lh_delete(lh, "z") != NULL || lh_error(lh) != 0) {
148 fprintf(stderr, "FAIL: delete succeeded for z\n");
149 goto failure;
150 }
151 if (lh_delete(lh, "a") != a2 || lh_error(lh) != 0) {
152 fprintf(stderr, "FAIL: delete failed for a\n");
153 goto failure;
154 }
155 if (lh_retrieve(lh, "a") != NULL || lh_error(lh) != 0) {
156 fprintf(stderr, "FAIL: retrieved a after deletion\n");
157 goto failure;
158 }
159 if (lh_delete(lh, "b") != b2 || lh_error(lh) != 0) {
160 fprintf(stderr, "FAIL: delete failed for b\n");
161 goto failure;
162 }
163 if (lh_retrieve(lh, "b") != NULL || lh_error(lh) != 0) {
164 fprintf(stderr, "FAIL: retrieved b after deletion\n");
165 goto failure;
166 }
167 if (lh_delete(lh, "c") != c || lh_error(lh) != 0) {
168 fprintf(stderr, "FAIL: delete failed for c\n");
169 goto failure;
170 }
171 if (lh_retrieve(lh, "c") != NULL || lh_error(lh) != 0) {
172 fprintf(stderr, "FAIL: retrieved c after deletion\n");
173 goto failure;
174 }
175 if (lh_delete(lh, "d") != d || lh_error(lh) != 0) {
176 fprintf(stderr, "FAIL: delete failed for d\n");
177 goto failure;
178 }
179 if (lh_retrieve(lh, "d") != NULL || lh_error(lh) != 0) {
180 fprintf(stderr, "FAIL: retrieved d after deletion\n");
181 goto failure;
182 }
183
184 if (lh_num_items(lh) != 0) {
185 fprintf(stderr, "FAIL: lh_num_items() = %ld, want 0\n",
186 lh_num_items(lh));
187 goto failure;
188 }
189
190 failed = 0;
191
192 failure:
193 lh_free(lh);
194
195 return failed;
196 }
197
198 static void
test_doall_fn(void * arg1)199 test_doall_fn(void *arg1)
200 {
201 }
202
203 static int
test_lhash_doall(void)204 test_lhash_doall(void)
205 {
206 _LHASH *lh;
207 int i;
208 int failed = 1;
209
210 if ((lh = lh_new(NULL, NULL)) == NULL)
211 goto failure;
212
213 /* Call doall multiple times while linked hash is empty. */
214 for (i = 0; i < 100; i++)
215 lh_doall(lh, test_doall_fn);
216
217 failed = 0;
218
219 failure:
220 lh_free(lh);
221
222 return failed;
223 }
224
225 static void
test_doall_delete_some(void * arg1,void * arg2)226 test_doall_delete_some(void *arg1, void *arg2)
227 {
228 void *data;
229
230 if (arc4random_uniform(32) != 0)
231 return;
232
233 data = lh_delete(arg2, arg1);
234 free(data);
235 }
236
237 static void
test_doall_delete_all(void * arg1,void * arg2)238 test_doall_delete_all(void *arg1, void *arg2)
239 {
240 void *data;
241
242 data = lh_delete(arg2, arg1);
243 free(data);
244 }
245
246 static int
test_lhash_load(void)247 test_lhash_load(void)
248 {
249 uint8_t c3 = 1, c2 = 1, c1 = 1, c0 = 1;
250 _LHASH *lh;
251 char *data = NULL;
252 int i, j;
253 int failed = 1;
254
255 if ((lh = lh_new(NULL, NULL)) == NULL)
256 goto failure;
257
258 for (i = 0; i < 1024; i++) {
259 for (j = 0; j < 1024; j++) {
260 if ((data = calloc(1, 128)) == NULL)
261 goto failure;
262
263 data[0] = c0;
264 data[1] = c1;
265 data[2] = c2;
266 data[3] = c3;
267
268 if (++c0 == 0) {
269 c0++;
270 c1++;
271 }
272 if (c1 == 0) {
273 c1++;
274 c2++;
275 }
276 if (c2 == 0) {
277 c2++;
278 c3++;
279 }
280
281 if (lh_insert(lh, data) != NULL || lh_error(lh) != 0) {
282 fprintf(stderr, "FAIL: lh_insert() failed\n");
283 goto failure;
284 }
285 data = NULL;
286 }
287 lh_doall_arg(lh, test_doall_delete_some, lh);
288 }
289
290 /* We should have ~31,713 entries. */
291 if (lh_num_items(lh) < 31000 || lh_num_items(lh) > 33000) {
292 fprintf(stderr, "FAIL: unexpected number of entries (%ld)\n",
293 lh_num_items(lh));
294 goto failure;
295 }
296
297 failed = 0;
298
299 failure:
300 if (lh != NULL)
301 lh_doall_arg(lh, test_doall_delete_all, lh);
302
303 lh_free(lh);
304 free(data);
305
306 return failed;
307 }
308
309 int
main(int argc,char ** argv)310 main(int argc, char **argv)
311 {
312 int failed = 0;
313
314 failed |= test_lhash();
315 failed |= test_lhash_doall();
316 failed |= test_lhash_load();
317
318 return failed;
319 }
320