libs/utils/gtest/src/HashMapTestSuite.cc (527 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <gtest/gtest.h>
#include "celix_hash_map_private.h"
#include "celix_long_hash_map.h"
#include "celix_string_hash_map.h"
#include "celix_hash_map_internal.h"
#include "celix_utils.h"
#include <atomic>
#include <random>
class HashMapTestSuite : public ::testing::Test {
public:
/**
* Create and fill string hash map with nrEntries entries.
*/
celix_string_hash_map_t* createStringHashMap(int nrEntries) {
auto* map = celix_stringHashMap_create();
fillStringHashMap(map, nrEntries);
return map;
}
/**
* Fill string hash map with nrOfEntries long value entries where the key and value are based on the
* i index value of the for loop.
* This way the expected hash map entry value can be deducted from the key.
*/
void fillStringHashMap(celix_string_hash_map_t* map, int nrEntries) {
for (int i = 0; i < nrEntries; ++i) {
std::string key = "key" + std::to_string(i);
celix_stringHashMap_putLong(map, key.c_str(), i);
EXPECT_EQ(i, celix_stringHashMap_getLong(map, key.c_str(), 0));
EXPECT_EQ(celix_stringHashMap_size(map), i + 1);
}
}
/**
* Randomly test nrOfEntriesToTest entries in a string hash map.
* This assumes that the map is filled using fillStringHashMap.
*/
void testGetEntriesFromStringMap(celix_string_hash_map_t* map, int nrOfEntriesToTest) {
std::uniform_int_distribution<long> keyDistribution{0, (long)celix_stringHashMap_size(map) - 1};
for (int i = 0; i < nrOfEntriesToTest; ++i) {
long rand = keyDistribution(generator);
auto key = std::string{"key"} + std::to_string(rand);
EXPECT_EQ(celix_stringHashMap_getLong(map, key.c_str(), 0), rand) << "got wrong result for key " << key;
}
}
/**
* Create and fill long hash map with nrEntries entries.
*/
celix_long_hash_map_t* createLongHashMap(int nrEntries) {
auto* map = celix_longHashMap_create();
fillLongHashMap(map, nrEntries);
return map;
}
/**
* Fill long hash map with nrOfEntries long value entries where the key and value are based on the
* i index value of the for loop.
* This way the expected hash map entry value can be deducted from the key.
*/
void fillLongHashMap(celix_long_hash_map_t* map, int nrEntries) {
for (int i = 0; i < nrEntries; ++i) {
celix_longHashMap_putLong(map, i, i);
EXPECT_EQ(i, celix_longHashMap_getLong(map, i, 0));
EXPECT_EQ(celix_longHashMap_size(map), i + 1);
}
}
/**
* Randomly test nrOfEntriesToTest entries in a long hash map.
* This assumes that the map is filled using fillLongHashMap.
*/
void testGetEntriesFromLongMap(celix_long_hash_map_t* map, int nrOfEntriesToTest) {
std::uniform_int_distribution<long> keyDistribution{0, (long)celix_longHashMap_size(map) - 1};
for (int i = 0; i < nrOfEntriesToTest; ++i) {
long rand = keyDistribution(generator);
EXPECT_EQ(celix_longHashMap_getLong(map, rand, 0), rand);
}
}
void printStats(const char* keyType, const celix_hash_map_statistics_t* stats) {
printf("Hashmap statistics:\n");
printf("|- key type: %s\n", keyType);
printf("|- nr of entries: %zu\n", stats->nrOfEntries);
printf("|- nr of buckets: %zu\n", stats->nrOfBuckets);
printf("|- average nr of entries in bucket: %f\n", stats->averageNrOfEntriesPerBucket);
printf("|- stddev nr of entries in bucket: %f\n", stats->stdDeviationNrOfEntriesPerBucket);
printf("|- resize count: %zu\n", stats->resizeCount);
}
private:
std::default_random_engine generator{};
};
TEST_F(HashMapTestSuite, CreateDestroyHashMap) {
auto* sMap = celix_stringHashMap_create();
EXPECT_TRUE(sMap != nullptr);
EXPECT_EQ(0, celix_stringHashMap_size(sMap));
celix_stringHashMap_destroy(sMap);
auto* lMap = celix_longHashMap_create();
EXPECT_TRUE(lMap != nullptr);
EXPECT_EQ(0, celix_longHashMap_size(lMap));
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, PutPointerEntriesInStringHashMap) {
auto* map = celix_stringHashMap_create();
EXPECT_TRUE(map != nullptr);
EXPECT_EQ(0, celix_stringHashMap_size(map));
celix_stringHashMap_put(map, "key1", (void*)0x41);
celix_stringHashMap_put(map, "key2", (void*)0x42);
celix_stringHashMap_put(map, "key3", (void*)0x43);
celix_stringHashMap_put(map, "key4", (void*)0x44);
EXPECT_EQ(celix_stringHashMap_size(map), 4);
EXPECT_TRUE(celix_stringHashMap_hasKey(map, "key1"));
EXPECT_TRUE(celix_stringHashMap_hasKey(map, "key2"));
EXPECT_TRUE(celix_stringHashMap_hasKey(map, "key3"));
EXPECT_TRUE(celix_stringHashMap_hasKey(map, "key4"));
EXPECT_FALSE(celix_stringHashMap_hasKey(map, "key5"));
EXPECT_EQ(celix_stringHashMap_get(map, "key1"), (void*)0x41);
EXPECT_EQ(celix_stringHashMap_get(map, "key2"), (void*)0x42);
EXPECT_EQ(celix_stringHashMap_get(map, "key3"), (void*)0x43);
EXPECT_EQ(celix_stringHashMap_get(map, "key4"), (void*)0x44);
EXPECT_EQ(celix_stringHashMap_get(map, "key5"), nullptr);
celix_stringHashMap_destroy(map);
}
TEST_F(HashMapTestSuite, PutPointerEntriesInLongHashMap) {
auto* map = celix_longHashMap_create();
EXPECT_TRUE(map != nullptr);
EXPECT_EQ(0, celix_longHashMap_size(map));
celix_longHashMap_put(map, 1, (void*)0x41);
celix_longHashMap_put(map, 2, (void*)0x42);
celix_longHashMap_put(map, 3, (void*)0x43);
celix_longHashMap_put(map, 4, (void*)0x44);
EXPECT_EQ(celix_longHashMap_size(map), 4);
EXPECT_TRUE(celix_longHashMap_hasKey(map, 1));
EXPECT_TRUE(celix_longHashMap_hasKey(map, 2));
EXPECT_TRUE(celix_longHashMap_hasKey(map, 3));
EXPECT_TRUE(celix_longHashMap_hasKey(map, 4));
EXPECT_FALSE(celix_longHashMap_hasKey(map, 5));
EXPECT_EQ(celix_longHashMap_get(map, 1), (void*)0x41);
EXPECT_EQ(celix_longHashMap_get(map, 2), (void*)0x42);
EXPECT_EQ(celix_longHashMap_get(map, 3), (void*)0x43);
EXPECT_EQ(celix_longHashMap_get(map, 4), (void*)0x44);
EXPECT_EQ(celix_longHashMap_get(map, 5), nullptr);
celix_longHashMap_destroy(map);
}
TEST_F(HashMapTestSuite, StringKeyNullEntry) {
auto* map = celix_stringHashMap_create();
EXPECT_FALSE(celix_stringHashMap_hasKey(map, nullptr));
celix_stringHashMap_put(map, nullptr, (void*)0x42);
EXPECT_TRUE(celix_stringHashMap_hasKey(map, nullptr));
EXPECT_EQ(1, celix_stringHashMap_size(map));
celix_stringHashMap_destroy(map);
}
TEST_F(HashMapTestSuite, DestroyHashMapWithSimpleRemovedCallback) {
celix_string_hash_map_create_options_t sOpts{};
sOpts.simpleRemovedCallback = free;
auto* sMap = celix_stringHashMap_createWithOptions(&sOpts);
EXPECT_TRUE(sMap != nullptr);
EXPECT_EQ(0, celix_stringHashMap_size(sMap));
celix_stringHashMap_put(sMap, "key1", celix_utils_strdup("41"));
celix_stringHashMap_put(sMap, "key2", celix_utils_strdup("42"));
celix_stringHashMap_put(sMap, "key3", celix_utils_strdup("43"));
celix_stringHashMap_put(sMap, "key4", celix_utils_strdup("44"));
celix_stringHashMap_put(sMap, nullptr, celix_utils_strdup("null"));
EXPECT_EQ(celix_stringHashMap_size(sMap), 5);
EXPECT_STREQ((char*)celix_stringHashMap_get(sMap, nullptr), "null");
bool removed = celix_stringHashMap_remove(sMap, "key3");
EXPECT_TRUE(removed);
removed = celix_stringHashMap_remove(sMap, "key3"); // double remove
EXPECT_FALSE(removed);
removed = celix_stringHashMap_remove(sMap, nullptr);
EXPECT_TRUE(removed);
removed = celix_stringHashMap_remove(sMap, nullptr); // double remove
EXPECT_FALSE(removed);
EXPECT_EQ(celix_stringHashMap_size(sMap), 3);
celix_stringHashMap_destroy(sMap);
celix_long_hash_map_create_options_t lOpts{};
lOpts.simpleRemovedCallback = free;
auto* lMap = celix_longHashMap_createWithOptions(&lOpts);
EXPECT_TRUE(lMap != nullptr);
EXPECT_EQ(0, celix_longHashMap_size(lMap));
celix_longHashMap_put(lMap, 1, celix_utils_strdup("41"));
celix_longHashMap_put(lMap, 2, celix_utils_strdup("42"));
celix_longHashMap_put(lMap, 3, celix_utils_strdup("43"));
celix_longHashMap_put(lMap, 4, celix_utils_strdup("44"));
celix_longHashMap_put(lMap, 0, celix_utils_strdup("null"));
EXPECT_EQ(celix_longHashMap_size(lMap), 5);
EXPECT_STREQ((char*)celix_longHashMap_get(lMap, 0), "null");
celix_longHashMap_remove(lMap, 3);
celix_longHashMap_remove(lMap, 0);
EXPECT_EQ(celix_longHashMap_size(lMap), 3);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, ReplaceAndClearHashMapWithRemovedCallback) {
std::atomic<int> count{0};
celix_string_hash_map_create_options_t sOpts{};
sOpts.removedCallbackData = &count;
sOpts.removedCallback = [](void* data, const char* key, celix_hash_map_value_t value) {
auto* c = static_cast<std::atomic<int>*>(data);
if (celix_utils_stringEquals(key, "key1")) {
c->fetch_add(1);
EXPECT_TRUE(value.longValue == 1 || value.longValue == 2);
} else if (celix_utils_stringEquals(key, "key2")) {
c->fetch_add(1);
EXPECT_TRUE(value.longValue == 3 || value.longValue == 4);
}
};
auto* sMap = celix_stringHashMap_createWithOptions(&sOpts);
celix_stringHashMap_putLong(sMap, "key1", 1);
celix_stringHashMap_putLong(sMap, "key1", 2); // replacing old value, count +1
celix_stringHashMap_putLong(sMap, "key2", 3);
celix_stringHashMap_putLong(sMap, "key2", 4); // replacing old value, count +1
celix_stringHashMap_clear(sMap); // count +2
EXPECT_EQ(count.load(), 4);
EXPECT_EQ(celix_stringHashMap_size(sMap), 0);
celix_stringHashMap_destroy(sMap);
count = 0;
celix_long_hash_map_create_options_t lOpts{};
lOpts.removedCallbackData = &count;
lOpts.removedCallback = [](void* data, long key, celix_hash_map_value_t value) {
auto* c = static_cast<std::atomic<int>*>(data);
if (key == 1) {
c->fetch_add(1);
EXPECT_TRUE(value.longValue == 1 || value.longValue == 2);
} else if (key == 2) {
c->fetch_add(1);
EXPECT_TRUE(value.longValue == 3 || value.longValue == 4);
}
};
auto* lMap = celix_longHashMap_createWithOptions(&lOpts);
celix_longHashMap_putLong(lMap, 1, 1);
celix_longHashMap_putLong(lMap, 1, 2); // replacing old value, count +1
celix_longHashMap_putLong(lMap, 2, 3);
celix_longHashMap_putLong(lMap, 2, 4); // replacing old value, count +1
celix_longHashMap_clear(lMap); // count +2
EXPECT_EQ(count.load(), 4);
EXPECT_EQ(celix_longHashMap_size(lMap), 0);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, HashMapClearTests) {
auto* sMap = createStringHashMap(3);
EXPECT_EQ(celix_stringHashMap_size(sMap), 3);
celix_stringHashMap_clear(sMap);
EXPECT_EQ(celix_stringHashMap_size(sMap), 0);
celix_stringHashMap_destroy(sMap);
auto* lMap = createLongHashMap(3);
EXPECT_EQ(celix_longHashMap_size(lMap), 3);
celix_longHashMap_clear(lMap);
EXPECT_EQ(celix_longHashMap_size(lMap), 0);
celix_longHashMap_destroy(lMap);
}
template <typename T>
void testMapsWithValues(const std::vector<std::tuple<std::string, long, T>>& values) {
auto* sMap = celix_stringHashMap_create();
auto* lMap = celix_longHashMap_create();
// test hashmap put
for (const auto& tuple : values) {
if constexpr (std::is_same_v<void*, T>) {
auto status = celix_stringHashMap_put(sMap, std::get<0>(tuple).c_str(), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_longHashMap_put(lMap, std::get<1>(tuple), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
} else if constexpr (std::is_same_v<long, T>) {
auto status = celix_stringHashMap_putLong(sMap, std::get<0>(tuple).c_str(), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_longHashMap_putLong(lMap, std::get<1>(tuple), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
} else if constexpr (std::is_same_v<double, T>) {
auto status = celix_stringHashMap_putDouble(sMap, std::get<0>(tuple).c_str(), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_longHashMap_putDouble(lMap, std::get<1>(tuple), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
} else if constexpr (std::is_same_v<bool, T>) {
auto status = celix_stringHashMap_putBool(sMap, std::get<0>(tuple).c_str(), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
status = celix_longHashMap_putBool(lMap, std::get<1>(tuple), std::get<2>(tuple));
EXPECT_EQ(status, CELIX_SUCCESS);
} else {
FAIL() << "expected of the following value types: void*, long, double or bool";
}
}
ASSERT_EQ(values.size(), celix_stringHashMap_size(sMap));
ASSERT_EQ(values.size(), celix_longHashMap_size(lMap));
// test hashmap get
for (const auto& tuple : values) {
if constexpr (std::is_same_v<void*, T>) {
auto* value = celix_stringHashMap_get(sMap, std::get<0>(tuple).c_str());
EXPECT_EQ(value, std::get<2>(tuple));
value = celix_longHashMap_get(lMap, std::get<1>(tuple));
EXPECT_EQ(value, std::get<2>(tuple));
} else if constexpr (std::is_same_v<long, T>) {
auto value = celix_stringHashMap_getLong(sMap, std::get<0>(tuple).c_str(), 0);
EXPECT_EQ(value, std::get<2>(tuple));
value = celix_longHashMap_getLong(lMap, std::get<1>(tuple), 0);
EXPECT_EQ(value, std::get<2>(tuple));
} else if constexpr (std::is_same_v<double, T>) {
auto value = celix_stringHashMap_getDouble(sMap, std::get<0>(tuple).c_str(), 0.0);
EXPECT_EQ(value, std::get<2>(tuple));
value = celix_longHashMap_getDouble(lMap, std::get<1>(tuple), 0.0);
EXPECT_EQ(value, std::get<2>(tuple));
} else if constexpr (std::is_same_v<bool, T>) {
auto value = celix_stringHashMap_getBool(sMap, std::get<0>(tuple).c_str(), false);
EXPECT_EQ(value, std::get<2>(tuple));
value = celix_longHashMap_getBool(lMap, std::get<1>(tuple), false);
EXPECT_EQ(value, std::get<2>(tuple));
} else {
FAIL() << "expected of the following value types: void*, long, double or bool";
}
}
// test hashmap remove
for (const auto& tuple : values) {
celix_stringHashMap_remove(sMap, std::get<0>(tuple).c_str());
celix_longHashMap_remove(lMap, std::get<1>(tuple));
}
ASSERT_EQ(0, celix_stringHashMap_size(sMap));
ASSERT_EQ(0, celix_longHashMap_size(lMap));
celix_stringHashMap_destroy(sMap);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, PutAndGetWithDifferentValueTypes) {
std::vector<std::tuple<std::string, long, void*>> values1{
{"key1", 1, (void*)0x42},
{"key2", 2, (void*)0x43},
{"key3", 3, (void*)0x44},
};
testMapsWithValues(values1);
std::vector<std::tuple<std::string, long, long>> values2{
{"key1", 1, 1},
{"key2", 2, 2},
{"key3", 3, 3},
};
testMapsWithValues(values2);
std::vector<std::tuple<std::string, long, double>> values3{
{"key1", 1, 1.0},
{"key2", 2, 2.0},
{"key3", 3, 3.0},
};
testMapsWithValues(values3);
std::vector<std::tuple<std::string, long, bool>> values4{
{"key1", 1, true},
{"key2", 2, false},
{"key3", 3, true},
};
testMapsWithValues(values4);
}
TEST_F(HashMapTestSuite, GetWithFallbackValues) {
auto* sMap = celix_stringHashMap_create();
EXPECT_EQ(celix_stringHashMap_get(sMap, "not-present"), nullptr);
EXPECT_EQ(celix_stringHashMap_getLong(sMap, "not-present", -1), -1);
EXPECT_EQ(celix_stringHashMap_getDouble(sMap, "not-present", 1.0), 1.0);
EXPECT_EQ(celix_stringHashMap_getBool(sMap, "not-present", true), true);
celix_stringHashMap_destroy(sMap);
auto* lMap = celix_longHashMap_create();
EXPECT_EQ(celix_longHashMap_get(lMap, 33), nullptr);
EXPECT_EQ(celix_longHashMap_getLong(lMap, 33, 1), 1);
EXPECT_EQ(celix_longHashMap_getDouble(lMap, 33, -1.0), -1.0);
EXPECT_EQ(celix_longHashMap_getBool(lMap, 33, false), false);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, IterateTest) {
auto* sMap = createStringHashMap(2);
size_t count = 0;
CELIX_STRING_HASH_MAP_ITERATE(sMap, iter) {
count++;
if (iter.value.longValue == 0) {
EXPECT_STREQ(iter.key, "key0");
} else if (iter.value.longValue == 1) {
EXPECT_STREQ(iter.key, "key1");
} else {
FAIL() << "Unexpected value " << iter.value.longValue;
}
}
EXPECT_EQ(count, 2);
celix_stringHashMap_destroy(sMap);
auto* lMap = createLongHashMap(2);
count = 0;
CELIX_LONG_HASH_MAP_ITERATE(lMap, iter) {
count++;
if (iter.value.longValue == 0) {
EXPECT_EQ(iter.key, 0);
} else if (iter.value.longValue == 1) {
EXPECT_EQ(iter.key, 1);
} else {
FAIL() << "Unexpected value " << iter.value.longValue;
}
}
EXPECT_EQ(count, 2);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, IterateStressTest) {
int testCount = 1000;
auto* sMap = createStringHashMap(testCount);
EXPECT_EQ(testCount, celix_stringHashMap_size(sMap));
int count = 0;
CELIX_STRING_HASH_MAP_ITERATE(sMap, iter) { count++; }
EXPECT_EQ(testCount, count);
testGetEntriesFromStringMap(sMap, 100);
celix_stringHashMap_destroy(sMap);
auto* lMap = createLongHashMap(testCount);
EXPECT_EQ(testCount, celix_longHashMap_size(lMap));
count = 0;
CELIX_LONG_HASH_MAP_ITERATE(lMap, iter) { count++; }
EXPECT_EQ(testCount, count);
testGetEntriesFromLongMap(lMap, 100);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, IterateStressCapacityAndLoadFactorTest) {
celix_string_hash_map_create_options sOpts{};
sOpts.maxLoadFactor = 10; // high load factor to ensure buckets have multiple entries for testing
sOpts.initialCapacity = 5;
auto* sMap = celix_stringHashMap_createWithOptions(&sOpts);
fillStringHashMap(sMap, 100);
long value = celix_stringHashMap_getLong(sMap, "key50", 0);
EXPECT_EQ(50, value);
// remove last 50 entries
for (long i = 50; i < 100; ++i) {
auto key = std::string{"key"} + std::to_string(i);
celix_stringHashMap_remove(sMap, key.c_str());
}
testGetEntriesFromStringMap(sMap, 50); // test entry 0-49
celix_stringHashMap_destroy(sMap);
celix_long_hash_map_create_options lOpts{};
lOpts.maxLoadFactor = 10; // high load factor to ensure buckets have multiple entries for testing
lOpts.initialCapacity = 5;
auto* lMap = celix_longHashMap_createWithOptions(&lOpts);
fillLongHashMap(lMap, 100);
value = celix_longHashMap_getLong(lMap, 50, 0);
EXPECT_EQ(50, value);
// remove last 50 entries
for (long i = 50; i < 100; ++i) {
celix_longHashMap_remove(lMap, i);
}
testGetEntriesFromLongMap(lMap, 50);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, IterateEndTest) {
auto* sMap1 = createStringHashMap(0);
auto* sMap2 = createStringHashMap(6);
auto* lMap1 = createLongHashMap(0);
auto* lMap2 = createLongHashMap(6);
auto sIter1 = celix_stringHashMap_end(sMap1);
auto sIter2 = celix_stringHashMap_end(sMap2);
auto lIter1 = celix_longHashMap_end(lMap1);
auto lIter2 = celix_longHashMap_end(lMap2);
EXPECT_TRUE(celix_stringHashMapIterator_isEnd(&sIter1));
EXPECT_TRUE(celix_stringHashMapIterator_isEnd(&sIter2));
EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter1));
EXPECT_TRUE(celix_longHashMapIterator_isEnd(&lIter2));
celix_stringHashMap_destroy(sMap1);
celix_stringHashMap_destroy(sMap2);
celix_longHashMap_destroy(lMap1);
celix_longHashMap_destroy(lMap2);
}
TEST_F(HashMapTestSuite, EqualsTest) {
auto* sMap = createStringHashMap(2);
auto sIter1 = celix_stringHashMap_begin(sMap);
auto sIter2 = celix_stringHashMap_begin(sMap);
// Test equal iterators
EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sIter2));
// Test unequal iterators after only 1 modification
celix_stringHashMapIterator_next(&sIter1);
EXPECT_FALSE(celix_stringHashMapIterator_equals(&sIter1, &sIter2));
// Test equal iterators after both modification
celix_stringHashMapIterator_next(&sIter2);
EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sIter2));
// Same for long hash map
auto* lMap = createLongHashMap(1);
auto lIter1 = celix_longHashMap_begin(lMap);
auto lIter2 = celix_longHashMap_begin(lMap);
// Test equal iterators
EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lIter2));
// Test unequal iterators after only 1 modification
celix_longHashMapIterator_next(&lIter1);
EXPECT_FALSE(celix_longHashMapIterator_equals(&lIter1, &lIter2));
// Test equal iterators after both modification
celix_longHashMapIterator_next(&lIter2);
EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lIter2));
celix_stringHashMap_destroy(sMap);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, EqualsZeroSizeMapTest) {
// Because map size is 0, begin iter should equal end iter
auto* sMap = createStringHashMap(0);
auto sIter1 = celix_stringHashMap_begin(sMap);
auto sEnd = celix_stringHashMap_end(sMap);
EXPECT_TRUE(celix_stringHashMapIterator_equals(&sIter1, &sEnd));
// Because map size is 0, begin iter should equal end iter
auto* lMap = createLongHashMap(0);
auto lIter1 = celix_longHashMap_begin(lMap);
auto lEnd = celix_longHashMap_end(lMap);
EXPECT_TRUE(celix_longHashMapIterator_equals(&lIter1, &lEnd));
celix_stringHashMap_destroy(sMap);
celix_longHashMap_destroy(lMap);
}
TEST_F(HashMapTestSuite, StoreKeysWeaklyTest) {
celix_string_hash_map_create_options_t opts{};
opts.removedCallbackData = (void*)0x1;
opts.storeKeysWeakly = true;
opts.removedKeyCallback = [](void* data, char* key) {
EXPECT_EQ(data, (void*)0x1);
free(key);
};
auto* sMap = celix_stringHashMap_createWithOptions(&opts);
EXPECT_FALSE(celix_stringHashMap_hasKey(sMap, "key1"));
EXPECT_EQ(CELIX_SUCCESS, celix_stringHashMap_putLong(sMap, celix_utils_strdup("key1"), 1)); // new key -> takes ownership
EXPECT_TRUE(celix_stringHashMap_hasKey(sMap, "key1"));
EXPECT_EQ(CELIX_SUCCESS, celix_stringHashMap_putLong(sMap, "key1", 2)); // replace key -> takes no ownership
EXPECT_FALSE(celix_stringHashMap_hasKey(sMap, "key2"));
EXPECT_EQ(CELIX_SUCCESS, celix_stringHashMap_putLong(sMap, celix_utils_strdup("key2"), 3)); // new key -> takes ownership
EXPECT_TRUE(celix_stringHashMap_hasKey(sMap, "key2"));
EXPECT_EQ(CELIX_SUCCESS, celix_stringHashMap_putLong(sMap, "key2", 4)); // replace key -> takes no ownership
celix_stringHashMap_remove(sMap, "key1");
celix_stringHashMap_destroy(sMap);
}
TEST_F(HashMapTestSuite, StringHashMapCleanupTest) {
celix_autoptr(celix_string_hash_map_t) map = celix_stringHashMap_create();
EXPECT_NE(nullptr, map);
}
TEST_F(HashMapTestSuite, LongHashMapCleanupTest) {
celix_autoptr(celix_long_hash_map_t) map = celix_longHashMap_create();
EXPECT_NE(nullptr, map);
}
TEST_F(HashMapTestSuite, StringHashMapEqualsTest) {
celix_autoptr(celix_string_hash_map_t) map1 = celix_stringHashMap_create();
celix_autoptr(celix_string_hash_map_t) map2 = celix_stringHashMap_create();
EXPECT_TRUE(celix_stringHashMap_equals(map1, map2));
EXPECT_FALSE(celix_stringHashMap_equals(map1, nullptr));
EXPECT_FALSE(celix_stringHashMap_equals(nullptr, map2));
EXPECT_TRUE(celix_stringHashMap_equals(nullptr, nullptr));
EXPECT_TRUE(celix_stringHashMap_equals(map1, map1));
celix_stringHashMap_putLong(map1, "key1", 42);
celix_stringHashMap_putBool(map1, "key2", true);
celix_stringHashMap_putLong(map2, "key1", 42);
celix_stringHashMap_putBool(map2, "key2", true);
EXPECT_TRUE(celix_stringHashMap_equals(map1, map2));
celix_stringHashMap_putLong(map2, "key3", 42);
EXPECT_FALSE(celix_stringHashMap_equals(map1, map2));
celix_stringHashMap_putLong(map1, "key3", 42);
EXPECT_TRUE(celix_stringHashMap_equals(map1, map2));
}
TEST_F(HashMapTestSuite, LongHashMapEqualsTest) {
celix_autoptr(celix_long_hash_map_t) map1 = celix_longHashMap_create();
celix_autoptr(celix_long_hash_map_t) map2 = celix_longHashMap_create();
EXPECT_TRUE(celix_longHashMap_equals(map1, map2));
EXPECT_FALSE(celix_longHashMap_equals(map1, nullptr));
EXPECT_FALSE(celix_longHashMap_equals(nullptr, map2));
EXPECT_TRUE(celix_longHashMap_equals(nullptr, nullptr));
EXPECT_TRUE(celix_longHashMap_equals(map1, map1));
celix_longHashMap_putLong(map1, 1, 42);
celix_longHashMap_putBool(map1, 2, true);
celix_longHashMap_putLong(map2, 1, 42);
celix_longHashMap_putBool(map2, 2, true);
EXPECT_TRUE(celix_longHashMap_equals(map1, map2));
celix_longHashMap_putLong(map2, 3, 42);
EXPECT_FALSE(celix_longHashMap_equals(map1, map2));
celix_longHashMap_putLong(map1, 3, 42);
EXPECT_TRUE(celix_longHashMap_equals(map1, map2));
}
TEST_F(HashMapTestSuite, GetStatsTest) {
celix_autoptr(celix_long_hash_map_t) map1 = celix_longHashMap_create();
celix_autoptr(celix_string_hash_map_t) map2 = celix_stringHashMap_create();
for (int i = 0; i < 200; ++i) {
celix_longHashMap_putLong(map1, i, i);
celix_stringHashMap_putLong(map2, std::to_string(i).c_str(), i);
}
celix_hash_map_statistics_t stats = celix_longHashMap_getStatistics(map1);
EXPECT_EQ(stats.nrOfEntries, 200);
printStats("long", &stats);
stats = celix_stringHashMap_getStatistics(map2);
EXPECT_EQ(stats.nrOfEntries, 200);
printStats("string", &stats);
}