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

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 }

mercurial