153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
253a5a1b3Sopenharmony_ci#include <config.h>
353a5a1b3Sopenharmony_ci#endif
453a5a1b3Sopenharmony_ci
553a5a1b3Sopenharmony_ci#include <check.h>
653a5a1b3Sopenharmony_ci#include <pulsecore/hashmap.h>
753a5a1b3Sopenharmony_ci
853a5a1b3Sopenharmony_cistruct int_entry {
953a5a1b3Sopenharmony_ci    int key;
1053a5a1b3Sopenharmony_ci    int value;
1153a5a1b3Sopenharmony_ci};
1253a5a1b3Sopenharmony_ci
1353a5a1b3Sopenharmony_cistatic unsigned int_trivial_hash_func(const void* pa) {
1453a5a1b3Sopenharmony_ci    int a = *((unsigned*) pa);
1553a5a1b3Sopenharmony_ci    if (a < 0) {
1653a5a1b3Sopenharmony_ci        return -a;
1753a5a1b3Sopenharmony_ci    }
1853a5a1b3Sopenharmony_ci    return a;
1953a5a1b3Sopenharmony_ci}
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_cistatic int int_compare_func(const void* pa, const void* pb) {
2253a5a1b3Sopenharmony_ci    int a, b;
2353a5a1b3Sopenharmony_ci    a = *((int*) pa);
2453a5a1b3Sopenharmony_ci    b = *((int*) pb);
2553a5a1b3Sopenharmony_ci    if (a < b) {
2653a5a1b3Sopenharmony_ci        return -1;
2753a5a1b3Sopenharmony_ci    }
2853a5a1b3Sopenharmony_ci    if (a == b) {
2953a5a1b3Sopenharmony_ci        return 0;
3053a5a1b3Sopenharmony_ci    }
3153a5a1b3Sopenharmony_ci    return 1;
3253a5a1b3Sopenharmony_ci}
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci/* single_key_test exercises basic hashmap functionality on a single key. */
3553a5a1b3Sopenharmony_ciSTART_TEST(single_key_test)
3653a5a1b3Sopenharmony_ci    {
3753a5a1b3Sopenharmony_ci        pa_hashmap* map;
3853a5a1b3Sopenharmony_ci        struct int_entry entry;
3953a5a1b3Sopenharmony_ci        int lookup_key;
4053a5a1b3Sopenharmony_ci        int put_ret;
4153a5a1b3Sopenharmony_ci        void* get_ret;
4253a5a1b3Sopenharmony_ci        unsigned size_ret;
4353a5a1b3Sopenharmony_ci
4453a5a1b3Sopenharmony_ci        entry.key = 0;
4553a5a1b3Sopenharmony_ci        entry.value = 0;
4653a5a1b3Sopenharmony_ci
4753a5a1b3Sopenharmony_ci        lookup_key = 0;
4853a5a1b3Sopenharmony_ci
4953a5a1b3Sopenharmony_ci        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci        if ((put_ret = pa_hashmap_put(map, &entry.key, &entry)) != 0) {
5253a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap rejected k=0/v=0; got %d, want %d", put_ret, 0);
5353a5a1b3Sopenharmony_ci        }
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_ci        if ((size_ret = pa_hashmap_size(map)) != 1) {
5653a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap reported wrong size; got %u, want 1", size_ret);
5753a5a1b3Sopenharmony_ci        }
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_ci        if ((get_ret = pa_hashmap_get(map, &lookup_key)) != &entry) {
6053a5a1b3Sopenharmony_ci            ck_abort_msg("Got wrong value from hashmap for k=0; got %p, want %p", get_ret, &entry);
6153a5a1b3Sopenharmony_ci        }
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_ci        if ((put_ret = pa_hashmap_put(map, &entry.key, &entry)) == 0) {
6453a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap allowed duplicate key for k=0; got %d, want non-zero", put_ret);
6553a5a1b3Sopenharmony_ci        }
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_ci        if ((size_ret = pa_hashmap_size(map)) != 1) {
6853a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap reported wrong size; got %u, want 1", size_ret);
6953a5a1b3Sopenharmony_ci        }
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_ci        if ((get_ret = pa_hashmap_remove(map, &lookup_key)) != &entry) {
7253a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap returned wrong value during free; got %p, want %p", get_ret, &entry);
7353a5a1b3Sopenharmony_ci        }
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci        if ((size_ret = pa_hashmap_size(map)) != 0) {
7653a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap reported wrong size; got %u, want 1", size_ret);
7753a5a1b3Sopenharmony_ci        }
7853a5a1b3Sopenharmony_ci
7953a5a1b3Sopenharmony_ci        pa_hashmap_free(map);
8053a5a1b3Sopenharmony_ci    }
8153a5a1b3Sopenharmony_ciEND_TEST
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_ci/* remove_all_test checks that pa_hashmap_remove_all really removes all entries
8453a5a1b3Sopenharmony_ci * from the map.*/
8553a5a1b3Sopenharmony_ciSTART_TEST(remove_all_test)
8653a5a1b3Sopenharmony_ci    {
8753a5a1b3Sopenharmony_ci        pa_hashmap* map;
8853a5a1b3Sopenharmony_ci        struct int_entry entries[1000];
8953a5a1b3Sopenharmony_ci        unsigned size;
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_ci        for (int i = 0; i < 1000; i++) {
9253a5a1b3Sopenharmony_ci            entries[i].key = i;
9353a5a1b3Sopenharmony_ci            entries[i].value = i;
9453a5a1b3Sopenharmony_ci        }
9553a5a1b3Sopenharmony_ci
9653a5a1b3Sopenharmony_ci        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci        for (int i = 0; i < 1000; i++) {
9953a5a1b3Sopenharmony_ci            pa_hashmap_put(map, &entries[i].key, &entries[i]);
10053a5a1b3Sopenharmony_ci        }
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci        if ((size = pa_hashmap_size(map)) != 1000) {
10353a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap has wrong size; got %u, want 1000", size);
10453a5a1b3Sopenharmony_ci        }
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci        pa_hashmap_remove_all(map);
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci        if ((size = pa_hashmap_size(map)) != 0) {
10953a5a1b3Sopenharmony_ci            ck_abort_msg("Hashmap has wrong size; got %u, want 0", size);
11053a5a1b3Sopenharmony_ci        }
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci        pa_hashmap_free(map);
11353a5a1b3Sopenharmony_ci    }
11453a5a1b3Sopenharmony_ciEND_TEST
11553a5a1b3Sopenharmony_ci
11653a5a1b3Sopenharmony_ci/* fill_all_buckets hits the hashmap with enough keys to exercise the bucket
11753a5a1b3Sopenharmony_ci * linked list for every bucket. */
11853a5a1b3Sopenharmony_ciSTART_TEST(fill_all_buckets)
11953a5a1b3Sopenharmony_ci    {
12053a5a1b3Sopenharmony_ci        pa_hashmap* map;
12153a5a1b3Sopenharmony_ci        struct int_entry entries[1000];
12253a5a1b3Sopenharmony_ci        int lookup_keys[1000]; /* Don't share addresses with insertion keys */
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
12553a5a1b3Sopenharmony_ci
12653a5a1b3Sopenharmony_ci        for (int i = 0; i < 1000; i++) {
12753a5a1b3Sopenharmony_ci            entries[i].key = i;
12853a5a1b3Sopenharmony_ci            lookup_keys[i] = i;
12953a5a1b3Sopenharmony_ci            entries[i].value = i;
13053a5a1b3Sopenharmony_ci        }
13153a5a1b3Sopenharmony_ci
13253a5a1b3Sopenharmony_ci        for (int i = 0; i < 1000; i++) {
13353a5a1b3Sopenharmony_ci            int put_ret;
13453a5a1b3Sopenharmony_ci            unsigned size_ret;
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci            if ((put_ret = pa_hashmap_put(map, &entries[i].key, &entries[i])) != 0) {
13753a5a1b3Sopenharmony_ci                ck_abort_msg("Unexpected failure putting k=%d v=%d into the map", entries[i].key, entries[i].value);
13853a5a1b3Sopenharmony_ci            }
13953a5a1b3Sopenharmony_ci
14053a5a1b3Sopenharmony_ci            if ((size_ret = pa_hashmap_size(map)) != i + 1) {
14153a5a1b3Sopenharmony_ci                ck_abort_msg("Hashmap reported wrong size; got %u, want %d", size_ret, i);
14253a5a1b3Sopenharmony_ci            }
14353a5a1b3Sopenharmony_ci        }
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci        for (int i = 0; i < 1000; i++) {
14653a5a1b3Sopenharmony_ci            unsigned size_ret;
14753a5a1b3Sopenharmony_ci            int* k;
14853a5a1b3Sopenharmony_ci            struct int_entry* v;
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci            k = lookup_keys + i;
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci            v = (struct int_entry*) pa_hashmap_remove(map, k);
15353a5a1b3Sopenharmony_ci            if (v == NULL) {
15453a5a1b3Sopenharmony_ci                ck_abort_msg("Hashmap returned NULL for k=%d; wanted nonnull", *k);
15553a5a1b3Sopenharmony_ci            }
15653a5a1b3Sopenharmony_ci            if ((*v).value != i) {
15753a5a1b3Sopenharmony_ci                ck_abort_msg("Hashmap returned wrong value for k=%d; got %d, want %d", *k, (*v).value, i);
15853a5a1b3Sopenharmony_ci            }
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci            if ((size_ret = pa_hashmap_size(map)) != 1000 - i - 1) {
16153a5a1b3Sopenharmony_ci                ck_abort_msg("Hashmap reported wrong size; got %u, want %d", size_ret, 1000 - i);
16253a5a1b3Sopenharmony_ci            }
16353a5a1b3Sopenharmony_ci        }
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_ci        pa_hashmap_free(map);
16653a5a1b3Sopenharmony_ci    }
16753a5a1b3Sopenharmony_ciEND_TEST
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_ci/* iterate_test exercises the iteration list maintained by the hashtable. */
17053a5a1b3Sopenharmony_ciSTART_TEST(iterate_test)
17153a5a1b3Sopenharmony_ci    {
17253a5a1b3Sopenharmony_ci        pa_hashmap* map;
17353a5a1b3Sopenharmony_ci        struct int_entry entries[1000];
17453a5a1b3Sopenharmony_ci        void* state;
17553a5a1b3Sopenharmony_ci        struct int_entry* v;
17653a5a1b3Sopenharmony_ci        int expected;
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_ci        for (int i = 0; i < 1000; i++) {
17953a5a1b3Sopenharmony_ci            entries[i].key = i;
18053a5a1b3Sopenharmony_ci            entries[i].value = i;
18153a5a1b3Sopenharmony_ci        }
18253a5a1b3Sopenharmony_ci
18353a5a1b3Sopenharmony_ci        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
18453a5a1b3Sopenharmony_ci
18553a5a1b3Sopenharmony_ci        for (int i = 0; i < 1000; i++) {
18653a5a1b3Sopenharmony_ci            if (pa_hashmap_put(map, &(entries[i].key), &(entries[i])) != 0) {
18753a5a1b3Sopenharmony_ci                ck_abort_msg("Unexpected failure putting k=%d v=%d into the map", entries[i].key, entries[i].value);
18853a5a1b3Sopenharmony_ci            }
18953a5a1b3Sopenharmony_ci        }
19053a5a1b3Sopenharmony_ci
19153a5a1b3Sopenharmony_ci        expected = 0;
19253a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH (v, map, state) {
19353a5a1b3Sopenharmony_ci            if ((*v).value != expected) {
19453a5a1b3Sopenharmony_ci                ck_abort_msg("Got bad order iterating over hashmap: got %d, want %d", v->value, expected);
19553a5a1b3Sopenharmony_ci            }
19653a5a1b3Sopenharmony_ci            expected++;
19753a5a1b3Sopenharmony_ci        }
19853a5a1b3Sopenharmony_ci
19953a5a1b3Sopenharmony_ci        expected = 999;
20053a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH_BACKWARDS (v, map, state) {
20153a5a1b3Sopenharmony_ci            if ((*v).value != expected) {
20253a5a1b3Sopenharmony_ci                ck_abort_msg("Got bad order iterating over hashmap: got %d, want %d", v->value, expected);
20353a5a1b3Sopenharmony_ci            }
20453a5a1b3Sopenharmony_ci            expected--;
20553a5a1b3Sopenharmony_ci        }
20653a5a1b3Sopenharmony_ci
20753a5a1b3Sopenharmony_ci        /* Now empty out the hashmap.  The iteration list should be empty. */
20853a5a1b3Sopenharmony_ci        for(int i = 0; i < 1000; i++) {
20953a5a1b3Sopenharmony_ci          pa_hashmap_remove(map, &(entries[i].key));
21053a5a1b3Sopenharmony_ci        }
21153a5a1b3Sopenharmony_ci
21253a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(v, map, state) {
21353a5a1b3Sopenharmony_ci          ck_abort_msg("Iteration over empty map returned entries");
21453a5a1b3Sopenharmony_ci        }
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci        /* Now add one element back. The iteration list should only contain this
21753a5a1b3Sopenharmony_ci         * one element, even though the entry nodes are reused. */
21853a5a1b3Sopenharmony_ci        if(pa_hashmap_put(map, &(entries[0].key), &(entries[0])) != 0) {
21953a5a1b3Sopenharmony_ci          ck_abort_msg("Unexpected failure putting k=%d v=%d into the map", entries[0].key, entries[0].value);
22053a5a1b3Sopenharmony_ci        }
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci        expected = 0;
22353a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(v, map, state) {
22453a5a1b3Sopenharmony_ci           if ((*v).value != expected) {
22553a5a1b3Sopenharmony_ci                ck_abort_msg("Got bad order iterating over hashmap: got %d, want %d", v->value, expected);
22653a5a1b3Sopenharmony_ci            }
22753a5a1b3Sopenharmony_ci           expected++;
22853a5a1b3Sopenharmony_ci        }
22953a5a1b3Sopenharmony_ci        if (expected != 1) {
23053a5a1b3Sopenharmony_ci          ck_abort_msg("Got too many elements while iterating: got %d, want 1", expected);
23153a5a1b3Sopenharmony_ci        }
23253a5a1b3Sopenharmony_ci
23353a5a1b3Sopenharmony_ci        pa_hashmap_free(map);
23453a5a1b3Sopenharmony_ci    }
23553a5a1b3Sopenharmony_ciEND_TEST
23653a5a1b3Sopenharmony_ci
23753a5a1b3Sopenharmony_ciint main(int argc, char** argv) {
23853a5a1b3Sopenharmony_ci    int failed = 0;
23953a5a1b3Sopenharmony_ci    Suite* s;
24053a5a1b3Sopenharmony_ci    TCase* tc;
24153a5a1b3Sopenharmony_ci    SRunner* sr;
24253a5a1b3Sopenharmony_ci
24353a5a1b3Sopenharmony_ci    s = suite_create("HashMap");
24453a5a1b3Sopenharmony_ci    tc = tcase_create("hashmap");
24553a5a1b3Sopenharmony_ci    tcase_add_test(tc, single_key_test);
24653a5a1b3Sopenharmony_ci    tcase_add_test(tc, remove_all_test);
24753a5a1b3Sopenharmony_ci    tcase_add_test(tc, fill_all_buckets);
24853a5a1b3Sopenharmony_ci    tcase_add_test(tc, iterate_test);
24953a5a1b3Sopenharmony_ci    suite_add_tcase(s, tc);
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    sr = srunner_create(s);
25253a5a1b3Sopenharmony_ci    srunner_run_all(sr, CK_NORMAL);
25353a5a1b3Sopenharmony_ci    failed = srunner_ntests_failed(sr);
25453a5a1b3Sopenharmony_ci    srunner_free(sr);
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci    if (failed > 0) {
25753a5a1b3Sopenharmony_ci        return EXIT_FAILURE;
25853a5a1b3Sopenharmony_ci    }
25953a5a1b3Sopenharmony_ci    return EXIT_SUCCESS;
26053a5a1b3Sopenharmony_ci}
261