]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/test/test-hashmap-plain.c
headers: remove unneeded includes from util.h
[thirdparty/systemd.git] / src / test / test-hashmap-plain.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
32a4456c 2
b5efdb8a 3#include "alloc-util.h"
07630cea 4#include "hashmap.h"
3d14a300 5#include "log.h"
d8b4d14d 6#include "nulstr-util.h"
8872c3a3 7#include "stdio-util.h"
07630cea 8#include "string-util.h"
32a4456c 9#include "strv.h"
ca78ad1d 10#include "time-util.h"
0cf29baa 11#include "tests.h"
32a4456c
MS
12
13void test_hashmap_funcs(void);
14
15static void test_hashmap_replace(void) {
16 Hashmap *m;
17 char *val1, *val2, *val3, *val4, *val5, *r;
18
32ca2911 19 log_info("/* %s */", __func__);
3d14a300 20
32a4456c
MS
21 m = hashmap_new(&string_hash_ops);
22
23 val1 = strdup("val1");
24 assert_se(val1);
25 val2 = strdup("val2");
26 assert_se(val2);
27 val3 = strdup("val3");
28 assert_se(val3);
29 val4 = strdup("val4");
30 assert_se(val4);
31 val5 = strdup("val5");
32 assert_se(val5);
33
34 hashmap_put(m, "key 1", val1);
35 hashmap_put(m, "key 2", val2);
36 hashmap_put(m, "key 3", val3);
37 hashmap_put(m, "key 4", val4);
38
39 hashmap_replace(m, "key 3", val1);
40 r = hashmap_get(m, "key 3");
41 assert_se(streq(r, "val1"));
42
43 hashmap_replace(m, "key 5", val5);
44 r = hashmap_get(m, "key 5");
45 assert_se(streq(r, "val5"));
46
47 free(val1);
48 free(val2);
49 free(val3);
50 free(val4);
51 free(val5);
52 hashmap_free(m);
53}
54
55static void test_hashmap_copy(void) {
56 Hashmap *m, *copy;
57 char *val1, *val2, *val3, *val4, *r;
58
32ca2911 59 log_info("/* %s */", __func__);
3d14a300 60
32a4456c
MS
61 val1 = strdup("val1");
62 assert_se(val1);
63 val2 = strdup("val2");
64 assert_se(val2);
65 val3 = strdup("val3");
66 assert_se(val3);
67 val4 = strdup("val4");
68 assert_se(val4);
69
70 m = hashmap_new(&string_hash_ops);
71
72 hashmap_put(m, "key 1", val1);
73 hashmap_put(m, "key 2", val2);
74 hashmap_put(m, "key 3", val3);
75 hashmap_put(m, "key 4", val4);
76
77 copy = hashmap_copy(m);
78
79 r = hashmap_get(copy, "key 1");
80 assert_se(streq(r, "val1"));
81 r = hashmap_get(copy, "key 2");
82 assert_se(streq(r, "val2"));
83 r = hashmap_get(copy, "key 3");
84 assert_se(streq(r, "val3"));
85 r = hashmap_get(copy, "key 4");
86 assert_se(streq(r, "val4"));
87
88 hashmap_free_free(copy);
89 hashmap_free(m);
90}
91
92static void test_hashmap_get_strv(void) {
93 Hashmap *m;
94 char **strv;
95 char *val1, *val2, *val3, *val4;
96
32ca2911 97 log_info("/* %s */", __func__);
3d14a300 98
32a4456c
MS
99 val1 = strdup("val1");
100 assert_se(val1);
101 val2 = strdup("val2");
102 assert_se(val2);
103 val3 = strdup("val3");
104 assert_se(val3);
105 val4 = strdup("val4");
106 assert_se(val4);
107
108 m = hashmap_new(&string_hash_ops);
109
110 hashmap_put(m, "key 1", val1);
111 hashmap_put(m, "key 2", val2);
112 hashmap_put(m, "key 3", val3);
113 hashmap_put(m, "key 4", val4);
114
115 strv = hashmap_get_strv(m);
116
117#ifndef ORDERED
118 strv = strv_sort(strv);
119#endif
120
121 assert_se(streq(strv[0], "val1"));
122 assert_se(streq(strv[1], "val2"));
123 assert_se(streq(strv[2], "val3"));
124 assert_se(streq(strv[3], "val4"));
125
126 strv_free(strv);
127
128 hashmap_free(m);
129}
130
131static void test_hashmap_move_one(void) {
132 Hashmap *m, *n;
133 char *val1, *val2, *val3, *val4, *r;
134
32ca2911 135 log_info("/* %s */", __func__);
3d14a300 136
32a4456c
MS
137 val1 = strdup("val1");
138 assert_se(val1);
139 val2 = strdup("val2");
140 assert_se(val2);
141 val3 = strdup("val3");
142 assert_se(val3);
143 val4 = strdup("val4");
144 assert_se(val4);
145
146 m = hashmap_new(&string_hash_ops);
147 n = hashmap_new(&string_hash_ops);
148
149 hashmap_put(m, "key 1", val1);
150 hashmap_put(m, "key 2", val2);
151 hashmap_put(m, "key 3", val3);
152 hashmap_put(m, "key 4", val4);
153
9ba81d5a
MS
154 assert_se(hashmap_move_one(n, NULL, "key 3") == -ENOENT);
155 assert_se(hashmap_move_one(n, m, "key 5") == -ENOENT);
156 assert_se(hashmap_move_one(n, m, "key 3") == 0);
157 assert_se(hashmap_move_one(n, m, "key 4") == 0);
32a4456c
MS
158
159 r = hashmap_get(n, "key 3");
160 assert_se(r && streq(r, "val3"));
161 r = hashmap_get(n, "key 4");
162 assert_se(r && streq(r, "val4"));
163 r = hashmap_get(m, "key 3");
164 assert_se(!r);
165
9ba81d5a
MS
166 assert_se(hashmap_move_one(n, m, "key 3") == -EEXIST);
167
168 hashmap_free_free(m);
169 hashmap_free_free(n);
170}
171
172static void test_hashmap_move(void) {
173 Hashmap *m, *n;
174 char *val1, *val2, *val3, *val4, *r;
175
32ca2911 176 log_info("/* %s */", __func__);
3d14a300 177
9ba81d5a
MS
178 val1 = strdup("val1");
179 assert_se(val1);
180 val2 = strdup("val2");
181 assert_se(val2);
182 val3 = strdup("val3");
183 assert_se(val3);
184 val4 = strdup("val4");
185 assert_se(val4);
186
187 m = hashmap_new(&string_hash_ops);
188 n = hashmap_new(&string_hash_ops);
189
190 hashmap_put(n, "key 1", strdup(val1));
191 hashmap_put(m, "key 1", val1);
192 hashmap_put(m, "key 2", val2);
193 hashmap_put(m, "key 3", val3);
194 hashmap_put(m, "key 4", val4);
195
e80afdb3
MS
196 assert_se(hashmap_move(n, NULL) == 0);
197 assert_se(hashmap_move(n, m) == 0);
9ba81d5a
MS
198
199 assert_se(hashmap_size(m) == 1);
200 r = hashmap_get(m, "key 1");
201 assert_se(r && streq(r, "val1"));
202
203 r = hashmap_get(n, "key 1");
204 assert_se(r && streq(r, "val1"));
205 r = hashmap_get(n, "key 2");
206 assert_se(r && streq(r, "val2"));
207 r = hashmap_get(n, "key 3");
208 assert_se(r && streq(r, "val3"));
209 r = hashmap_get(n, "key 4");
210 assert_se(r && streq(r, "val4"));
32a4456c
MS
211
212 hashmap_free_free(m);
213 hashmap_free_free(n);
214}
215
216static void test_hashmap_update(void) {
217 Hashmap *m;
218 char *val1, *val2, *r;
219
32ca2911 220 log_info("/* %s */", __func__);
3d14a300 221
32a4456c
MS
222 m = hashmap_new(&string_hash_ops);
223 val1 = strdup("old_value");
224 assert_se(val1);
225 val2 = strdup("new_value");
226 assert_se(val2);
227
228 hashmap_put(m, "key 1", val1);
229 r = hashmap_get(m, "key 1");
230 assert_se(streq(r, "old_value"));
231
9ba81d5a
MS
232 assert_se(hashmap_update(m, "key 2", val2) == -ENOENT);
233 r = hashmap_get(m, "key 1");
234 assert_se(streq(r, "old_value"));
235
236 assert_se(hashmap_update(m, "key 1", val2) == 0);
32a4456c
MS
237 r = hashmap_get(m, "key 1");
238 assert_se(streq(r, "new_value"));
239
240 free(val1);
241 free(val2);
242 hashmap_free(m);
243}
244
245static void test_hashmap_put(void) {
9ba81d5a 246 Hashmap *m = NULL;
32a4456c 247 int valid_hashmap_put;
9ba81d5a 248 void *val1 = (void*) "val 1";
435fc317
MP
249 void *val2 = (void*) "val 2";
250 _cleanup_free_ char* key1 = NULL;
32a4456c 251
32ca2911 252 log_info("/* %s */", __func__);
3d14a300 253
61c81750 254 assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) >= 0);
9ba81d5a 255 assert_se(m);
32a4456c 256
9ba81d5a 257 valid_hashmap_put = hashmap_put(m, "key 1", val1);
32a4456c 258 assert_se(valid_hashmap_put == 1);
9ba81d5a 259 assert_se(hashmap_put(m, "key 1", val1) == 0);
435fc317
MP
260 assert_se(hashmap_put(m, "key 1", val2) == -EEXIST);
261 key1 = strdup("key 1");
262 assert_se(hashmap_put(m, key1, val1) == 0);
263 assert_se(hashmap_put(m, key1, val2) == -EEXIST);
32a4456c 264
32a4456c
MS
265 hashmap_free(m);
266}
267
9ba81d5a
MS
268static void test_hashmap_remove(void) {
269 _cleanup_hashmap_free_ Hashmap *m = NULL;
270 char *r;
271
32ca2911 272 log_info("/* %s */", __func__);
3d14a300 273
9ba81d5a
MS
274 r = hashmap_remove(NULL, "key 1");
275 assert_se(r == NULL);
276
277 m = hashmap_new(&string_hash_ops);
278 assert_se(m);
279
280 r = hashmap_remove(m, "no such key");
281 assert_se(r == NULL);
282
283 hashmap_put(m, "key 1", (void*) "val 1");
284 hashmap_put(m, "key 2", (void*) "val 2");
285
286 r = hashmap_remove(m, "key 1");
287 assert_se(streq(r, "val 1"));
288
289 r = hashmap_get(m, "key 2");
290 assert_se(streq(r, "val 2"));
291 assert_se(!hashmap_get(m, "key 1"));
292}
293
294static void test_hashmap_remove2(void) {
295 _cleanup_hashmap_free_free_free_ Hashmap *m = NULL;
296 char key1[] = "key 1";
297 char key2[] = "key 2";
298 char val1[] = "val 1";
299 char val2[] = "val 2";
300 void *r, *r2;
301
32ca2911 302 log_info("/* %s */", __func__);
3d14a300 303
9ba81d5a
MS
304 r = hashmap_remove2(NULL, "key 1", &r2);
305 assert_se(r == NULL);
306
307 m = hashmap_new(&string_hash_ops);
308 assert_se(m);
309
310 r = hashmap_remove2(m, "no such key", &r2);
311 assert_se(r == NULL);
312
313 hashmap_put(m, strdup(key1), strdup(val1));
314 hashmap_put(m, strdup(key2), strdup(val2));
315
316 r = hashmap_remove2(m, key1, &r2);
317 assert_se(streq(r, val1));
318 assert_se(streq(r2, key1));
319 free(r);
320 free(r2);
321
322 r = hashmap_get(m, key2);
323 assert_se(streq(r, val2));
324 assert_se(!hashmap_get(m, key1));
325}
326
327static void test_hashmap_remove_value(void) {
328 _cleanup_hashmap_free_ Hashmap *m = NULL;
329 char *r;
330
d09139e1
ZJS
331 char val1[] = "val 1";
332 char val2[] = "val 2";
333
32ca2911 334 log_info("/* %s */", __func__);
3d14a300 335
d09139e1 336 r = hashmap_remove_value(NULL, "key 1", val1);
9ba81d5a
MS
337 assert_se(r == NULL);
338
339 m = hashmap_new(&string_hash_ops);
340 assert_se(m);
341
d09139e1 342 r = hashmap_remove_value(m, "key 1", val1);
9ba81d5a
MS
343 assert_se(r == NULL);
344
d09139e1
ZJS
345 hashmap_put(m, "key 1", val1);
346 hashmap_put(m, "key 2", val2);
9ba81d5a 347
d09139e1 348 r = hashmap_remove_value(m, "key 1", val1);
9ba81d5a
MS
349 assert_se(streq(r, "val 1"));
350
351 r = hashmap_get(m, "key 2");
352 assert_se(streq(r, "val 2"));
353 assert_se(!hashmap_get(m, "key 1"));
354
d09139e1 355 r = hashmap_remove_value(m, "key 2", val1);
9ba81d5a
MS
356 assert_se(r == NULL);
357
358 r = hashmap_get(m, "key 2");
359 assert_se(streq(r, "val 2"));
360 assert_se(!hashmap_get(m, "key 1"));
361}
362
32a4456c
MS
363static void test_hashmap_remove_and_put(void) {
364 _cleanup_hashmap_free_ Hashmap *m = NULL;
365 int valid;
366 char *r;
367
32ca2911 368 log_info("/* %s */", __func__);
3d14a300 369
32a4456c
MS
370 m = hashmap_new(&string_hash_ops);
371 assert_se(m);
372
9ba81d5a
MS
373 valid = hashmap_remove_and_put(m, "invalid key", "new key", NULL);
374 assert_se(valid == -ENOENT);
32a4456c
MS
375
376 valid = hashmap_put(m, "key 1", (void*) (const char *) "val 1");
377 assert_se(valid == 1);
9ba81d5a
MS
378
379 valid = hashmap_remove_and_put(NULL, "key 1", "key 2", (void*) (const char *) "val 2");
380 assert_se(valid == -ENOENT);
381
32a4456c
MS
382 valid = hashmap_remove_and_put(m, "key 1", "key 2", (void*) (const char *) "val 2");
383 assert_se(valid == 0);
384
385 r = hashmap_get(m, "key 2");
386 assert_se(streq(r, "val 2"));
387 assert_se(!hashmap_get(m, "key 1"));
388
389 valid = hashmap_put(m, "key 3", (void*) (const char *) "val 3");
390 assert_se(valid == 1);
391 valid = hashmap_remove_and_put(m, "key 3", "key 2", (void*) (const char *) "val 2");
9ba81d5a
MS
392 assert_se(valid == -EEXIST);
393}
394
395static void test_hashmap_remove_and_replace(void) {
396 _cleanup_hashmap_free_ Hashmap *m = NULL;
397 int valid;
398 void *key1 = UINT_TO_PTR(1);
399 void *key2 = UINT_TO_PTR(2);
400 void *key3 = UINT_TO_PTR(3);
401 void *r;
e1323fbf 402 int i, j;
9ba81d5a 403
32ca2911 404 log_info("/* %s */", __func__);
3d14a300 405
9ba81d5a
MS
406 m = hashmap_new(&trivial_hash_ops);
407 assert_se(m);
408
409 valid = hashmap_remove_and_replace(m, key1, key2, NULL);
410 assert_se(valid == -ENOENT);
411
412 valid = hashmap_put(m, key1, key1);
413 assert_se(valid == 1);
414
415 valid = hashmap_remove_and_replace(NULL, key1, key2, key2);
416 assert_se(valid == -ENOENT);
417
418 valid = hashmap_remove_and_replace(m, key1, key2, key2);
419 assert_se(valid == 0);
420
421 r = hashmap_get(m, key2);
422 assert_se(r == key2);
423 assert_se(!hashmap_get(m, key1));
424
425 valid = hashmap_put(m, key3, key3);
426 assert_se(valid == 1);
427 valid = hashmap_remove_and_replace(m, key3, key2, key2);
428 assert_se(valid == 0);
429 r = hashmap_get(m, key2);
430 assert_se(r == key2);
431 assert_se(!hashmap_get(m, key3));
e1323fbf
MS
432
433 /* Repeat this test several times to increase the chance of hitting
434 * the less likely case in hashmap_remove_and_replace where it
435 * compensates for the backward shift. */
436 for (i = 0; i < 20; i++) {
437 hashmap_clear(m);
438
439 for (j = 1; j < 7; j++)
440 hashmap_put(m, UINT_TO_PTR(10*i + j), UINT_TO_PTR(10*i + j));
441 valid = hashmap_remove_and_replace(m, UINT_TO_PTR(10*i + 1),
442 UINT_TO_PTR(10*i + 2),
443 UINT_TO_PTR(10*i + 2));
444 assert_se(valid == 0);
445 assert_se(!hashmap_get(m, UINT_TO_PTR(10*i + 1)));
446 for (j = 2; j < 7; j++) {
447 r = hashmap_get(m, UINT_TO_PTR(10*i + j));
448 assert_se(r == UINT_TO_PTR(10*i + j));
449 }
450 }
32a4456c
MS
451}
452
453static void test_hashmap_ensure_allocated(void) {
454 Hashmap *m;
455 int valid_hashmap;
456
32ca2911 457 log_info("/* %s */", __func__);
3d14a300 458
32a4456c
MS
459 m = hashmap_new(&string_hash_ops);
460
461 valid_hashmap = hashmap_ensure_allocated(&m, &string_hash_ops);
462 assert_se(valid_hashmap == 0);
463
464 assert_se(m);
465 hashmap_free(m);
466}
467
468static void test_hashmap_foreach_key(void) {
469 Hashmap *m;
470 Iterator i;
471 bool key_found[] = { false, false, false, false };
472 const char *s;
473 const char *key;
474 static const char key_table[] =
475 "key 1\0"
476 "key 2\0"
477 "key 3\0"
478 "key 4\0";
479
32ca2911 480 log_info("/* %s */", __func__);
3d14a300 481
32a4456c
MS
482 m = hashmap_new(&string_hash_ops);
483
484 NULSTR_FOREACH(key, key_table)
485 hashmap_put(m, key, (void*) (const char*) "my dummy val");
486
487 HASHMAP_FOREACH_KEY(s, key, m, i) {
8927b1da 488 assert(s);
32a4456c
MS
489 if (!key_found[0] && streq(key, "key 1"))
490 key_found[0] = true;
491 else if (!key_found[1] && streq(key, "key 2"))
492 key_found[1] = true;
493 else if (!key_found[2] && streq(key, "key 3"))
494 key_found[2] = true;
495 else if (!key_found[3] && streq(key, "fail"))
496 key_found[3] = true;
497 }
498
499 assert_se(m);
500 assert_se(key_found[0] && key_found[1] && key_found[2] && !key_found[3]);
501
502 hashmap_free(m);
503}
504
505static void test_hashmap_foreach(void) {
506 Hashmap *m;
507 Iterator i;
508 bool value_found[] = { false, false, false, false };
509 char *val1, *val2, *val3, *val4, *s;
9ba81d5a 510 unsigned count;
32a4456c 511
32ca2911 512 log_info("/* %s */", __func__);
3d14a300 513
32a4456c
MS
514 val1 = strdup("my val1");
515 assert_se(val1);
516 val2 = strdup("my val2");
517 assert_se(val2);
518 val3 = strdup("my val3");
519 assert_se(val3);
520 val4 = strdup("my val4");
521 assert_se(val4);
522
9ba81d5a
MS
523 m = NULL;
524
525 count = 0;
526 HASHMAP_FOREACH(s, m, i)
527 count++;
528 assert_se(count == 0);
529
32a4456c
MS
530 m = hashmap_new(&string_hash_ops);
531
9ba81d5a
MS
532 count = 0;
533 HASHMAP_FOREACH(s, m, i)
534 count++;
535 assert_se(count == 0);
536
32a4456c
MS
537 hashmap_put(m, "Key 1", val1);
538 hashmap_put(m, "Key 2", val2);
539 hashmap_put(m, "Key 3", val3);
540 hashmap_put(m, "Key 4", val4);
541
542 HASHMAP_FOREACH(s, m, i) {
543 if (!value_found[0] && streq(s, val1))
544 value_found[0] = true;
545 else if (!value_found[1] && streq(s, val2))
546 value_found[1] = true;
547 else if (!value_found[2] && streq(s, val3))
548 value_found[2] = true;
549 else if (!value_found[3] && streq(s, val4))
550 value_found[3] = true;
551 }
552
553 assert_se(m);
554 assert_se(value_found[0] && value_found[1] && value_found[2] && value_found[3]);
555
556 hashmap_free_free(m);
557}
558
559static void test_hashmap_merge(void) {
560 Hashmap *m;
561 Hashmap *n;
562 char *val1, *val2, *val3, *val4, *r;
563
32ca2911 564 log_info("/* %s */", __func__);
3d14a300 565
32a4456c
MS
566 val1 = strdup("my val1");
567 assert_se(val1);
568 val2 = strdup("my val2");
569 assert_se(val2);
570 val3 = strdup("my val3");
571 assert_se(val3);
572 val4 = strdup("my val4");
573 assert_se(val4);
574
575 n = hashmap_new(&string_hash_ops);
576 m = hashmap_new(&string_hash_ops);
577
578 hashmap_put(m, "Key 1", val1);
579 hashmap_put(m, "Key 2", val2);
580 hashmap_put(n, "Key 3", val3);
581 hashmap_put(n, "Key 4", val4);
582
583 assert_se(hashmap_merge(m, n) == 0);
584 r = hashmap_get(m, "Key 3");
585 assert_se(r && streq(r, "my val3"));
586 r = hashmap_get(m, "Key 4");
587 assert_se(r && streq(r, "my val4"));
588
589 assert_se(n);
590 assert_se(m);
591 hashmap_free(n);
592 hashmap_free_free(m);
593}
594
595static void test_hashmap_contains(void) {
596 Hashmap *m;
597 char *val1;
598
32ca2911 599 log_info("/* %s */", __func__);
3d14a300 600
32a4456c
MS
601 val1 = strdup("my val");
602 assert_se(val1);
603
604 m = hashmap_new(&string_hash_ops);
605
606 assert_se(!hashmap_contains(m, "Key 1"));
607 hashmap_put(m, "Key 1", val1);
608 assert_se(hashmap_contains(m, "Key 1"));
9ba81d5a
MS
609 assert_se(!hashmap_contains(m, "Key 2"));
610
611 assert_se(!hashmap_contains(NULL, "Key 1"));
32a4456c
MS
612
613 assert_se(m);
614 hashmap_free_free(m);
615}
616
617static void test_hashmap_isempty(void) {
618 Hashmap *m;
619 char *val1;
620
32ca2911 621 log_info("/* %s */", __func__);
3d14a300 622
32a4456c
MS
623 val1 = strdup("my val");
624 assert_se(val1);
625
626 m = hashmap_new(&string_hash_ops);
627
628 assert_se(hashmap_isempty(m));
629 hashmap_put(m, "Key 1", val1);
630 assert_se(!hashmap_isempty(m));
631
632 assert_se(m);
633 hashmap_free_free(m);
634}
635
636static void test_hashmap_size(void) {
637 Hashmap *m;
638 char *val1, *val2, *val3, *val4;
639
32ca2911 640 log_info("/* %s */", __func__);
3d14a300 641
32a4456c
MS
642 val1 = strdup("my val");
643 assert_se(val1);
644 val2 = strdup("my val");
645 assert_se(val2);
646 val3 = strdup("my val");
647 assert_se(val3);
648 val4 = strdup("my val");
649 assert_se(val4);
650
9ba81d5a
MS
651 assert_se(hashmap_size(NULL) == 0);
652 assert_se(hashmap_buckets(NULL) == 0);
653
32a4456c
MS
654 m = hashmap_new(&string_hash_ops);
655
656 hashmap_put(m, "Key 1", val1);
657 hashmap_put(m, "Key 2", val2);
658 hashmap_put(m, "Key 3", val3);
659 hashmap_put(m, "Key 4", val4);
660
661 assert_se(m);
662 assert_se(hashmap_size(m) == 4);
9ba81d5a 663 assert_se(hashmap_buckets(m) >= 4);
32a4456c
MS
664 hashmap_free_free(m);
665}
666
667static void test_hashmap_get(void) {
668 Hashmap *m;
669 char *r;
670 char *val;
671
32ca2911 672 log_info("/* %s */", __func__);
3d14a300 673
32a4456c
MS
674 val = strdup("my val");
675 assert_se(val);
676
9ba81d5a
MS
677 r = hashmap_get(NULL, "Key 1");
678 assert_se(r == NULL);
679
32a4456c
MS
680 m = hashmap_new(&string_hash_ops);
681
682 hashmap_put(m, "Key 1", val);
683
684 r = hashmap_get(m, "Key 1");
685 assert_se(streq(r, val));
686
9ba81d5a
MS
687 r = hashmap_get(m, "no such key");
688 assert_se(r == NULL);
689
32a4456c
MS
690 assert_se(m);
691 hashmap_free_free(m);
692}
693
9ba81d5a
MS
694static void test_hashmap_get2(void) {
695 Hashmap *m;
696 char *r;
697 char *val;
698 char key_orig[] = "Key 1";
699 void *key_copy;
700
32ca2911 701 log_info("/* %s */", __func__);
3d14a300 702
9ba81d5a
MS
703 val = strdup("my val");
704 assert_se(val);
705
706 key_copy = strdup(key_orig);
707 assert_se(key_copy);
708
709 r = hashmap_get2(NULL, key_orig, &key_copy);
710 assert_se(r == NULL);
711
712 m = hashmap_new(&string_hash_ops);
713
714 hashmap_put(m, key_copy, val);
715 key_copy = NULL;
716
717 r = hashmap_get2(m, key_orig, &key_copy);
718 assert_se(streq(r, val));
719 assert_se(key_orig != key_copy);
b669934f 720 assert_se(streq(key_orig, key_copy));
9ba81d5a
MS
721
722 r = hashmap_get2(m, "no such key", NULL);
723 assert_se(r == NULL);
724
725 assert_se(m);
726 hashmap_free_free_free(m);
727}
728
b826ab58
TG
729static void crippled_hashmap_func(const void *p, struct siphash *state) {
730 return trivial_hash_func(INT_TO_PTR(PTR_TO_INT(p) & 0xff), state);
9ba81d5a
MS
731}
732
733static const struct hash_ops crippled_hashmap_ops = {
734 .hash = crippled_hashmap_func,
735 .compare = trivial_compare_func,
736};
737
32a4456c
MS
738static void test_hashmap_many(void) {
739 Hashmap *h;
9ba81d5a
MS
740 unsigned i, j;
741 void *v, *k;
0cf29baa 742 bool slow = slow_tests_enabled();
3d14a300 743 const struct {
32ca2911 744 const char *title;
9ba81d5a
MS
745 const struct hash_ops *ops;
746 unsigned n_entries;
747 } tests[] = {
32ca2911
ZJS
748 { "trivial_hashmap_ops", NULL, slow ? 1 << 20 : 240 },
749 { "crippled_hashmap_ops", &crippled_hashmap_ops, slow ? 1 << 14 : 140 },
9ba81d5a 750 };
32a4456c 751
32ca2911 752 log_info("/* %s (%s) */", __func__, slow ? "slow" : "fast");
32a4456c 753
9ba81d5a 754 for (j = 0; j < ELEMENTSOF(tests); j++) {
32ca2911
ZJS
755 usec_t ts = now(CLOCK_MONOTONIC), n;
756 char b[FORMAT_TIMESPAN_MAX];
757
9ba81d5a 758 assert_se(h = hashmap_new(tests[j].ops));
32a4456c 759
9ba81d5a
MS
760 for (i = 1; i < tests[j].n_entries*3; i+=3) {
761 assert_se(hashmap_put(h, UINT_TO_PTR(i), UINT_TO_PTR(i)) >= 0);
762 assert_se(PTR_TO_UINT(hashmap_get(h, UINT_TO_PTR(i))) == i);
763 }
764
765 for (i = 1; i < tests[j].n_entries*3; i++)
766 assert_se(hashmap_contains(h, UINT_TO_PTR(i)) == (i % 3 == 1));
32a4456c 767
32ca2911
ZJS
768 log_info("%s %u <= %u * 0.8 = %g",
769 tests[j].title, hashmap_size(h), hashmap_buckets(h), hashmap_buckets(h) * 0.8);
32a4456c 770
ce79279b 771 assert_se(hashmap_size(h) <= hashmap_buckets(h) * 0.8);
9ba81d5a 772 assert_se(hashmap_size(h) == tests[j].n_entries);
32a4456c 773
9ba81d5a
MS
774 while (!hashmap_isempty(h)) {
775 k = hashmap_first_key(h);
776 v = hashmap_remove(h, k);
777 assert_se(v == k);
778 }
32a4456c 779
9ba81d5a 780 hashmap_free(h);
32ca2911
ZJS
781
782 n = now(CLOCK_MONOTONIC);
783 log_info("test took %s", format_timespan(b, sizeof b, n - ts, 0));
9ba81d5a
MS
784 }
785}
786
8872c3a3
ZJS
787extern unsigned custom_counter;
788extern const struct hash_ops boring_hash_ops, custom_hash_ops;
789
790static void test_hashmap_free(void) {
791 Hashmap *h;
792 bool slow = slow_tests_enabled();
793 usec_t ts, n;
794 char b[FORMAT_TIMESPAN_MAX];
795 unsigned n_entries = slow ? 1 << 20 : 240;
796
797 const struct {
798 const char *title;
799 const struct hash_ops *ops;
800 unsigned expect_counter;
801 } tests[] = {
802 { "string_hash_ops", &boring_hash_ops, 2 * n_entries},
803 { "custom_free_hash_ops", &custom_hash_ops, 0 },
804 };
805
806 log_info("/* %s (%s, %u entries) */", __func__, slow ? "slow" : "fast", n_entries);
807
808 for (unsigned j = 0; j < ELEMENTSOF(tests); j++) {
809 ts = now(CLOCK_MONOTONIC);
810 assert_se(h = hashmap_new(tests[j].ops));
811
812 custom_counter = 0;
813 for (unsigned i = 0; i < n_entries; i++) {
814 char s[DECIMAL_STR_MAX(unsigned)];
815 char *k, *v;
816
817 xsprintf(s, "%u", i);
818 assert_se(k = strdup(s));
819 assert_se(v = strdup(s));
820 custom_counter += 2;
821
822 assert_se(hashmap_put(h, k, v) >= 0);
823 }
824
825 hashmap_free(h);
826
827 n = now(CLOCK_MONOTONIC);
828 log_info("%s test took %s", tests[j].title, format_timespan(b, sizeof b, n - ts, 0));
829
830 assert_se(custom_counter == tests[j].expect_counter);
831 }
832}
833
9ba81d5a
MS
834static void test_hashmap_first(void) {
835 _cleanup_hashmap_free_ Hashmap *m = NULL;
836
32ca2911 837 log_info("/* %s */", __func__);
3d14a300 838
9ba81d5a
MS
839 m = hashmap_new(&string_hash_ops);
840 assert_se(m);
841
842 assert_se(!hashmap_first(m));
843 assert_se(hashmap_put(m, "key 1", (void*) "val 1") == 1);
844 assert_se(streq(hashmap_first(m), "val 1"));
845 assert_se(hashmap_put(m, "key 2", (void*) "val 2") == 1);
846#ifdef ORDERED
847 assert_se(streq(hashmap_first(m), "val 1"));
848 assert_se(hashmap_remove(m, "key 1"));
849 assert_se(streq(hashmap_first(m), "val 2"));
850#endif
32a4456c
MS
851}
852
853static void test_hashmap_first_key(void) {
854 _cleanup_hashmap_free_ Hashmap *m = NULL;
855
32ca2911 856 log_info("/* %s */", __func__);
3d14a300 857
32a4456c
MS
858 m = hashmap_new(&string_hash_ops);
859 assert_se(m);
860
861 assert_se(!hashmap_first_key(m));
862 assert_se(hashmap_put(m, "key 1", NULL) == 1);
863 assert_se(streq(hashmap_first_key(m), "key 1"));
864 assert_se(hashmap_put(m, "key 2", NULL) == 1);
865#ifdef ORDERED
866 assert_se(streq(hashmap_first_key(m), "key 1"));
867 assert_se(hashmap_remove(m, "key 1") == NULL);
868 assert_se(streq(hashmap_first_key(m), "key 2"));
869#endif
870}
871
872static void test_hashmap_steal_first_key(void) {
873 _cleanup_hashmap_free_ Hashmap *m = NULL;
874
32ca2911 875 log_info("/* %s */", __func__);
3d14a300 876
32a4456c
MS
877 m = hashmap_new(&string_hash_ops);
878 assert_se(m);
879
880 assert_se(!hashmap_steal_first_key(m));
881 assert_se(hashmap_put(m, "key 1", NULL) == 1);
882 assert_se(streq(hashmap_steal_first_key(m), "key 1"));
883
884 assert_se(hashmap_isempty(m));
885}
886
887static void test_hashmap_steal_first(void) {
888 _cleanup_hashmap_free_ Hashmap *m = NULL;
889 int seen[3] = {};
890 char *val;
891
32ca2911 892 log_info("/* %s */", __func__);
3d14a300 893
32a4456c
MS
894 m = hashmap_new(&string_hash_ops);
895 assert_se(m);
896
897 assert_se(hashmap_put(m, "key 1", (void*) "1") == 1);
898 assert_se(hashmap_put(m, "key 2", (void*) "22") == 1);
899 assert_se(hashmap_put(m, "key 3", (void*) "333") == 1);
900
901 while ((val = hashmap_steal_first(m)))
902 seen[strlen(val) - 1]++;
903
904 assert_se(seen[0] == 1 && seen[1] == 1 && seen[2] == 1);
905
906 assert_se(hashmap_isempty(m));
907}
908
909static void test_hashmap_clear_free_free(void) {
910 _cleanup_hashmap_free_ Hashmap *m = NULL;
911
32ca2911 912 log_info("/* %s */", __func__);
3d14a300 913
32a4456c
MS
914 m = hashmap_new(&string_hash_ops);
915 assert_se(m);
916
917 assert_se(hashmap_put(m, strdup("key 1"), NULL) == 1);
918 assert_se(hashmap_put(m, strdup("key 2"), NULL) == 1);
919 assert_se(hashmap_put(m, strdup("key 3"), NULL) == 1);
920
921 hashmap_clear_free_free(m);
922 assert_se(hashmap_isempty(m));
98233ee5
YW
923
924 assert_se(hashmap_put(m, strdup("key 1"), strdup("value 1")) == 1);
925 assert_se(hashmap_put(m, strdup("key 2"), strdup("value 2")) == 1);
926 assert_se(hashmap_put(m, strdup("key 3"), strdup("value 3")) == 1);
927
928 hashmap_clear_free_free(m);
929 assert_se(hashmap_isempty(m));
930}
931
932DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(test_hash_ops_key, char, string_hash_func, string_compare_func, free);
933DEFINE_PRIVATE_HASH_OPS_FULL(test_hash_ops_full, char, string_hash_func, string_compare_func, free, char, free);
934
935static void test_hashmap_clear_free_with_destructor(void) {
936 _cleanup_hashmap_free_ Hashmap *m = NULL;
937
32ca2911 938 log_info("/* %s */", __func__);
98233ee5
YW
939
940 m = hashmap_new(&test_hash_ops_key);
941 assert_se(m);
942
943 assert_se(hashmap_put(m, strdup("key 1"), NULL) == 1);
944 assert_se(hashmap_put(m, strdup("key 2"), NULL) == 1);
945 assert_se(hashmap_put(m, strdup("key 3"), NULL) == 1);
946
947 hashmap_clear_free(m);
948 assert_se(hashmap_isempty(m));
949 m = hashmap_free(m);
950
951 m = hashmap_new(&test_hash_ops_full);
952 assert_se(m);
953
954 assert_se(hashmap_put(m, strdup("key 1"), strdup("value 1")) == 1);
955 assert_se(hashmap_put(m, strdup("key 2"), strdup("value 2")) == 1);
956 assert_se(hashmap_put(m, strdup("key 3"), strdup("value 3")) == 1);
957
958 hashmap_clear_free(m);
959 assert_se(hashmap_isempty(m));
32a4456c
MS
960}
961
8f88aed7
MS
962static void test_hashmap_reserve(void) {
963 _cleanup_hashmap_free_ Hashmap *m = NULL;
964
32ca2911 965 log_info("/* %s */", __func__);
3d14a300 966
8f88aed7
MS
967 m = hashmap_new(&string_hash_ops);
968
969 assert_se(hashmap_reserve(m, 1) == 0);
970 assert_se(hashmap_buckets(m) < 1000);
971 assert_se(hashmap_reserve(m, 1000) == 0);
972 assert_se(hashmap_buckets(m) >= 1000);
973 assert_se(hashmap_isempty(m));
974
975 assert_se(hashmap_put(m, "key 1", (void*) "val 1") == 1);
976
977 assert_se(hashmap_reserve(m, UINT_MAX) == -ENOMEM);
978 assert_se(hashmap_reserve(m, UINT_MAX - 1) == -ENOMEM);
979}
980
32a4456c 981void test_hashmap_funcs(void) {
3d14a300
ZJS
982 log_parse_environment();
983 log_open();
984
32a4456c
MS
985 test_hashmap_copy();
986 test_hashmap_get_strv();
987 test_hashmap_move_one();
9ba81d5a 988 test_hashmap_move();
32a4456c
MS
989 test_hashmap_replace();
990 test_hashmap_update();
991 test_hashmap_put();
9ba81d5a
MS
992 test_hashmap_remove();
993 test_hashmap_remove2();
994 test_hashmap_remove_value();
32a4456c 995 test_hashmap_remove_and_put();
9ba81d5a 996 test_hashmap_remove_and_replace();
32a4456c
MS
997 test_hashmap_ensure_allocated();
998 test_hashmap_foreach();
999 test_hashmap_foreach_key();
1000 test_hashmap_contains();
1001 test_hashmap_merge();
1002 test_hashmap_isempty();
1003 test_hashmap_get();
9ba81d5a 1004 test_hashmap_get2();
32a4456c
MS
1005 test_hashmap_size();
1006 test_hashmap_many();
8872c3a3 1007 test_hashmap_free();
9ba81d5a 1008 test_hashmap_first();
32a4456c
MS
1009 test_hashmap_first_key();
1010 test_hashmap_steal_first_key();
1011 test_hashmap_steal_first();
1012 test_hashmap_clear_free_free();
98233ee5 1013 test_hashmap_clear_free_with_destructor();
8f88aed7 1014 test_hashmap_reserve();
32a4456c 1015}