1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
8 #include "nulstr-util.h"
9 #include "stdio-util.h"
12 #include "time-util.h"
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. */
18 # define PROJECT_FILE __FILE__
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
;
26 m
= hashmap_new(&string_hash_ops
);
28 val1
= strdup("val1");
30 val2
= strdup("val2");
32 val3
= strdup("val3");
34 val4
= strdup("val4");
36 val5
= strdup("val5");
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
);
44 hashmap_replace(m
, "key 3", val1
);
45 r
= hashmap_get(m
, "key 3");
46 ASSERT_STREQ(r
, "val1");
48 hashmap_replace(m
, "key 5", val5
);
49 r
= hashmap_get(m
, "key 5");
50 ASSERT_STREQ(r
, "val5");
53 TEST(hashmap_ensure_replace
) {
54 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
55 _cleanup_free_
char *val1
= NULL
, *val2
= NULL
;
57 val1
= strdup("val1");
58 ASSERT_NOT_NULL(val1
);
59 val2
= strdup("val2");
60 ASSERT_NOT_NULL(val2
);
62 ASSERT_OK(hashmap_ensure_replace(&m
, &string_hash_ops
, val1
, val2
));
64 ASSERT_OK(hashmap_ensure_replace(&m
, &string_hash_ops
, "key 1", val1
));
65 ASSERT_STREQ(hashmap_get(m
, "key 1"), "val1");
67 ASSERT_OK(hashmap_ensure_replace(&m
, &string_hash_ops
, "key 2", val2
));
68 ASSERT_STREQ(hashmap_get(m
, "key 2"), "val2");
70 ASSERT_OK(hashmap_ensure_replace(&m
, &string_hash_ops
, "key 3", val1
));
71 ASSERT_STREQ(hashmap_get(m
, "key 3"), "val1");
73 ASSERT_OK(hashmap_ensure_replace(&m
, &string_hash_ops
, "key 3", val2
));
74 ASSERT_STREQ(hashmap_get(m
, "key 3"), "val2");
78 _cleanup_hashmap_free_ Hashmap
*m
= NULL
, *copy
= NULL
;
80 ASSERT_NOT_NULL((m
= hashmap_new(&string_hash_ops
)));
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"));
87 ASSERT_NOT_NULL((copy
= hashmap_copy(m
)));
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");
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
;
100 val1
= strdup("val1");
102 val2
= strdup("val2");
104 val3
= strdup("val3");
106 val4
= strdup("val4");
109 m
= hashmap_new(&string_hash_ops
);
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
);
116 strv
= hashmap_get_strv(m
);
119 strv
= strv_sort(strv
);
122 ASSERT_STREQ(strv
[0], "val1");
123 ASSERT_STREQ(strv
[1], "val2");
124 ASSERT_STREQ(strv
[2], "val3");
125 ASSERT_STREQ(strv
[3], "val4");
128 TEST(hashmap_move_one
) {
129 _cleanup_hashmap_free_ Hashmap
*m
= NULL
, *n
= NULL
;
130 char *val1
, *val2
, *val3
, *val4
, *r
;
132 val1
= strdup("val1");
134 val2
= strdup("val2");
136 val3
= strdup("val3");
138 val4
= strdup("val4");
141 m
= hashmap_new(&string_hash_ops_value_free
);
142 n
= hashmap_new(&string_hash_ops_value_free
);
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
);
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);
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");
161 assert_se(hashmap_move_one(n
, m
, "key 3") == -EEXIST
);
165 _cleanup_hashmap_free_ Hashmap
*m
= NULL
, *n
= NULL
;
166 char *val1
, *val2
, *val3
, *val4
, *r
;
168 val1
= strdup("val1");
170 val2
= strdup("val2");
172 val3
= strdup("val3");
174 val4
= strdup("val4");
177 m
= hashmap_new(&string_hash_ops_value_free
);
178 n
= hashmap_new(&string_hash_ops_value_free
);
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
);
186 assert_se(hashmap_move(n
, NULL
) == 0);
187 assert_se(hashmap_move(n
, m
) == 0);
189 assert_se(hashmap_size(m
) == 1);
190 r
= hashmap_get(m
, "key 1");
191 assert_se(r
&& streq(r
, "val1"));
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"));
203 TEST(hashmap_update
) {
204 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
205 _cleanup_free_
char *val1
= NULL
, *val2
= NULL
;
208 m
= hashmap_new(&string_hash_ops
);
209 val1
= strdup("old_value");
211 val2
= strdup("new_value");
214 hashmap_put(m
, "key 1", val1
);
215 r
= hashmap_get(m
, "key 1");
216 ASSERT_STREQ(r
, "old_value");
218 assert_se(hashmap_update(m
, "key 2", val2
) == -ENOENT
);
219 r
= hashmap_get(m
, "key 1");
220 ASSERT_STREQ(r
, "old_value");
222 assert_se(hashmap_update(m
, "key 1", val2
) == 0);
223 r
= hashmap_get(m
, "key 1");
224 ASSERT_STREQ(r
, "new_value");
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
;
234 assert_se(hashmap_ensure_allocated(&m
, &string_hash_ops
) == 1);
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
);
246 TEST(hashmap_remove1
) {
247 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
250 r
= hashmap_remove(NULL
, "key 1");
253 m
= hashmap_new(&string_hash_ops
);
256 r
= hashmap_remove(m
, "no such key");
259 hashmap_put(m
, "key 1", (void*) "val 1");
260 hashmap_put(m
, "key 2", (void*) "val 2");
262 r
= hashmap_remove(m
, "key 1");
263 ASSERT_STREQ(r
, "val 1");
265 r
= hashmap_get(m
, "key 2");
266 ASSERT_STREQ(r
, "val 2");
267 assert_se(!hashmap_get(m
, "key 1"));
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";
278 r
= hashmap_remove2(NULL
, "key 1", &r2
);
281 m
= hashmap_new(&string_hash_ops_free_free
);
284 r
= hashmap_remove2(m
, "no such key", &r2
);
287 hashmap_put(m
, strdup(key1
), strdup(val1
));
288 hashmap_put(m
, strdup(key2
), strdup(val2
));
290 r
= hashmap_remove2(m
, key1
, &r2
);
291 ASSERT_STREQ(r
, val1
);
292 ASSERT_STREQ(r2
, key1
);
296 r
= hashmap_get(m
, key2
);
297 ASSERT_STREQ(r
, val2
);
298 assert_se(!hashmap_get(m
, key1
));
301 TEST(hashmap_remove_value
) {
302 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
305 char val1
[] = "val 1";
306 char val2
[] = "val 2";
308 r
= hashmap_remove_value(NULL
, "key 1", val1
);
311 m
= hashmap_new(&string_hash_ops
);
314 r
= hashmap_remove_value(m
, "key 1", val1
);
317 hashmap_put(m
, "key 1", val1
);
318 hashmap_put(m
, "key 2", val2
);
320 r
= hashmap_remove_value(m
, "key 1", val1
);
321 ASSERT_STREQ(r
, "val 1");
323 r
= hashmap_get(m
, "key 2");
324 ASSERT_STREQ(r
, "val 2");
325 assert_se(!hashmap_get(m
, "key 1"));
327 r
= hashmap_remove_value(m
, "key 2", val1
);
330 r
= hashmap_get(m
, "key 2");
331 ASSERT_STREQ(r
, "val 2");
332 assert_se(!hashmap_get(m
, "key 1"));
335 TEST(hashmap_remove_and_put
) {
336 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
340 m
= hashmap_new(&string_hash_ops
);
343 valid
= hashmap_remove_and_put(m
, "invalid key", "new key", NULL
);
344 assert_se(valid
== -ENOENT
);
346 valid
= hashmap_put(m
, "key 1", (void*) (const char *) "val 1");
347 assert_se(valid
== 1);
349 valid
= hashmap_remove_and_put(NULL
, "key 1", "key 2", (void*) (const char *) "val 2");
350 assert_se(valid
== -ENOENT
);
352 valid
= hashmap_remove_and_put(m
, "key 1", "key 2", (void*) (const char *) "val 2");
353 assert_se(valid
== 0);
355 r
= hashmap_get(m
, "key 2");
356 ASSERT_STREQ(r
, "val 2");
357 assert_se(!hashmap_get(m
, "key 1"));
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
);
365 TEST(hashmap_remove_and_replace
) {
366 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
368 void *key1
= UINT_TO_PTR(1);
369 void *key2
= UINT_TO_PTR(2);
370 void *key3
= UINT_TO_PTR(3);
374 m
= hashmap_new(&trivial_hash_ops
);
377 valid
= hashmap_remove_and_replace(m
, key1
, key2
, NULL
);
378 assert_se(valid
== -ENOENT
);
380 valid
= hashmap_put(m
, key1
, key1
);
381 assert_se(valid
== 1);
383 valid
= hashmap_remove_and_replace(NULL
, key1
, key2
, key2
);
384 assert_se(valid
== -ENOENT
);
386 valid
= hashmap_remove_and_replace(m
, key1
, key2
, key2
);
387 assert_se(valid
== 0);
389 r
= hashmap_get(m
, key2
);
390 assert_se(r
== key2
);
391 assert_se(!hashmap_get(m
, key1
));
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
));
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
++) {
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
));
421 TEST(hashmap_ensure_allocated
) {
422 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
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
);
429 TEST(hashmap_foreach_key
) {
430 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
431 bool key_found
[] = { false, false, false, false };
434 static const char key_table
[] =
440 m
= hashmap_new(&string_hash_ops
);
442 NULSTR_FOREACH(k
, key_table
)
443 hashmap_put(m
, k
, (void*) (const char*) "my dummy val");
445 HASHMAP_FOREACH_KEY(s
, key
, m
) {
447 if (!key_found
[0] && streq(key
, "key 1"))
449 else if (!key_found
[1] && streq(key
, "key 2"))
451 else if (!key_found
[2] && streq(key
, "key 3"))
453 else if (!key_found
[3] && streq(key
, "fail"))
458 assert_se(key_found
[0] && key_found
[1] && key_found
[2] && !key_found
[3]);
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
;
467 val1
= strdup("my val1");
469 val2
= strdup("my val2");
471 val3
= strdup("my val3");
473 val4
= strdup("my val4");
477 HASHMAP_FOREACH(s
, m
)
479 assert_se(count
== 0);
481 m
= hashmap_new(&string_hash_ops_value_free
);
484 HASHMAP_FOREACH(s
, m
)
486 assert_se(count
== 0);
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
);
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;
505 assert_se(value_found
[0] && value_found
[1] && value_found
[2] && value_found
[3]);
508 TEST(hashmap_merge
) {
509 _cleanup_hashmap_free_ Hashmap
*m
= NULL
, *n
= NULL
;
510 char *val1
, *val2
, *val3
, *val4
, *r
;
512 val1
= strdup("my val1");
514 val2
= strdup("my val2");
516 val3
= strdup("my val3");
518 val4
= strdup("my val4");
521 m
= hashmap_new(&string_hash_ops_value_free
);
522 n
= hashmap_new(&string_hash_ops
);
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
);
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"));
539 TEST(hashmap_contains
) {
540 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
543 val1
= strdup("my val");
546 m
= hashmap_new(&string_hash_ops_value_free
);
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"));
553 assert_se(!hashmap_contains(NULL
, "Key 1"));
558 TEST(hashmap_isempty
) {
559 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
562 val1
= strdup("my val");
565 m
= hashmap_new(&string_hash_ops_value_free
);
567 assert_se(hashmap_isempty(m
));
568 hashmap_put(m
, "Key 1", val1
);
569 assert_se(!hashmap_isempty(m
));
575 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
576 char *val1
, *val2
, *val3
, *val4
;
578 val1
= strdup("my val");
580 val2
= strdup("my val");
582 val3
= strdup("my val");
584 val4
= strdup("my val");
587 assert_se(hashmap_size(NULL
) == 0);
588 assert_se(hashmap_buckets(NULL
) == 0);
590 m
= hashmap_new(&string_hash_ops_value_free
);
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
);
598 assert_se(hashmap_size(m
) == 4);
599 assert_se(hashmap_buckets(m
) >= 4);
603 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
607 val
= strdup("my val");
610 r
= hashmap_get(NULL
, "Key 1");
613 m
= hashmap_new(&string_hash_ops_value_free
);
615 hashmap_put(m
, "Key 1", val
);
617 r
= hashmap_get(m
, "Key 1");
618 ASSERT_STREQ(r
, val
);
620 r
= hashmap_get(m
, "no such key");
627 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
630 char key_orig
[] = "Key 1";
633 val
= strdup("my val");
636 key_copy
= strdup(key_orig
);
639 r
= hashmap_get2(NULL
, key_orig
, &key_copy
);
642 m
= hashmap_new(&string_hash_ops_free_free
);
644 hashmap_put(m
, key_copy
, val
);
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
);
652 r
= hashmap_get2(m
, "no such key", NULL
);
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
);
662 static const struct hash_ops crippled_hashmap_ops
= {
663 .hash
= crippled_hashmap_func
,
664 .compare
= trivial_compare_func
,
671 bool slow
= slow_tests_enabled();
674 const struct hash_ops
*ops
;
677 { "trivial_hashmap_ops", NULL
, slow
? 1 << 20 : 240 },
678 { "crippled_hashmap_ops", &crippled_hashmap_ops
, slow
? 1 << 14 : 140 },
681 log_info("/* %s (%s) */", __func__
, slow
? "slow" : "fast");
683 FOREACH_ELEMENT(test
, tests
) {
684 usec_t ts
= now(CLOCK_MONOTONIC
), n
;
686 assert_se(h
= hashmap_new(test
->ops
));
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
);
693 for (i
= 1; i
< test
->n_entries
*3; i
++)
694 assert_se(hashmap_contains(h
, UINT_TO_PTR(i
)) == (i
% 3 == 1));
696 log_info("%s %u <= %u * 0.8 = %g",
697 test
->title
, hashmap_size(h
), hashmap_buckets(h
), hashmap_buckets(h
) * 0.8);
699 assert_se(hashmap_size(h
) <= hashmap_buckets(h
) * 0.8);
700 assert_se(hashmap_size(h
) == test
->n_entries
);
702 while (!hashmap_isempty(h
)) {
703 k
= hashmap_first_key(h
);
704 v
= hashmap_remove(h
, k
);
710 n
= now(CLOCK_MONOTONIC
);
711 log_info("test took %s", FORMAT_TIMESPAN(n
- ts
, 0));
715 extern unsigned custom_counter
;
716 extern const struct hash_ops boring_hash_ops
, custom_hash_ops
;
720 bool slow
= slow_tests_enabled();
722 unsigned n_entries
= slow
? 1 << 20 : 240;
726 const struct hash_ops
*ops
;
727 unsigned expect_counter
;
729 { "string_hash_ops", &boring_hash_ops
, 2 * n_entries
},
730 { "custom_free_hash_ops", &custom_hash_ops
, 0 },
733 log_info("/* %s (%s, %u entries) */", __func__
, slow
? "slow" : "fast", n_entries
);
735 FOREACH_ELEMENT(test
, tests
) {
736 ts
= now(CLOCK_MONOTONIC
);
737 assert_se(h
= hashmap_new(test
->ops
));
740 for (unsigned i
= 0; i
< n_entries
; i
++) {
741 char s
[DECIMAL_STR_MAX(unsigned)];
744 xsprintf(s
, "%u", i
);
745 assert_se(k
= strdup(s
));
746 assert_se(v
= strdup(s
));
749 assert_se(hashmap_put(h
, k
, v
) >= 0);
754 n
= now(CLOCK_MONOTONIC
);
755 log_info("%s test took %s", test
->title
, FORMAT_TIMESPAN(n
- ts
, 0));
757 assert_se(custom_counter
== test
->expect_counter
);
761 TEST(hashmap_first
) {
762 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
764 m
= hashmap_new(&string_hash_ops
);
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);
772 ASSERT_STREQ(hashmap_first(m
), "val 1");
773 assert_se(hashmap_remove(m
, "key 1"));
774 ASSERT_STREQ(hashmap_first(m
), "val 2");
778 TEST(hashmap_first_key
) {
779 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
781 m
= hashmap_new(&string_hash_ops
);
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);
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");
795 TEST(hashmap_steal_first_key
) {
796 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
798 m
= hashmap_new(&string_hash_ops
);
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");
805 assert_se(hashmap_isempty(m
));
808 TEST(hashmap_steal_first
) {
809 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
813 m
= hashmap_new(&string_hash_ops
);
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);
820 while ((val
= hashmap_steal_first(m
)))
821 seen
[strlen(val
) - 1]++;
823 assert_se(seen
[0] == 1 && seen
[1] == 1 && seen
[2] == 1);
825 assert_se(hashmap_isempty(m
));
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
);
831 TEST(hashmap_clear
) {
832 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
834 m
= hashmap_new(&string_hash_ops_free_free
);
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);
842 assert_se(hashmap_isempty(m
));
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);
849 assert_se(hashmap_isempty(m
));
852 m
= hashmap_new(&test_hash_ops_key
);
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);
860 assert_se(hashmap_isempty(m
));
863 m
= hashmap_new(&test_hash_ops_full
);
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);
871 assert_se(hashmap_isempty(m
));
874 TEST(hashmap_reserve
) {
875 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
877 m
= hashmap_new(&string_hash_ops
);
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
));
885 assert_se(hashmap_put(m
, "key 1", (void*) "val 1") == 1);
887 assert_se(hashmap_reserve(m
, UINT_MAX
) == -ENOMEM
);
888 assert_se(hashmap_reserve(m
, UINT_MAX
- 1) == -ENOMEM
);
892 _cleanup_hashmap_free_ Hashmap
*h
= NULL
;
894 assert_se(h
= hashmap_new(&path_hash_ops
));
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
);
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));
924 TEST(string_strv_hashmap
) {
925 _cleanup_hashmap_free_ Hashmap
*m
= NULL
;
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"));
935 s
= hashmap_get(m
, "foo");
936 assert_se(strv_equal(s
, STRV_MAKE("bar", "BAR")));
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"));
945 s
= hashmap_get(m
, "xxx");
946 assert_se(strv_equal(s
, STRV_MAKE("bar", "BAR")));
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")));
952 string_strv_hashmap_remove(m
, "foo", "BAR");
953 ASSERT_NULL(hashmap_get(m
, "foo"));
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")));
959 string_strv_hashmap_remove(m
, "xxx", "bar");
960 ASSERT_NULL(hashmap_get(m
, "xxx"));
962 ASSERT_TRUE(hashmap_isempty(m
));
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
;
973 assert_se(m
= hashmap_new(&string_hash_ops
));
975 assert_se(hashmap_dump_sorted(m
, &vals
, &n
) >= 0);
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);
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);
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
]);
997 assert_se(m
= hashmap_new(NULL
));
999 assert_se(hashmap_dump_sorted(m
, &vals
, &n
) >= 0);
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);
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);
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);
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
++;