[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] Add a basic test suite for pa_hashmap

Tanu Kaskinen gitlab at gitlab.freedesktop.org
Mon Jun 22 14:46:17 UTC 2020



Tanu Kaskinen pushed to branch master at PulseAudio / pulseaudio


Commits:
d97075c7 by Taahir Ahmed at 2020-06-22T14:39:14+00:00
Add a basic test suite for pa_hashmap

I spent a little time working through the implementation of
pa_hashmap, and wrote a test suite while doing so.  It tests a few
basic edge cases, like saturating all buckets of the hashtable.

- - - - -


3 changed files:

- src/Makefile.am
- + src/tests/hashmap-test.c
- src/tests/meson.build


Changes:

=====================================
src/Makefile.am
=====================================
@@ -253,36 +253,37 @@ endif
 noinst_LTLIBRARIES =
 
 TESTS_default = \
-		core-util-test \
-		mainloop-test \
-		strlist-test \
-		close-test \
-		memblockq-test \
-		channelmap-test \
-		thread-mainloop-test \
-		utf8-test \
-		format-test \
-		json-test \
-		get-binary-name-test \
-		hook-list-test \
-		memblock-test \
-		asyncq-test \
-		asyncmsgq-test \
-		queue-test \
-		rtpoll-test \
-		resampler-test \
-		smoother-test \
-		thread-test \
-		volume-test \
-		mix-test \
-		proplist-test \
-		cpu-mix-test \
-		cpu-remap-test \
-		cpu-sconv-test \
-		cpu-volume-test \
-		lock-autospawn-test \
-		mult-s16-test \
-		lfe-filter-test
+        asyncmsgq-test \
+        asyncq-test \
+        channelmap-test \
+        close-test \
+        core-util-test \
+        cpu-mix-test \
+        cpu-remap-test \
+        cpu-sconv-test \
+        cpu-volume-test \
+        format-test \
+        get-binary-name-test \
+        hashmap-test \
+        hook-list-test \
+        json-test \
+        lfe-filter-test \
+        lock-autospawn-test \
+        mainloop-test \
+        memblock-test \
+        memblockq-test \
+        mix-test \
+        mult-s16-test \
+        proplist-test \
+        queue-test \
+        resampler-test \
+        rtpoll-test \
+        smoother-test \
+        strlist-test \
+        thread-mainloop-test \
+        thread-test \
+        utf8-test \
+        volume-test
 
 TESTS_norun = \
 		ipacl-test \
@@ -385,6 +386,11 @@ core_util_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
 core_util_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
 core_util_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
 
+hashmap_test_SOURCES = tests/hashmap-test.c
+hashmap_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
+hashmap_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
+hashmap_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBCHECK_LIBS)
+
 mainloop_test_SOURCES = tests/mainloop-test.c
 mainloop_test_CFLAGS = $(AM_CFLAGS) $(LIBCHECK_CFLAGS)
 mainloop_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon- at PA_MAJORMINOR@.la


