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