/* * libfud * Copyright 2025 Dominick Allen * * Licensed 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 "fud_hash_map.hpp" #include "fud_string.hpp" #include "fud_vector.hpp" // #include "test_common.hpp" #include "gtest/gtest.h" namespace fud { Vector testStrings() { Vector stringList{Vector::withCapacity(9).takeOkay()}; fudAssert(stringList.pushBack(String::makeFromCString("foo").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("bar").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("baz").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("qux").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("Tom").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("Dick").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("Harry").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("Alice").takeOkay()) == FudStatus::Success); fudAssert(stringList.pushBack(String::makeFromCString("Bob").takeOkay()) == FudStatus::Success); return stringList; } Vector testStringViews() { Vector stringList{Vector::withCapacity(9).takeOkay()}; fudAssert(stringList.pushBack(StringView::makeFromCString("foo")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("bar")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("baz")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("qux")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("Tom")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("Dick")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("Harry")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("Alice")) == FudStatus::Success); fudAssert(stringList.pushBack(StringView::makeFromCString("Bob")) == FudStatus::Success); return stringList; } TEST(FudHash, InsertMoveKeyMoveValue) { auto stringList{testStrings()}; HashMap mapInt2String{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_FALSE(mapInt2String.contains(index)); EXPECT_FALSE(mapInt2String.contains(index * 1)); auto insertStatus = mapInt2String.insert(index * 1, String::from(stringList[index]).takeOkay()); EXPECT_EQ(insertStatus, FudStatus::Success); EXPECT_TRUE(mapInt2String.contains(index)); EXPECT_TRUE(mapInt2String.contains(index * 1)); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapInt2String.insert(index * 1, String::from(stringList[index]).takeOkay()); EXPECT_EQ(insertStatus, FudStatus::Exists); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); const String invalid{String::makeFromCString("Invalid").takeOkay()}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]); } } TEST(FudHash, InsertMoveKeyCopyValue) { auto stringList{testStrings()}; HashMap mapString2Int{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_FALSE(mapString2Int.contains(stringList[index])); auto insertStatus = mapString2Int.insert(String::from(stringList[index]).takeOkay(), index); EXPECT_TRUE(mapString2Int.contains(stringList[index])); EXPECT_EQ(insertStatus, FudStatus::Success); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapString2Int.insert(String::from(stringList[index]).takeOkay(), index); EXPECT_EQ(insertStatus, FudStatus::Exists); } EXPECT_EQ(mapString2Int.size(), stringList.size()); EXPECT_GT(mapString2Int.capacity(), mapString2Int.size()); for (int index = 0; index < static_cast(stringList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapString2Int.getConstRef(stringList[index]).valueOr(invalid), index); } for (int index = 0; index < static_cast(stringList.size()); ++index) { int invalid = -1; int& refVal = mapString2Int.getRef(stringList[index]).mutValueOr(invalid); refVal *= 2; EXPECT_EQ(mapString2Int.getConstRef(stringList[index]).valueOr(invalid), index * 2); } } TEST(FudHash, InsertCopyKeyMoveValue) { auto stringList{testStrings()}; HashMap mapInt2String{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapInt2String.insert(index, String::from(stringList[index]).takeOkay()); EXPECT_EQ(insertStatus, FudStatus::Success); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapInt2String.insert(index, String::from(stringList[index]).takeOkay()); EXPECT_EQ(insertStatus, FudStatus::Exists); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); const String invalid{String::makeFromCString("Invalid").takeOkay()}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]); } } TEST(FudHash, InsertCopyKeyCopyValue) { auto stringViewList{testStringViews()}; HashMap mapView2Int{}; for (int index = 0; index < static_cast(stringViewList.size()); ++index) { auto insertStatus = mapView2Int.insert(stringViewList[index], index); EXPECT_EQ(insertStatus, FudStatus::Success); } for (int index = 0; index < static_cast(stringViewList.size()); ++index) { auto insertStatus = mapView2Int.insert(stringViewList[index], index); EXPECT_EQ(insertStatus, FudStatus::Exists); } EXPECT_EQ(mapView2Int.size(), stringViewList.size()); EXPECT_GT(mapView2Int.capacity(), mapView2Int.size()); for (int index = 0; index < static_cast(stringViewList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapView2Int.getConstRef(stringViewList[index]).valueOr(invalid), index); } } TEST(FudHash, UpdateMoveKeyMoveValue) { auto stringList{testStrings()}; HashMap mapInt2String{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto updateStatus = mapInt2String.update(index * 1, String::from(stringList[index]).takeOkay()); EXPECT_EQ(updateStatus, FudStatus::Success); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); const String invalid{String::makeFromCString("Invalid").takeOkay()}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto updateStatus = mapInt2String.update( index * 1, String::from(stringList[stringList.size() - (index + 1)]).takeOkay()); EXPECT_EQ(updateStatus, FudStatus::Success); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[stringList.size() - (index + 1)]); } } TEST(FudHash, UpdateMoveKeyCopyValue) { auto stringList{testStrings()}; HashMap mapString2Int{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto updateStatus = mapString2Int.update(String::from(stringList[index]).takeOkay(), index); EXPECT_EQ(updateStatus, FudStatus::Success); } EXPECT_EQ(mapString2Int.size(), stringList.size()); EXPECT_GT(mapString2Int.capacity(), mapString2Int.size()); for (int index = 0; index < static_cast(stringList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapString2Int.getConstRef(stringList[index]).valueOr(invalid), index); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto value = index * 2; auto updateStatus = mapString2Int.update(String::from(stringList[index]).takeOkay(), value); EXPECT_EQ(updateStatus, FudStatus::Success); } for (int index = 0; index < static_cast(stringList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapString2Int.getConstRef(stringList[index]).valueOr(invalid), index * 2); } } TEST(FudHash, UpdateCopyKeyMoveValue) { auto stringList{testStrings()}; HashMap mapInt2String{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto updateStatus = mapInt2String.update(index, String::from(stringList[index]).takeOkay()); EXPECT_EQ(updateStatus, FudStatus::Success); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); const String invalid{String::makeFromCString("Invalid").takeOkay()}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto updateStatus = mapInt2String.update( index, String::from(stringList[stringList.size() - (index + 1)]).takeOkay()); EXPECT_EQ(updateStatus, FudStatus::Success); } for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[stringList.size() - (index + 1)]); } } TEST(FudHash, UpdateCopyKeyCopyValue) { auto stringViewList{testStringViews()}; HashMap mapView2Int{}; for (int index = 0; index < static_cast(stringViewList.size()); ++index) { EXPECT_FALSE(mapView2Int.contains(stringViewList[index])); auto updateStatus = mapView2Int.update(stringViewList[index], index); EXPECT_TRUE(mapView2Int.contains(stringViewList[index])); EXPECT_EQ(updateStatus, FudStatus::Success); } EXPECT_EQ(mapView2Int.size(), stringViewList.size()); EXPECT_GT(mapView2Int.capacity(), mapView2Int.size()); for (int index = 0; index < static_cast(stringViewList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapView2Int.getConstRef(stringViewList[index]).valueOr(invalid), index); } for (int index = 0; index < static_cast(stringViewList.size()); ++index) { int updateValue = index * 2; auto updateStatus = mapView2Int.update(stringViewList[index], updateValue); EXPECT_EQ(updateStatus, FudStatus::Success); } EXPECT_EQ(mapView2Int.size(), stringViewList.size()); EXPECT_GT(mapView2Int.capacity(), mapView2Int.size()); for (int index = 0; index < static_cast(stringViewList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapView2Int.getConstRef(stringViewList[index]).valueOr(invalid), index * 2); } } TEST(FudHash, RemoveKeyConstRef) { auto stringList{testStrings()}; HashMap mapString2Int{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapString2Int.insert(String::from(stringList[index]).takeOkay(), index); EXPECT_EQ(insertStatus, FudStatus::Success); } EXPECT_EQ(mapString2Int.size(), stringList.size()); EXPECT_GT(mapString2Int.capacity(), mapString2Int.size()); for (int index = 0; index < static_cast(stringList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapString2Int.get(stringList[index]).valueOr(invalid), index); } for (const auto& entry : stringList) { EXPECT_TRUE(mapString2Int.contains(entry)); EXPECT_EQ(mapString2Int.remove(entry), FudStatus::Success); EXPECT_FALSE(mapString2Int.contains(entry)); } for (const auto& entry : stringList) { EXPECT_EQ(mapString2Int.remove(entry), FudStatus::NotFound); } for (const auto& entry : stringList) { const int invalid = -1; EXPECT_EQ(mapString2Int.get(entry).valueOr(invalid), invalid); } } TEST(FudHash, RemoveKeyMoveRef) { auto stringList{testStrings()}; HashMap mapInt2String{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapInt2String.insert(index, String::from(stringList[index]).takeOkay()); EXPECT_EQ(insertStatus, FudStatus::Success); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); const String invalid{String::makeFromCString("Invalid").takeOkay()}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]); } for (int index = 0; index < stringList.size(); ++index) { EXPECT_EQ(mapInt2String.remove(index * 1), FudStatus::Success); } for (int index = 0; index < stringList.size(); ++index) { EXPECT_EQ(mapInt2String.remove(index * 1), FudStatus::NotFound); } for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), invalid); } } TEST(FudHash, ExtractConstKey) { auto stringList{testStrings()}; HashMap mapString2Int{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapString2Int.insert(String::from(stringList[index]).takeOkay(), index); EXPECT_EQ(insertStatus, FudStatus::Success); } EXPECT_EQ(mapString2Int.size(), stringList.size()); EXPECT_GT(mapString2Int.capacity(), mapString2Int.size()); for (int index = 0; index < static_cast(stringList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapString2Int.get(stringList[index]).valueOr(invalid), index); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto valueOpt{mapString2Int.extract(stringList[index])}; ASSERT_TRUE(valueOpt.hasValue()); EXPECT_EQ(valueOpt.value(), index); } for (const auto& entry : stringList) { EXPECT_TRUE(mapString2Int.extract(entry).isNone()); } for (const auto& entry : stringList) { const int invalid = -1; EXPECT_EQ(mapString2Int.get(entry).valueOr(invalid), invalid); } } TEST(FudHash, ExtractMoveKey) { auto stringList{testStrings()}; HashMap mapInt2String{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapInt2String.insert(index, String::from(stringList[index]).takeOkay()); EXPECT_EQ(insertStatus, FudStatus::Success); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); const String invalid{String::makeFromCString("Invalid").takeOkay()}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto valueOpt{mapInt2String.extract(index * 1)}; ASSERT_TRUE(valueOpt.hasValue()); EXPECT_EQ(valueOpt.value(), stringList[index]); } for (int index = 0; index < stringList.size(); ++index) { EXPECT_TRUE(mapInt2String.extract(index * 1).isNone()); } for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), invalid); } } TEST(FudHash, ExtractPairConstKey) { auto stringList{testStrings()}; HashMap mapString2Int{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapString2Int.insert(String::from(stringList[index]).takeOkay(), index); EXPECT_EQ(insertStatus, FudStatus::Success); } EXPECT_EQ(mapString2Int.size(), stringList.size()); EXPECT_GT(mapString2Int.capacity(), mapString2Int.size()); for (int index = 0; index < static_cast(stringList.size()); ++index) { const int invalid = -1; EXPECT_EQ(mapString2Int.get(stringList[index]).valueOr(invalid), index); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto valueOpt{mapString2Int.extractPair(stringList[index])}; ASSERT_TRUE(valueOpt.hasValue()); EXPECT_EQ(valueOpt.value().m_key, stringList[index]); EXPECT_EQ(valueOpt.value().m_value, index); } for (const auto& entry : stringList) { EXPECT_TRUE(mapString2Int.extract(entry).isNone()); } for (const auto& entry : stringList) { const int invalid = -1; EXPECT_EQ(mapString2Int.get(entry).valueOr(invalid), invalid); } } TEST(FudHash, ExtractPairMoveKey) { auto stringList{testStrings()}; HashMap mapInt2String{}; for (int index = 0; index < static_cast(stringList.size()); ++index) { auto insertStatus = mapInt2String.insert(index, String::from(stringList[index]).takeOkay()); EXPECT_EQ(insertStatus, FudStatus::Success); } EXPECT_EQ(mapInt2String.size(), stringList.size()); EXPECT_GT(mapInt2String.capacity(), mapInt2String.size()); const String invalid{String::makeFromCString("Invalid").takeOkay()}; for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]); } for (int index = 0; index < static_cast(stringList.size()); ++index) { auto valueOpt{mapInt2String.extractPair(index * 1)}; ASSERT_TRUE(valueOpt.hasValue()); EXPECT_EQ(valueOpt.value().m_value, stringList[index]); EXPECT_EQ(valueOpt.value().m_key, index); } for (int index = 0; index < stringList.size(); ++index) { EXPECT_TRUE(mapInt2String.extract(index * 1).isNone()); } for (int index = 0; index < static_cast(stringList.size()); ++index) { EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), invalid); } } } // namespace fud