=====================================
src/tests/hashmap-test.c
=====================================
@@ -0,0 +1,258 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+#include <pulsecore/hashmap.h>
+
+struct int_entry {
+    int key;
+    int value;
+};
+
+static unsigned int_trivial_hash_func(const void* pa) {
+    int a = *((unsigned*) pa);
+    if (a < 0) {
+        return -a;
+    }
+    return a;
+}
+
+static int int_compare_func(const void* pa, const void* pb) {
+    int a, b;
+    a = *((int*) pa);
+    b = *((int*) pb);
+    if (a < b) {
+        return -1;
+    }
+    if (a == b) {
+        return 0;
+    }
+    return 1;
+}
+
+/* single_key_test exercises basic hashmap functionality on a single key. */
+START_TEST(single_key_test)
+    {
+        pa_hashmap* map;
+        struct int_entry entry;
+        int lookup_key;
+        int put_ret;
+        void* get_ret;
+        unsigned size_ret;
+
+        entry.key = 0;
+        entry.value = 0;
+
+        lookup_key = 0;
+
+        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
+
+        if ((put_ret = pa_hashmap_put(map, &entry.key, &entry)) != 0) {
+            ck_abort_msg("Hashmap rejected k=0/v=0; got %d, want %d", put_ret, 0);
+        }
+
+        if ((size_ret = pa_hashmap_size(map)) != 1) {
+            ck_abort_msg("Hashmap reported wrong size; got %u, want 1", size_ret);
+        }
+
+        if ((get_ret = pa_hashmap_get(map, &lookup_key)) != &entry) {
+            ck_abort_msg("Got wrong value from hashmap for k=0; got %p, want %p", get_ret, &entry);
+        }
+
+        if ((put_ret = pa_hashmap_put(map, &entry.key, &entry)) == 0) {
+            ck_abort_msg("Hashmap allowed duplicate key for k=0; got %d, want non-zero", put_ret);
+        }
+
+        if ((size_ret = pa_hashmap_size(map)) != 1) {
+            ck_abort_msg("Hashmap reported wrong size; got %u, want 1", size_ret);
+        }
+
+        if ((get_ret = pa_hashmap_remove(map, &lookup_key)) != &entry) {
+            ck_abort_msg("Hashmap returned wrong value during free; got %p, want %p", get_ret, &entry);
+        }
+
+        if ((size_ret = pa_hashmap_size(map)) != 0) {
+            ck_abort_msg("Hashmap reported wrong size; got %u, want 1", size_ret);
+        }
+
+        pa_hashmap_free(map);
+    }
+END_TEST
+
+/* remove_all_test checks that pa_hashmap_remove_all really removes all entries
+ * from the map.*/
+START_TEST(remove_all_test)
+    {
+        pa_hashmap* map;
+        struct int_entry entries[1000];
+        unsigned size;
+
+        for (int i = 0; i < 1000; i++) {
+            entries[i].key = i;
+            entries[i].value = i;
+        }
+
+        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
+
+        for (int i = 0; i < 1000; i++) {
+            pa_hashmap_put(map, &entries[i].key, &entries[i]);
+        }
+
+        if ((size = pa_hashmap_size(map)) != 1000) {
+            ck_abort_msg("Hashmap has wrong size; got %u, want 1000", size);
+        }
+
+        pa_hashmap_remove_all(map);
+
+        if ((size = pa_hashmap_size(map)) != 0) {
+            ck_abort_msg("Hashmap has wrong size; got %u, want 0", size);
+        }
+    }
+END_TEST
+
+/* fill_all_buckets hits the hashmap with enough keys to exercise the bucket
+ * linked list for every bucket. */
+START_TEST(fill_all_buckets)
+    {
+        pa_hashmap* map;
+        struct int_entry entries[1000];
+        int lookup_keys[1000]; /* Don't share addresses with insertion keys */
+
+        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
+
+        for (int i = 0; i < 1000; i++) {
+            entries[i].key = i;
+            lookup_keys[i] = i;
+            entries[i].value = i;
+        }
+
+        for (int i = 0; i < 1000; i++) {
+            int put_ret;
+            unsigned size_ret;
+
+            if ((put_ret = pa_hashmap_put(map, &entries[i].key, &entries[i])) != 0) {
+                ck_abort_msg("Unexpected failure putting k=%d v=%d into the map", entries[i].key, entries[i].value);
+            }
+
+            if ((size_ret = pa_hashmap_size(map)) != i + 1) {
+                ck_abort_msg("Hashmap reported wrong size; got %u, want %d", size_ret, i);
+            }
+        }
+
+        for (int i = 0; i < 1000; i++) {
+            unsigned size_ret;
+            int* k;
+            struct int_entry* v;
+
+            k = lookup_keys + i;
+
+            v = (struct int_entry*) pa_hashmap_remove(map, k);
+            if (v == NULL) {
+                ck_abort_msg("Hashmap returned NULL for k=%d; wanted nonnull", *k);
+            }
+            if ((*v).value != i) {
+                ck_abort_msg("Hashmap returned wrong value for k=%d; got %d, want %d", *k, (*v).value, i);
+            }
+
+            if ((size_ret = pa_hashmap_size(map)) != 1000 - i - 1) {
+                ck_abort_msg("Hashmap reported wrong size; got %u, want %d", size_ret, 1000 - i);
+            }
+        }
+
+        pa_hashmap_free(map);
+    }
+END_TEST
+
+/* iterate_test exercises the iteration list maintained by the hashtable. */
+START_TEST(iterate_test)
+    {
+        pa_hashmap* map;
+        struct int_entry entries[1000];
+        void* state;
+        struct int_entry* v;
+        int expected;
+
+        for (int i = 0; i < 1000; i++) {
+            entries[i].key = i;
+            entries[i].value = i;
+        }
+
+        map = pa_hashmap_new(int_trivial_hash_func, int_compare_func);
+
+        for (int i = 0; i < 1000; i++) {
+            if (pa_hashmap_put(map, &(entries[i].key), &(entries[i])) != 0) {
+                ck_abort_msg("Unexpected failure putting k=%d v=%d into the map", entries[i].key, entries[i].value);
+            }
+        }
+
+        expected = 0;
+        PA_HASHMAP_FOREACH (v, map, state) {
+            if ((*v).value != expected) {
+                ck_abort_msg("Got bad order iterating over hashmap: got %d, want %d", v->value, expected);
+            }
+            expected++;
+        }
+
+        expected = 999;
+        PA_HASHMAP_FOREACH_BACKWARDS (v, map, state) {
+            if ((*v).value != expected) {
+                ck_abort_msg("Got bad order iterating over hashmap: got %d, want %d", v->value, expected);
+            }
+            expected--;
+        }
+
+        /* Now empty out the hashmap.  The iteration list should be empty. */
+        for(int i = 0; i < 1000; i++) {
+          pa_hashmap_remove(map, &(entries[i].key));
+        }
+
+        PA_HASHMAP_FOREACH(v, map, state) {
+          ck_abort_msg("Iteration over empty map returned entries");
+        }
+
+        /* Now add one element back. The iteration list should only contain this
+         * one element, even though the entry nodes are reused. */
+        if(pa_hashmap_put(map, &(entries[0].key), &(entries[0])) != 0) {
+          ck_abort_msg("Unexpected failure putting k=%d v=%d into the map", entries[0].key, entries[0].value);
+        }
+
+        expected = 0;
+        PA_HASHMAP_FOREACH(v, map, state) {
+           if ((*v).value != expected) {
+                ck_abort_msg("Got bad order iterating over hashmap: got %d, want %d", v->value, expected);
+            }
+           expected++;
+        }
+        if (expected != 1) {
+          ck_abort_msg("Got too many elements while iterating: got %d, want 1", expected);
+        }
+
+        pa_hashmap_free(map);
+    }
+END_TEST
+
+int main(int argc, char** argv) {
+    int failed = 0;
+    Suite* s;
+    TCase* tc;
+    SRunner* sr;
+
+    s = suite_create("HashMap");
+    tc = tcase_create("hashmap");
+    tcase_add_test(tc, single_key_test);
+    tcase_add_test(tc, remove_all_test);
+    tcase_add_test(tc, fill_all_buckets);
+    tcase_add_test(tc, iterate_test);
+    suite_add_tcase(s, tc);
+
+    sr = srunner_create(s);
+    srunner_run_all(sr, CK_NORMAL);
+    failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+
+    if (failed > 0) {
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}


=====================================
src/tests/meson.build
=====================================
@@ -30,6 +30,8 @@ default_tests = [
     [ check_dep, libpulse_dep, libpulsecommon_dep, libpulsecore_dep ] ],
   [ 'get-binary-name-test', 'get-binary-name-test.c',
     [ check_dep, libpulse_dep, libpulsecommon_dep ] ],
+  [ 'hashmap-test', 'hashmap-test.c',
+    [ check_dep, libpulse_dep, libpulsecommon_dep ] ],
   [ 'hook-list-test', 'hook-list-test.c',
     [ check_dep, libpulse_dep, libpulsecommon_dep, libpulsecore_dep ] ],
   [ 'json-test', 'json-test.c',



View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/commit/d97075c79f566e808c17f787a4a24240330ebe82

-- 
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/commit/d97075c79f566e808c17f787a4a24240330ebe82
You're receiving this email because of your account on gitlab.freedesktop.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20200622/ffd5f711/attachment-0001.htm>


More information about the pulseaudio-commits mailing list