test/test_map.cpp

Mon, 08 Aug 2022 17:12:00 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 08 Aug 2022 17:12:00 +0200
changeset 572
f0f99dd06d9f
parent 563
69a83fad8a35
child 594
d90cfa6721f9
permissions
-rw-r--r--

#201 - remove dangerous allocator config

There is no plausible use case, except using the testing
allocator in the test case, and having the possibility to
specify any allocator (including another mempool) causes
more harm than good.

universe@556 1 /*
universe@556 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@556 3 *
universe@556 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
universe@556 5 *
universe@556 6 * Redistribution and use in source and binary forms, with or without
universe@556 7 * modification, are permitted provided that the following conditions are met:
universe@556 8 *
universe@556 9 * 1. Redistributions of source code must retain the above copyright
universe@556 10 * notice, this list of conditions and the following disclaimer.
universe@556 11 *
universe@556 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@556 13 * notice, this list of conditions and the following disclaimer in the
universe@556 14 * documentation and/or other materials provided with the distribution.
universe@556 15 *
universe@556 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@556 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@556 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@556 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@556 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@556 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@556 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@556 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@556 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@556 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@556 26 * POSSIBILITY OF SUCH DAMAGE.
universe@556 27 */
universe@556 28
universe@556 29 #include "cx/hash_map.h"
universe@556 30 #include "cx/utils.h"
universe@556 31 #include "util_allocator.h"
universe@556 32
universe@556 33 #include <gtest/gtest.h>
universe@556 34 #include <unordered_map>
universe@556 35 #include <unordered_set>
universe@556 36
universe@556 37 struct map_operation {
universe@556 38 enum {
universe@556 39 put, rm
universe@556 40 } op;
universe@556 41 char const *key;
universe@556 42 char const *value;
universe@556 43 };
universe@556 44
universe@556 45 auto generate_map_operations() -> std::vector<map_operation> {
universe@556 46 return {
universe@556 47 {map_operation::put, "key 1", "test"},
universe@556 48 {map_operation::put, "key 2", "blub"},
universe@556 49 {map_operation::put, "key 3", "hallo"},
universe@556 50 {map_operation::put, "key 2", "foobar"},
universe@556 51 {map_operation::put, "key 4", "value 4"},
universe@556 52 {map_operation::put, "key 5", "value 5"},
universe@556 53 {map_operation::put, "key 6", "value 6"},
universe@556 54 {map_operation::rm, "key 4", nullptr},
universe@556 55 {map_operation::put, "key 7", "value 7"},
universe@556 56 {map_operation::put, "key 8", "value 8"},
universe@556 57 {map_operation::rm, "does not exist", nullptr},
universe@556 58 {map_operation::put, "key 9", "value 9"},
universe@556 59 {map_operation::put, "key 6", "other value"},
universe@556 60 {map_operation::put, "key 7", "something else"},
universe@556 61 {map_operation::rm, "key 8", nullptr},
universe@556 62 {map_operation::rm, "key 2", nullptr},
universe@556 63 {map_operation::put, "key 8", "new value"},
universe@556 64 };
universe@556 65 }
universe@556 66
universe@556 67 static void verify_map_contents(
universe@556 68 CxMap *map,
universe@556 69 std::unordered_map<std::string, std::string> const &refmap
universe@556 70 ) {
universe@556 71 // verify key iterator
universe@556 72 {
universe@556 73 auto keyiter = cxMapIteratorKeys(map);
universe@556 74 std::unordered_set<std::string> keys;
universe@563 75 cx_foreach(CxHashKey*, elem, keyiter) {
universe@556 76 // we use that our test keys contain NULL-terminated strings
universe@563 77 keys.insert(std::string(elem->data.cstr));
universe@556 78 }
universe@561 79 EXPECT_EQ(keyiter.index, map->size);
universe@556 80 ASSERT_EQ(keys.size(), map->size);
universe@556 81 for (auto &&k: keys) {
universe@556 82 EXPECT_NE(refmap.find(k), refmap.end());
universe@556 83 }
universe@556 84 }
universe@556 85
universe@556 86 // verify value iterator
universe@556 87 {
universe@556 88 auto valiter = cxMapIteratorValues(map);
universe@556 89 std::unordered_set<std::string> values; // we use that the values in our test data are unique strings
universe@556 90 cx_foreach(char const*, elem, valiter) {
universe@556 91 values.insert(std::string(elem));
universe@556 92 }
universe@561 93 EXPECT_EQ(valiter.index, map->size);
universe@556 94 ASSERT_EQ(values.size(), map->size);
universe@556 95 for (auto &&v: values) {
universe@556 96 EXPECT_NE(std::find_if(refmap.begin(), refmap.end(),
universe@556 97 [v](auto const &e) { return e.second == v; }), refmap.end());
universe@556 98 }
universe@556 99 }
universe@556 100
universe@556 101 // verify pair iterator
universe@556 102 {
universe@556 103 auto pairiter = cxMapIterator(map);
universe@556 104 std::unordered_map<std::string, std::string> pairs;
universe@556 105 cx_foreach(CxMapEntry*, entry, pairiter) {
universe@563 106 pairs[std::string(entry->key->data.cstr)] = std::string((char *) entry->value);
universe@556 107 }
universe@561 108 EXPECT_EQ(pairiter.index, map->size);
universe@556 109 ASSERT_EQ(pairs.size(), refmap.size());
universe@556 110 for (auto &&p: pairs) {
universe@556 111 ASSERT_EQ(p.second, refmap.at(p.first));
universe@556 112 }
universe@556 113 }
universe@556 114 }
universe@556 115
universe@556 116 TEST(CxHashMap, Create) {
universe@556 117 CxTestingAllocator allocator;
universe@556 118 auto map = cxHashMapCreate(&allocator, 0);
universe@556 119 auto hmap = reinterpret_cast<struct cx_hash_map_s *>(map);
universe@556 120 EXPECT_GT(hmap->bucket_count, 0);
universe@556 121 cx_for_n(i, hmap->bucket_count) {
universe@556 122 EXPECT_EQ(hmap->buckets[i], nullptr);
universe@556 123 }
universe@556 124 EXPECT_EQ(map->size, 0);
universe@556 125 EXPECT_EQ(map->allocator, &allocator);
universe@556 126
universe@556 127 cxMapDestroy(map);
universe@556 128 EXPECT_TRUE(allocator.verify());
universe@556 129 }
universe@556 130
universe@556 131 TEST(CxHashMap, BasicOperations) {
universe@556 132 // create the map
universe@556 133 CxTestingAllocator allocator;
universe@556 134 auto map = cxHashMapCreate(&allocator, 8);
universe@556 135
universe@556 136 // create a reference map
universe@556 137 std::unordered_map<std::string, std::string> refmap;
universe@556 138
universe@556 139 // generate operations
universe@556 140 auto ops = generate_map_operations();
universe@556 141
universe@556 142 // verify iterators for empty map
universe@556 143 verify_map_contents(map, refmap);
universe@556 144
universe@556 145 // execute operations and verify results
universe@556 146 for (auto &&op: ops) {
universe@563 147 CxHashKey key = cx_hash_key_str(op.key);
universe@563 148 key.hash = 0; // force the hash map to compute the hash
universe@556 149 if (op.op == map_operation::put) {
universe@556 150 // execute a put operation and verify that the exact value can be read back
universe@556 151 refmap[std::string(op.key)] = std::string(op.value);
universe@556 152 int result = cxMapPut(map, key, (void *) op.value);
universe@556 153 EXPECT_EQ(result, 0);
universe@556 154 auto added = cxMapGet(map, key);
universe@556 155 EXPECT_EQ(memcmp(op.value, added, strlen(op.value)), 0);
universe@556 156 } else {
universe@556 157 // execute a remove and verify that the removed element was returned (or nullptr)
universe@556 158 auto found = refmap.find(op.key);
universe@556 159 auto removed = cxMapRemove(map, key);
universe@556 160 if (found == refmap.end()) {
universe@556 161 EXPECT_EQ(removed, nullptr);
universe@556 162 } else {
universe@556 163 EXPECT_EQ(std::string((char *) removed), found->second);
universe@556 164 refmap.erase(found);
universe@556 165 }
universe@556 166 }
universe@556 167 // compare the current map state with the reference map
universe@556 168 verify_map_contents(map, refmap);
universe@556 169 }
universe@556 170
universe@556 171 // destroy the map and verify the memory (de)allocations
universe@556 172 cxMapDestroy(map);
universe@556 173 EXPECT_TRUE(allocator.verify());
universe@556 174 }
universe@561 175
universe@561 176 TEST(CxHashMap, RemoveViaIterator) {
universe@561 177 CxTestingAllocator allocator;
universe@561 178 auto map = cxHashMapCreate(&allocator, 4);
universe@561 179
universe@563 180 cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
universe@563 181 cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
universe@563 182 cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
universe@563 183 cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
universe@563 184 cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
universe@563 185 cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
universe@561 186
universe@561 187 auto iter = cxMapIterator(map);
universe@561 188 cx_foreach(CxMapEntry*, entry, iter) {
universe@563 189 if (entry->key->data.cstr[4] % 2 == 1) iter.remove = true;
universe@561 190 }
universe@561 191 EXPECT_EQ(map->size, 3);
universe@561 192 EXPECT_EQ(iter.index, map->size);
universe@561 193
universe@563 194 EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 1")), nullptr);
universe@563 195 EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 2")), nullptr);
universe@563 196 EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 3")), nullptr);
universe@563 197 EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 4")), nullptr);
universe@563 198 EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 5")), nullptr);
universe@563 199 EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 6")), nullptr);
universe@561 200
universe@561 201 cxMapDestroy(map);
universe@561 202 EXPECT_TRUE(allocator.verify());
universe@561 203 }
universe@562 204
universe@562 205 TEST(CxHashMap, RehashNotRequired) {
universe@562 206 CxTestingAllocator allocator;
universe@562 207 auto map = cxHashMapCreate(&allocator, 8);
universe@562 208
universe@563 209 cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
universe@563 210 cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
universe@563 211 cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
universe@563 212 cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
universe@563 213 cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
universe@563 214 cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
universe@562 215
universe@562 216 // 6/8 does not exceed 0.75, therefore the function should not rehash
universe@562 217 int result = cxMapRehash(map);
universe@562 218 EXPECT_EQ(result, 0);
universe@562 219 EXPECT_EQ(reinterpret_cast<struct cx_hash_map_s *>(map)->bucket_count, 8);
universe@562 220
universe@562 221 cxMapDestroy(map);
universe@562 222 EXPECT_TRUE(allocator.verify());
universe@562 223 }
universe@562 224
universe@562 225 TEST(CxHashMap, Rehash) {
universe@562 226 CxTestingAllocator allocator;
universe@562 227 auto map = cxHashMapCreate(&allocator, 8);
universe@562 228
universe@563 229 cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1");
universe@563 230 cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2");
universe@563 231 cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3");
universe@563 232 cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4");
universe@563 233 cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5");
universe@563 234 cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6");
universe@563 235 cxMapPut(map, cx_hash_key_str("key 7"), (void *) "val 7");
universe@562 236
universe@562 237 int result = cxMapRehash(map);
universe@562 238 EXPECT_EQ(result, 0);
universe@562 239 EXPECT_EQ(reinterpret_cast<struct cx_hash_map_s *>(map)->bucket_count, 17);
universe@562 240 EXPECT_EQ(map->size, 7);
universe@562 241
universe@563 242 EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 1")), "val 1"), 0);
universe@563 243 EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 2")), "val 2"), 0);
universe@563 244 EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 3")), "val 3"), 0);
universe@563 245 EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 4")), "val 4"), 0);
universe@563 246 EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 5")), "val 5"), 0);
universe@563 247 EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 6")), "val 6"), 0);
universe@563 248 EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 7")), "val 7"), 0);
universe@562 249
universe@562 250 cxMapDestroy(map);
universe@562 251 EXPECT_TRUE(allocator.verify());
universe@562 252 }

mercurial