#include "../Tests.hpp" #include "core/data/HashMap.hpp" #include "core/data/ProbingHashMap.hpp" template struct Core::ProbingHashMap; using ProbingIntMap = Core::ProbingHashMap; template struct Core::HashMap; using IntMap = Core::HashMap; constexpr int INVALID = Core::emptyValue(); template static T getTestIntMap() { T map; map.add(1, 3).add(2, 4).add(3, 5).add(INVALID, 20); return map; } template static void checkIntMap(T& map) { int* a = map.search(1); int* b = map.search(2); int* c = map.search(3); int* d = map.search(INVALID); if(CORE_TEST_NOT_NULL(a) && CORE_TEST_NOT_NULL(b) && CORE_TEST_NOT_NULL(c) && CORE_TEST_NOT_NULL(d)) { CORE_TEST_EQUAL(3, *a); CORE_TEST_EQUAL(4, *b); CORE_TEST_EQUAL(5, *c); CORE_TEST_EQUAL(20, *d); } } template static void testAdd() { T map; map.add(5, 4); int* value = map.search(5); CORE_TEST_NOT_NULL(value); if(value != nullptr) { CORE_TEST_EQUAL(4, *value); } } template static void testMultipleAdd() { T map = getTestIntMap(); CORE_TEST_TRUE(map.contains(1)); CORE_TEST_TRUE(map.contains(2)); CORE_TEST_TRUE(map.contains(3)); checkIntMap(map); } template static void testSearch() { T map; CORE_TEST_NULL(map.search(6)); map.add(5, 4).add(10, 3).add(15, 2); CORE_TEST_NULL(map.search(6)); } template static void testAddReplace() { T map; map.add(5, 4).add(5, 10); CORE_TEST_TRUE(map.contains(5)); int* a = map.search(5); if(CORE_TEST_NOT_NULL(a)) { CORE_TEST_EQUAL(10, *a); } } template static void testClear() { T map; map.add(5, 4).add(4, 10); map.clear(); CORE_TEST_FALSE(map.contains(5)); CORE_TEST_FALSE(map.contains(4)); } template static void testOverflow(bool light) { int limit = light ? 10000 : 100000; T map; map.add(INVALID, 42); for(int i = 0; i < limit; i++) { map.add(i, i); } for(int i = 0; i < limit; i++) { CORE_TEST_TRUE(map.contains(i)); } CORE_TEST_TRUE(map.contains(INVALID)); } static int aInstances = 0; struct HashMapTest { int a; int b; HashMapTest(int a_, int b_) : a(a_), b(b_) { } // none of these should be needed for the hashmap HashMapTest(const HashMapTest&) = delete; HashMapTest(HashMapTest&&) = delete; HashMapTest& operator=(const HashMapTest&) = delete; HashMapTest& operator=(HashMapTest&&) = delete; bool operator==(const HashMapTest& other) const { return a == other.a && b == other.b; } template void toString(String& s) const { s.append("A(").append(a).append(", ").append(b).append(")"); } }; struct ProbingTest final : public HashMapTest { ProbingTest(int a_, int b_) : HashMapTest(a_, b_) { aInstances++; } ProbingTest(const ProbingTest& o) : HashMapTest(o.a, o.b) { aInstances++; } ProbingTest(ProbingTest&& o) : HashMapTest(o.a, o.b) { aInstances++; } ~ProbingTest() { aInstances--; } ProbingTest& operator=(ProbingTest o) { a = o.a; b = o.b; return *this; } }; static void testEmplaceProbing() { { Core::ProbingHashMap map; ProbingTest* ar = nullptr; CORE_TEST_TRUE(map.tryEmplace(ar, 0, 3, 4)); CORE_TEST_TRUE(map.tryEmplace(ar, 3, 4, 5)); CORE_TEST_TRUE(map.tryEmplace(ar, 20, 5, 6)); CORE_TEST_FALSE(map.tryEmplace(ar, 3, 6, 7)); CORE_TEST_FALSE(map.tryEmplace(ar, 20, 7, 8)); ProbingTest* a = map.search(0); ProbingTest* b = map.search(3); ProbingTest* c = map.search(20); if(CORE_TEST_NOT_NULL(a) && CORE_TEST_NOT_NULL(b) && CORE_TEST_NOT_NULL(c)) { CORE_TEST_EQUAL(ProbingTest(3, 4), *a); CORE_TEST_EQUAL(ProbingTest(4, 5), *b); CORE_TEST_EQUAL(ProbingTest(5, 6), *c); } } CORE_TEST_EQUAL(0, aInstances); } template static void testToString() { if constexpr(Core::IsSame) { CORE_TEST_STRING("[1 = 3, 2 = 4, 3 = 5, 2147483647 = 20]", getTestIntMap()); } else { CORE_TEST_STRING("[2 = 4, 1 = 3, 3 = 5, 2147483647 = 20]", getTestIntMap()); } CORE_TEST_STRING("[1 = 3]", T().add(1, 3)); CORE_TEST_STRING("[]", T()); } template static void testCopy() { T map = getTestIntMap(); T copy = map; T copyA; copyA = map; checkIntMap(map); checkIntMap(copy); checkIntMap(copyA); map.add(1, 20).add(2, 30).add(3, 40); checkIntMap(copy); checkIntMap(copyA); } template static void testMove() { T map = getTestIntMap(); T move(Core::move(map)); checkIntMap(move); } template static void testMoveAssignment() { T map = getTestIntMap(); T move; move = Core::move(map); checkIntMap(move); } template static void testEntryForEach() { T map; map.add(5, 4).add(10, 3).add(15, 2); int counter = 0; for(auto entry : map) { counter += entry.getKey() + entry.value; } CORE_TEST_EQUAL(39, counter); const T& cmap = map; counter = 0; for(auto entry : cmap) { counter += entry.getKey() + entry.value; } CORE_TEST_EQUAL(39, counter); } template static void testKeyForEach() { T map; map.add(5, 4).add(10, 3).add(15, 2); int counter = 0; for(const int& key : map.getKeys()) { counter += key; } CORE_TEST_EQUAL(30, counter); const T& cmap = map; counter = 0; for(const int& key : cmap.getKeys()) { counter += key; } CORE_TEST_EQUAL(30, counter); } template static void testValueForEach() { T map; map.add(5, 4).add(10, 3).add(15, 2); int counter = 0; for(int& value : map.getValues()) { counter += value; } CORE_TEST_EQUAL(9, counter); const T& cmap = map; counter = 0; for(const int& value : cmap.getValues()) { counter += value; } CORE_TEST_EQUAL(9, counter); } template static void testType() { Core::ProbingHashMap m; m.add(T(), 3); } template static void testTypes() { testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); testType(); } template static void testInvalid() { T map; int* v; CORE_TEST_TRUE(map.tryEmplace(v, INVALID, 2)); if(CORE_TEST_NOT_NULL(v)) { CORE_TEST_EQUAL(2, *v); } CORE_TEST_FALSE(map.tryEmplace(v, INVALID, 6)); if(CORE_TEST_NOT_NULL(v)) { CORE_TEST_EQUAL(2, *v); } CORE_TEST_EQUAL(3, map.put(INVALID, 3)); v = map.search(INVALID); if(CORE_TEST_NOT_NULL(v)) { CORE_TEST_EQUAL(3, *v); } map.clear(); CORE_TEST_NULL(map.search(INVALID)); } template static void testInvalidPut() { T map; CORE_TEST_EQUAL(3, map.put(INVALID, 3)); int* v = map.search(INVALID); if(CORE_TEST_NOT_NULL(v)) { CORE_TEST_EQUAL(3, *v); } } template static void testAddCollisions() { T map; for(int i = 0; i < 8; i++) { map.add(i * 16, i); } } template static void testMap(bool light) { testAdd(); testMultipleAdd(); testSearch(); testAddReplace(); testClear(); testOverflow(light); testToString(); testCopy(); testMove(); testMoveAssignment(); testEntryForEach(); testKeyForEach(); testValueForEach(); testTypes(); testInvalid(); testInvalidPut(); testAddCollisions(); } static void testEmplace() { Core::HashMap map; HashMapTest* ar = nullptr; CORE_TEST_TRUE(map.tryEmplace(ar, 0, 3, 4)); CORE_TEST_TRUE(map.tryEmplace(ar, 3, 4, 5)); CORE_TEST_TRUE(map.tryEmplace(ar, 20, 5, 6)); CORE_TEST_FALSE(map.tryEmplace(ar, 3, 6, 7)); CORE_TEST_FALSE(map.tryEmplace(ar, 20, 7, 8)); HashMapTest* a = map.search(0); HashMapTest* b = map.search(3); HashMapTest* c = map.search(20); if(CORE_TEST_NOT_NULL(a) && CORE_TEST_NOT_NULL(b) && CORE_TEST_NOT_NULL(c)) { CORE_TEST_EQUAL(HashMapTest(3, 4), *a); CORE_TEST_EQUAL(HashMapTest(4, 5), *b); CORE_TEST_EQUAL(HashMapTest(5, 6), *c); } } static void testRemove() { IntMap map; map.add(1, 3).add(2, 4).add(3, 5); CORE_TEST_TRUE(map.remove(2)); CORE_TEST_FALSE(map.remove(7)); int* a = map.search(1); int* b = map.search(2); int* c = map.search(3); CORE_TEST_NULL(b); if(CORE_TEST_NOT_NULL(a) && CORE_TEST_NOT_NULL(c)) { CORE_TEST_EQUAL(3, *a); CORE_TEST_EQUAL(5, *c); } } void Core::testHashMap(bool light) { testMap(light); testMap(light); testEmplace(); testEmplaceProbing(); testRemove(); }