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