tests/test_hash_map.c

Sat, 30 Dec 2023 18:48:25 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 30 Dec 2023 18:48:25 +0100
changeset 785
bb18daa62d5f
permissions
-rw-r--r--

migrate map tests - relates to #342

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  */
    29 #include "cx/test.h"
    30 #include "util_allocator.h"
    32 #include "cx/hash_map.h"
    34 CX_TEST(test_hash_map_create) {
    35     CxTestingAllocator talloc;
    36     cx_testing_allocator_init(&talloc);
    37     CxAllocator *allocator = &talloc.base;
    38     CX_TEST_DO {
    39         CxMap *map = cxHashMapCreate(allocator, 1, 0);
    40         struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map;
    41         CX_TEST_ASSERT(hmap->bucket_count > 0);
    42         for(size_t i = 0 ; i < hmap->bucket_count ; i++) {
    43             CX_TEST_ASSERT(hmap->buckets[i] == NULL);
    44         }
    45         CX_TEST_ASSERT(map->item_size == 1);
    46         CX_TEST_ASSERT(map->size == 0);
    47         CX_TEST_ASSERT(map->allocator == allocator);
    48         CX_TEST_ASSERT(!map->store_pointer);
    49         CX_TEST_ASSERT(map->cmpfunc == NULL);
    50         CX_TEST_ASSERT(map->simple_destructor == NULL);
    51         CX_TEST_ASSERT(map->advanced_destructor == NULL);
    52         CX_TEST_ASSERT(map->destructor_data == NULL);
    53         cxMapStorePointers(map);
    54         CX_TEST_ASSERT(map->store_pointer);
    55         CX_TEST_ASSERT(map->item_size == sizeof(void *));
    56         cxMapStoreObjects(map);
    57         CX_TEST_ASSERT(!map->store_pointer);
    59         cxMapDestroy(map);
    60         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
    61     }
    62     cx_testing_allocator_destroy(&talloc);
    63 }
    65 CX_TEST(test_hash_map_create_store_pointers) {
    66     CxTestingAllocator talloc;
    67     cx_testing_allocator_init(&talloc);
    68     CxAllocator *allocator = &talloc.base;
    69     CX_TEST_DO {
    70         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
    71         struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map;
    72         CX_TEST_ASSERT(hmap->bucket_count > 0);
    73         for (size_t i = 0; i < hmap->bucket_count; i++) {
    74             CX_TEST_ASSERT(hmap->buckets[i] == NULL);
    75         }
    76         CX_TEST_ASSERT(map->size == 0);
    77         CX_TEST_ASSERT(map->allocator == allocator);
    78         CX_TEST_ASSERT(map->store_pointer);
    79         CX_TEST_ASSERT(map->item_size == sizeof(void *));
    81         cxMapDestroy(map);
    82         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
    83     }
    84     cx_testing_allocator_destroy(&talloc);
    85 }
    87 CX_TEST(test_hash_map_rehash) {
    88     CxTestingAllocator talloc;
    89     cx_testing_allocator_init(&talloc);
    90     CxAllocator *allocator = &talloc.base;
    91     CX_TEST_DO {
    92         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 7);
    94         cxMapPut(map, "key 1", (void *) "val 1");
    95         cxMapPut(map, "key 2", (void *) "val 2");
    96         cxMapPut(map, "key 3", (void *) "val 3");
    97         cxMapPut(map, "foo 4", (void *) "val 4");
    98         cxMapPut(map, "key 5", (void *) "val 5");
    99         cxMapPut(map, "key 6", (void *) "val 6");
   100         cxMapPut(map, "bar 7", (void *) "val 7");
   101         cxMapPut(map, "key 8", (void *) "val 8");
   102         cxMapPut(map, "key 9", (void *) "val 9");
   103         cxMapPut(map, "key 10", (void *) "val 10");
   105         CX_TEST_ASSERT(((struct cx_hash_map_s *)map)->bucket_count == 7);
   106         int result = cxMapRehash(map);
   107         CX_TEST_ASSERT(result == 0);
   108         CX_TEST_ASSERT(((struct cx_hash_map_s *)map)->bucket_count == 25);
   109         CX_TEST_ASSERT(map->size == 10);
   111         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 1"), "val 1"));
   112         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 2"), "val 2"));
   113         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 3"), "val 3"));
   114         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo 4"), "val 4"));
   115         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 5"), "val 5"));
   116         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 6"), "val 6"));
   117         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "bar 7"), "val 7"));
   118         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 8"), "val 8"));
   119         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 9"), "val 9"));
   120         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key 10"), "val 10"));
   122         cxMapDestroy(map);
   123         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   124     }
   125     cx_testing_allocator_destroy(&talloc);
   126 }
   128 CX_TEST(test_hash_map_rehash_not_required) {
   129     CxTestingAllocator talloc;
   130     cx_testing_allocator_init(&talloc);
   131     CxAllocator *allocator = &talloc.base;
   132     CX_TEST_DO {
   133         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 8);
   135         cxMapPut(map, "key 1", (void *) "val 1");
   136         cxMapPut(map, "key 2", (void *) "val 2");
   137         cxMapPut(map, "key 3", (void *) "val 3");
   138         cxMapPut(map, "key 4", (void *) "val 4");
   139         cxMapPut(map, "key 5", (void *) "val 5");
   140         cxMapPut(map, "key 6", (void *) "val 6");
   142         // 6/8 does not exceed 0.75, therefore the function should not rehash
   143         int result = cxMapRehash(map);
   144         CX_TEST_ASSERT(result == 0);
   145         CX_TEST_ASSERT(((struct cx_hash_map_s *)map)->bucket_count == 8);
   147         cxMapDestroy(map);
   148         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   149     }
   150     cx_testing_allocator_destroy(&talloc);
   151 }
   153 CX_TEST(test_hash_map_clear) {
   154     CxTestingAllocator talloc;
   155     cx_testing_allocator_init(&talloc);
   156     CxAllocator *allocator = &talloc.base;
   157     CX_TEST_DO {
   158         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
   160         cxMapPut(map, "key 1", (void *) "val 1");
   161         cxMapPut(map, "key 2", (void *) "val 2");
   162         cxMapPut(map, "key 3", (void *) "val 3");
   164         CX_TEST_ASSERT(map->size == 3);
   166         cxMapClear(map);
   168         CX_TEST_ASSERT(map->size == 0);
   169         CX_TEST_ASSERT(cxMapGet(map, "key 1") == NULL);
   170         CX_TEST_ASSERT(cxMapGet(map, "key 2") == NULL);
   171         CX_TEST_ASSERT(cxMapGet(map, "key 3") == NULL);
   173         cxMapDestroy(map);
   174         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   175     }
   176     cx_testing_allocator_destroy(&talloc);
   177 }
   179 CX_TEST(test_hash_map_store_ucx_strings) {
   180     CxTestingAllocator talloc;
   181     cx_testing_allocator_init(&talloc);
   182     CxAllocator *allocator = &talloc.base;
   183     CX_TEST_DO {
   184         // create the map
   185         CxMap *map = cxHashMapCreate(allocator, sizeof(cxstring), 8);
   187         // define some strings
   188         cxstring s1 = CX_STR("this");
   189         cxstring s2 = CX_STR("is");
   190         cxstring s3 = CX_STR("a");
   191         cxstring s4 = CX_STR("test");
   192         cxstring s5 = CX_STR("setup");
   194         // put them into the map
   195         cxMapPut(map, "s1", &s1);
   196         cxMapPut(map, "s2", &s2);
   197         cxMapPut(map, "s3", &s3);
   198         cxMapPut(map, "s4", &s4);
   200         // overwrite a value
   201         cxMapPut(map, "s1", &s5);
   203         // look up a string
   204         cxstring * s3p = cxMapGet(map, "s3");
   205         CX_TEST_ASSERT(s3p->length == s3.length);
   206         CX_TEST_ASSERT(s3p->ptr == s3.ptr);
   207         CX_TEST_ASSERT(s3p !=  &s3);
   209         // remove a string
   210         cxMapRemove(map, "s2");
   211         CX_TEST_ASSERT(map->size == 3);
   213         // iterate
   214         bool s3found = false, s4found = false, s5found = false;
   215         CxIterator iter = cxMapIteratorValues(map);
   216         cx_foreach(cxstring*, s, iter) {
   217             s3found |= s3.ptr == s->ptr;
   218             s4found |= s4.ptr == s->ptr;
   219             s5found |= s5.ptr == s->ptr;
   220         }
   221         CX_TEST_ASSERT(s3found && s4found && s5found);
   223         cxMapDestroy(map);
   224         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   225     }
   226     cx_testing_allocator_destroy(&talloc);
   227 }
   229 CX_TEST(test_hash_map_remove_via_iterator) {
   230     CxTestingAllocator talloc;
   231     cx_testing_allocator_init(&talloc);
   232     CxAllocator *allocator = &talloc.base;
   233     CX_TEST_DO {
   234         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 4);
   236         cxMapPut(map, "key 1", (void *) "val 1");
   237         cxMapPut(map, "key 2", (void *) "val 2");
   238         cxMapPut(map, "key 3", (void *) "val 3");
   239         cxMapPut(map, "key 4", (void *) "val 4");
   240         cxMapPut(map, "key 5", (void *) "val 5");
   241         cxMapPut(map, "key 6", (void *) "val 6");
   243         CxMutIterator iter = cxMapMutIterator(map);
   244         cx_foreach(CxMapEntry*, entry, iter) {
   245             if (((char const *)entry->key->data)[4] % 2 == 1) cxIteratorFlagRemoval(iter);
   246         }
   247         CX_TEST_ASSERT(map->size == 3);
   248         CX_TEST_ASSERT(iter.index == map->size);
   250         CX_TEST_ASSERT(cxMapGet(map, "key 1") == NULL);
   251         CX_TEST_ASSERT(cxMapGet(map, "key 2") != NULL);
   252         CX_TEST_ASSERT(cxMapGet(map, "key 3") == NULL);
   253         CX_TEST_ASSERT(cxMapGet(map, "key 4") != NULL);
   254         CX_TEST_ASSERT(cxMapGet(map, "key 5") == NULL);
   255         CX_TEST_ASSERT(cxMapGet(map, "key 6") != NULL);
   257         cxMapDestroy(map);
   258         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   259     }
   260     cx_testing_allocator_destroy(&talloc);
   261 }
   263 static void test_simple_destructor(void *data) {
   264     strcpy(data, "OK");
   265 }
   267 static void test_advanced_destructor(
   268         __attribute__((__unused__)) void *unused,
   269         void *data
   270 ) {
   271     strcpy(data, "OK");
   272 }
   274 static CX_TEST_SUBROUTINE(verify_any_destructor, CxMap *map) {
   275     CxHashKey k1 = cx_hash_key_str("key 1");
   276     CxHashKey k2 = cx_hash_key_str("key 2");
   277     CxHashKey k3 = cx_hash_key_str("key 3");
   278     CxHashKey k4 = cx_hash_key_str("key 4");
   279     CxHashKey k5 = cx_hash_key_str("key 5");
   281     char v1[] = "val 1";
   282     char v2[] = "val 2";
   283     char v3[] = "val 3";
   284     char v4[] = "val 4";
   285     char v5[] = "val 5";
   287     cxMapPut(map, k1, v1);
   288     cxMapPut(map, k2, v2);
   289     cxMapPut(map, k3, v3);
   290     cxMapPut(map, k4, v4);
   292     cxMapRemove(map, k2);
   293     char *r = cxMapRemoveAndGet(map, k3);
   294     cxMapDetach(map, k1);
   296     CX_TEST_ASSERT(0 == strcmp(v1, "val 1"));
   297     CX_TEST_ASSERT(0 == strcmp(v2, "OK"));
   298     CX_TEST_ASSERT(0 == strcmp(v3, "val 3"));
   299     CX_TEST_ASSERT(0 == strcmp(v4, "val 4"));
   300     CX_TEST_ASSERT(0 == strcmp(v5, "val 5"));
   301     CX_TEST_ASSERT(r == v3);
   303     cxMapClear(map);
   305     CX_TEST_ASSERT(0 == strcmp(v1, "val 1"));
   306     CX_TEST_ASSERT(0 == strcmp(v2, "OK"));
   307     CX_TEST_ASSERT(0 == strcmp(v3, "val 3"));
   308     CX_TEST_ASSERT(0 == strcmp(v4, "OK"));
   309     CX_TEST_ASSERT(0 == strcmp(v5, "val 5"));
   311     cxMapPut(map, k1, (void *) v1);
   312     cxMapPut(map, k3, (void *) v3);
   313     cxMapPut(map, k5, (void *) v5);
   315     {
   316         CxMutIterator iter = cxMapMutIteratorKeys(map);
   317         cx_foreach(CxHashKey*, key, iter) {
   318             if (((char*)key->data)[4] == '1') cxIteratorFlagRemoval(iter);
   319         }
   320     }
   321     {
   322         CxMutIterator iter = cxMapMutIteratorValues(map);
   323         cx_foreach(char*, v, iter) {
   324             if (v[4] == '5') cxIteratorFlagRemoval(iter);
   325         }
   326     }
   328     CX_TEST_ASSERT(0 == strcmp(v1, "OK"));
   329     CX_TEST_ASSERT(0 == strcmp(v2, "OK"));
   330     CX_TEST_ASSERT(0 == strcmp(v3, "val 3"));
   331     CX_TEST_ASSERT(0 == strcmp(v4, "OK"));
   332     CX_TEST_ASSERT(0 == strcmp(v5, "OK"));
   334     v1[0] = v2[0] = v4[0] = v5[0] = 'c';
   336     cxMapDestroy(map);
   338     CX_TEST_ASSERT(0 == strcmp(v1, "cK"));
   339     CX_TEST_ASSERT(0 == strcmp(v2, "cK"));
   340     CX_TEST_ASSERT(0 == strcmp(v3, "OK"));
   341     CX_TEST_ASSERT(0 == strcmp(v4, "cK"));
   342     CX_TEST_ASSERT(0 == strcmp(v5, "cK"));
   343 }
   345 CX_TEST(test_hash_map_simple_destructor) {
   346     CxTestingAllocator talloc;
   347     cx_testing_allocator_init(&talloc);
   348     CxAllocator *allocator = &talloc.base;
   349     CX_TEST_DO {
   350         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
   351         map->simple_destructor = test_simple_destructor;
   352         CX_TEST_CALL_SUBROUTINE(verify_any_destructor, map);
   353         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   354     }
   355     cx_testing_allocator_destroy(&talloc);
   356 }
   358 CX_TEST(test_hash_map_advanced_destructor) {
   359     CxTestingAllocator talloc;
   360     cx_testing_allocator_init(&talloc);
   361     CxAllocator *allocator = &talloc.base;
   362     CX_TEST_DO {
   363         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
   364         map->advanced_destructor = test_advanced_destructor;
   365         CX_TEST_CALL_SUBROUTINE(verify_any_destructor, map);
   366         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   367     }
   368     cx_testing_allocator_destroy(&talloc);
   369 }
   371 CX_TEST(test_empty_map_size) {
   372     CX_TEST_DO {
   373         CX_TEST_ASSERT(cxEmptyMap->size == 0);
   374     }
   375 }
   377 CX_TEST(test_empty_map_iterator) {
   378     CxMap *map = cxEmptyMap;
   380     CxIterator it1 = cxMapIterator(map);
   381     CxIterator it2 = cxMapIteratorValues(map);
   382     CxIterator it3 = cxMapIteratorKeys(map);
   383     CxMutIterator it4 = cxMapMutIterator(map);
   384     CxMutIterator it5 = cxMapMutIteratorValues(map);
   385     CxMutIterator it6 = cxMapMutIteratorKeys(map);
   387     CX_TEST_DO {
   388         CX_TEST_ASSERT(!cxIteratorValid(it1));
   389         CX_TEST_ASSERT(!cxIteratorValid(it2));
   390         CX_TEST_ASSERT(!cxIteratorValid(it3));
   391         CX_TEST_ASSERT(!cxIteratorValid(it4));
   392         CX_TEST_ASSERT(!cxIteratorValid(it5));
   393         CX_TEST_ASSERT(!cxIteratorValid(it6));
   395         int c = 0;
   396         cx_foreach(void*, data, it1) c++;
   397         cx_foreach(void*, data, it2) c++;
   398         cx_foreach(void*, data, it3) c++;
   399         cx_foreach(void*, data, it4) c++;
   400         cx_foreach(void*, data, it5) c++;
   401         cx_foreach(void*, data, it6) c++;
   402         CX_TEST_ASSERT(c == 0);
   403     }
   404 }
   406 CX_TEST(test_empty_map_no_ops) {
   407     CX_TEST_DO {
   408         // assertion not possible
   409         // test that no segfault happens and valgrind is happy
   410         cxMapClear(cxEmptyMap);
   411         cxMapDestroy(cxEmptyMap);
   412         CX_TEST_ASSERT(true);
   413     }
   414 }
   416 CX_TEST(test_empty_map_get) {
   417     CX_TEST_DO {
   418         CxHashKey key = cx_hash_key_str("test");
   419         CX_TEST_ASSERT(cxMapGet(cxEmptyMap, key) == NULL);
   420     }
   421 }
   423 CX_TEST(test_hash_map_generics) {
   424     CxTestingAllocator talloc;
   425     cx_testing_allocator_init(&talloc);
   426     CxAllocator *allocator = &talloc.base;
   427     CX_TEST_DO {
   428         CxMap *map = cxHashMapCreate(allocator, sizeof(cxstring), 0);
   429         cxMapPut(map, "test", "test");
   430         cxMapPut(map, cx_mutstr("foo"), "bar");
   431         cxMapPut(map, cx_str("hallo"), "welt");
   433         CX_TEST_ASSERT(map->size == 3);
   434         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "test"), "test"));
   435         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar"));
   436         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "hallo"), "welt"));
   438         // note: we don't have a destructor here, so remove and detach are the same
   439         cxMapRemove(map, cx_str("test"));
   440         char const *hallo = "hallo";
   441         cxMapDetach(map, hallo);
   442         cxMapPut(map, cx_hash_key_str("key"), "value");
   444         CX_TEST_ASSERT(map->size == 2);
   445         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "key"), "value"));
   446         CX_TEST_ASSERT(0 == strcmp(cxMapGet(map, "foo"), "bar"));
   448         void *r;
   449         r = cxMapRemoveAndGet(map, "key");
   450         r = cxMapRemoveAndGet(map, cx_str("foo"));
   451         if (r != NULL) map->size = 47;
   453         CX_TEST_ASSERT(map->size == 0);
   455         cxMapDestroy(map);
   456         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   457     }
   458     cx_testing_allocator_destroy(&talloc);
   459 }
   461 struct test_map_kv {
   462     char const *key;
   463     char const *value;
   464 };
   466 static struct test_map_kv const test_map_operations[] = {
   467         {"key 1",          "test"},
   468         {"key 2",          "blub"},
   469         {"key 3",          "hallo"},
   470         {"key 2",          "foobar"},
   471         {"key 4",          "value 4"},
   472         {"key 5",          "value 5"},
   473         {"key 6",          "value 6"},
   474         {"key 4",          NULL},
   475         {"key 7",          "value 7"},
   476         {"key 8",          "value 8"},
   477         {"does not exist", NULL},
   478         {"key 9",          "value 9"},
   479         {"key 6",          "other value"},
   480         {"key 7",          "something else"},
   481         {"key 8",          NULL},
   482         {"key 2",          NULL},
   483         {"key 8",          "new value"},
   484 };
   485 static size_t const test_map_operations_len =
   486         sizeof(test_map_operations) / sizeof(struct test_map_kv);
   487 static struct test_map_kv test_map_reference[] = {
   488         {"key 1", NULL},
   489         {"key 2", NULL},
   490         {"key 3", NULL},
   491         {"key 4", NULL},
   492         {"key 5", NULL},
   493         {"key 6", NULL},
   494         {"key 7", NULL},
   495         {"key 8", NULL},
   496         {"key 9", NULL},
   497 };
   498 static size_t const test_map_reference_len =
   499         sizeof(test_map_reference) / sizeof(struct test_map_kv);
   501 static void test_map_reference_put(char const *key, char const* value) {
   502     for (size_t i = 0 ; i < test_map_reference_len ; i++) {
   503         if (0 == strcmp(key, test_map_reference[i].key)) {
   504             test_map_reference[i].value = value;
   505             return;
   506         }
   507     }
   508 }
   510 static char const *test_map_reference_get(char const *key) {
   511     for (size_t i = 0 ; i < test_map_reference_len ; i++) {
   512         if (0 == strcmp(key, test_map_reference[i].key)) {
   513             return test_map_reference[i].value;
   514         }
   515     }
   516     return NULL;
   517 }
   519 static char const *test_map_reference_remove(char const *key) {
   520     for (size_t i = 0 ; i < test_map_reference_len ; i++) {
   521         if (0 == strcmp(key, test_map_reference[i].key)) {
   522             char const *ret = test_map_reference[i].value;
   523             test_map_reference[i].value = NULL;
   524             return ret;
   525         }
   526     }
   527     return NULL;
   528 }
   530 static size_t test_map_reference_size(void) {
   531     size_t size = 0;
   532     for (size_t i = 0; i < test_map_reference_len; i++) {
   533         if (test_map_reference[i].value != NULL) {
   534             size++;
   535         }
   536     }
   537     return size;
   538 }
   540 static CX_TEST_SUBROUTINE(verify_map_contents, CxMap *map) {
   541     // verify that the reference map has same size (i.e. no other keys are mapped)
   542     CX_TEST_ASSERT(map->size == test_map_reference_size());
   544     // verify key iterator
   545     {
   546         // collect the keys from the map iterator
   547         CxIterator keyiter = cxMapIteratorKeys(map);
   548         CxHashKey *keys = calloc(map->size, sizeof(CxHashKey));
   549         cx_foreach(CxHashKey*, elem, keyiter) {
   550             keys[keyiter.index] = *elem;
   551         }
   552         CX_TEST_ASSERT(keyiter.index == map->size);
   553         // verify that all keys are mapped to values in reference map
   554         for (size_t i = 0 ; i < map->size ; i++) {
   555             cxmutstr ksz = cx_strdup(cx_strn(keys[i].data, keys[i].len));
   556             CX_TEST_ASSERT(test_map_reference_get(ksz.ptr) != NULL);
   557             cx_strfree(&ksz);
   558         }
   559         free(keys);
   560     }
   562     // verify value iterator
   563     {
   564         // by using that the values in our test data are unique strings
   565         // we can re-use a similar approach as above
   566         CxIterator valiter = cxMapIteratorValues(map);
   567         char const** values = calloc(map->size, sizeof(char const*));
   568         cx_foreach(char const*, elem, valiter) {
   569             values[valiter.index] = elem;
   570         }
   571         CX_TEST_ASSERT(valiter.index == map->size);
   572         // verify that all values are present in the reference map
   573         for (size_t i = 0 ; i < map->size ; i++) {
   574             bool found = false;
   575             for (size_t j = 0; j < test_map_reference_len ; j++) {
   576                 if (test_map_reference[j].value == values[i]) {
   577                     found = true;
   578                     break;
   579                 }
   580             }
   581             CX_TEST_ASSERTM(found, "A value was not found in the reference map");
   582         }
   583         free(values);
   584     }
   586     // verify pair iterator
   587     {
   588         CxIterator pairiter = cxMapIterator(map);
   589         struct test_map_kv *pairs = calloc(map->size, sizeof(struct test_map_kv));
   590         cx_foreach(CxMapEntry*, entry, pairiter) {
   591             CxHashKey const *key = entry->key;
   592             pairs[pairiter.index].key = cx_strdup(cx_strn(key->data, key->len)).ptr;
   593             pairs[pairiter.index].value = entry->value;
   594         }
   595         CX_TEST_ASSERT(pairiter.index == map->size);
   596         // verify that all pairs are present in the reference map
   597         for (size_t i = 0 ; i < map->size ; i++) {
   598             CX_TEST_ASSERT(test_map_reference_get(pairs[i].key) == pairs[i].value);
   599             // this was strdup'ed
   600             free((void*)pairs[i].key);
   601         }
   602         free(pairs);
   603     }
   604 }
   606 CX_TEST(test_hash_map_basic_operations) {
   607     CxTestingAllocator talloc;
   608     cx_testing_allocator_init(&talloc);
   609     CxAllocator *allocator = &talloc.base;
   610     CX_TEST_DO {
   611         // create the map
   612         CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 8);
   614         // clear the reference map
   615         for (size_t i = 0 ; i < test_map_reference_len ; i++) {
   616             test_map_reference[i].value = NULL;
   617         }
   619         // verify iterators for empty map
   620         CX_TEST_CALL_SUBROUTINE(verify_map_contents, map);
   622         // execute operations and verify results
   623         for (size_t i = 0 ; i < test_map_operations_len ; i++) {
   624             struct test_map_kv kv = test_map_operations[i];
   625             CxHashKey key = cx_hash_key_str(kv.key);
   626             key.hash = 0; // force the hash map to compute the hash
   627             if (kv.value != NULL) {
   628                 // execute a put operation and verify that the exact value can be read back
   629                 test_map_reference_put(kv.key, kv.value);
   630                 int result = cxMapPut(map, key, (void *) kv.value);
   631                 CX_TEST_ASSERT(result == 0);
   632                 void *added = cxMapGet(map, key);
   633                 CX_TEST_ASSERT(0 == memcmp(kv.value, added, strlen(kv.value)));
   634             } else {
   635                 // execute a remove and verify that the removed element was returned (or NULL)
   636                 char const *found = test_map_reference_remove(kv.key);
   637                 void *removed = cxMapRemoveAndGet(map, key);
   638                 CX_TEST_ASSERT(found == removed);
   639             }
   640             // compare the current map state with the reference map
   641             CX_TEST_CALL_SUBROUTINE(verify_map_contents, map);
   642         }
   644         // destroy the map and verify the memory (de)allocations
   645         cxMapDestroy(map);
   646         CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
   647     }
   648     cx_testing_allocator_destroy(&talloc);
   649 }
   651 CxTestSuite *cx_test_suite_hash_map(void) {
   652     CxTestSuite *suite = cx_test_suite_new("map");
   654     cx_test_register(suite, test_hash_map_create);
   655     cx_test_register(suite, test_hash_map_create_store_pointers);
   656     cx_test_register(suite, test_hash_map_basic_operations);
   657     cx_test_register(suite, test_hash_map_rehash);
   658     cx_test_register(suite, test_hash_map_rehash_not_required);
   659     cx_test_register(suite, test_hash_map_clear);
   660     cx_test_register(suite, test_hash_map_store_ucx_strings);
   661     cx_test_register(suite, test_hash_map_remove_via_iterator);
   662     cx_test_register(suite, test_empty_map_no_ops);
   663     cx_test_register(suite, test_empty_map_size);
   664     cx_test_register(suite, test_empty_map_get);
   665     cx_test_register(suite, test_empty_map_iterator);
   666     cx_test_register(suite, test_hash_map_generics);
   668     return suite;
   669 }

mercurial