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