From cb9fa588ba8144fcdd52ba4b83d69d93fb18066f Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Sun, 30 Mar 2025 23:08:43 -0500 Subject: Add hash map. --- test/.clang-tidy | 2 +- test/CMakeLists.txt | 1 + test/test_directory.cpp | 4 +- test/test_file.cpp | 4 -- test/test_format.cpp | 160 ++++++++++++++++++++++++++++-------------------- test/test_hash_map.cpp | 146 +++++++++++++++++++++++++++++++++++++++++++ test/test_option.cpp | 8 +++ test/test_vector.cpp | 32 +++++++--- 8 files changed, 278 insertions(+), 79 deletions(-) create mode 100644 test/test_hash_map.cpp (limited to 'test') diff --git a/test/.clang-tidy b/test/.clang-tidy index 97da183..0b184c0 100644 --- a/test/.clang-tidy +++ b/test/.clang-tidy @@ -1,2 +1,2 @@ -Checks: -readability-function-cognitive-complexity +Checks: -readability-function-cognitive-complexity,-readability-magic-numbers InheritParentConfig: true diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cdc8c6e..9b905c9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -67,6 +67,7 @@ fud_add_test(test_csv SOURCES test_csv.cpp) # fud_add_test(test_c_file SOURCES test_c_file.cpp) fud_add_test(test_directory SOURCES test_directory.cpp) fud_add_test(test_file SOURCES test_file.cpp) +fud_add_test(test_hash_map SOURCES test_hash_map.cpp) fud_add_test(test_format SOURCES test_format.cpp) fud_add_test(test_option SOURCES test_option.cpp) fud_add_test(test_result SOURCES test_result.cpp) diff --git a/test/test_directory.cpp b/test/test_directory.cpp index 7a420ca..905e503 100644 --- a/test/test_directory.cpp +++ b/test/test_directory.cpp @@ -81,7 +81,7 @@ TEST(FudDirectory, Basic) DirectoryEntry{String::from(files[0]).takeOkay(), 0, files[0].size(), 1, 0, DirectoryEntryType::RegularFile}, DirectoryEntry{String::from(files[1]).takeOkay(), 0, files[1].size(), 1, 0, DirectoryEntryType::RegularFile}, }; - ASSERT_TRUE(expectedFiles[0].name.compare(expectedFiles[0].name)); + ASSERT_EQ(expectedFiles[0].name, expectedFiles[0].name); for (auto idx = 0; idx < expectedFiles.size(); ++idx) { debugPrint(u8"On iteration {} - '{}' \n", idx, expectedFiles[idx].name.asView()); @@ -95,7 +95,7 @@ TEST(FudDirectory, Basic) const auto* expected = std::ranges::find_if( expectedFiles, [&dirEntry](const DirectoryEntry& entry) { - return entry.name.compare(dirEntry.name) && entry.entryType == dirEntry.entryType; + return entry.name == dirEntry.name && entry.entryType == dirEntry.entryType; }); EXPECT_NE(expected, nullptr); EXPECT_NE(expected, expectedFiles.end()); diff --git a/test/test_file.cpp b/test/test_file.cpp index c1ce4f1..878c740 100644 --- a/test/test_file.cpp +++ b/test/test_file.cpp @@ -25,8 +25,6 @@ #include #include -// NOLINTBEGIN(readability-magic-numbers) - namespace fud { TEST(FudFile, Basic) @@ -169,5 +167,3 @@ TEST(FudBufferedFile, OpenReadWrite) } } // namespace fud - -// NOLINTEND(readability-magic-numbers) diff --git a/test/test_format.cpp b/test/test_format.cpp index 738b551..0c5c520 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -17,10 +17,13 @@ #include "fud_format.hpp" #include "fud_string_view.hpp" +#include "fud_print.hpp" #include "gtest/gtest.h" #include +// NOLINTBEGIN(readability-magic-numbers) + namespace fud { TEST(FormatTest, BasePositionalTest) @@ -506,27 +509,27 @@ TEST(FormatTest, OneBoolFormatTest) TEST(FormatTest, OneFloatFormatUnspecifiedTest) { -/* - String sink{}; - - ASSERT_EQ(sink.clear(), FudStatus::Success); - auto expected = std::format("{:}", 42.0); - auto formatResult = format(sink, FormatCharMode::Unchecked, u8"{:}", 42.0); - EXPECT_TRUE(formatResult.isOkay()); - EXPECT_STREQ(sink.c_str(), expected.c_str()); - - ASSERT_EQ(sink.clear(), FudStatus::Success); - expected = std::format("{:1.0}", 42.0); - formatResult = format(sink, FormatCharMode::Unchecked, u8"{:1.0}", 42.0); - EXPECT_TRUE(formatResult.isOkay()); - EXPECT_STREQ(sink.c_str(), expected.c_str()); - expected = std::format("{:1.0}", 42.0); - expected = std::format("u {:}", 10.0); - expected = std::format("u {:}", 100.0); - expected = std::format("u {:}", 1000.0); - expected = std::format("u {:}", 10000.0); - expected = std::format("u {:}", 100000.0); -*/ + /* + String sink{}; + + ASSERT_EQ(sink.clear(), FudStatus::Success); + auto expected = std::format("{:}", 42.0); + auto formatResult = format(sink, FormatCharMode::Unchecked, u8"{:}", 42.0); + EXPECT_TRUE(formatResult.isOkay()); + EXPECT_STREQ(sink.c_str(), expected.c_str()); + + ASSERT_EQ(sink.clear(), FudStatus::Success); + expected = std::format("{:1.0}", 42.0); + formatResult = format(sink, FormatCharMode::Unchecked, u8"{:1.0}", 42.0); + EXPECT_TRUE(formatResult.isOkay()); + EXPECT_STREQ(sink.c_str(), expected.c_str()); + expected = std::format("{:1.0}", 42.0); + expected = std::format("u {:}", 10.0); + expected = std::format("u {:}", 100.0); + expected = std::format("u {:}", 1000.0); + expected = std::format("u {:}", 10000.0); + expected = std::format("u {:}", 100000.0); + */ } TEST(FormatTest, OneFloatFormatScientificTest) @@ -648,50 +651,50 @@ TEST(FormatTest, OneFloatFormatScientificTest) // Which is: "E#+ +0003.0000000000000000000000000000000000E+00" // Which is: "E#+ +0003.00000000000000000000000000000000000000000000000000E+00" -/* - expected = std::format("E#+ {:Z>+#060.30E}", val); - ASSERT_EQ(sink.clear(), FudStatus::Success); - formatResult = format(sink, FormatCharMode::Unchecked, u8"E#+ {:Z>+#060.30E}", val); - EXPECT_TRUE(formatResult.isOkay()); - EXPECT_STREQ(sink.c_str(), expected.c_str()); - - expected = std::format("E#+ {:Z<+#060.30E}", val); - ASSERT_EQ(sink.clear(), FudStatus::Success); - formatResult = format(sink, FormatCharMode::Unchecked, u8"E#+ {:Z<+#060.30E}", val); - EXPECT_TRUE(formatResult.isOkay()); - EXPECT_STREQ(sink.c_str(), expected.c_str()); - - expected = std::format("E#+ {:Z^+#060.30E}", val); - ASSERT_EQ(sink.clear(), FudStatus::Success); - formatResult = format(sink, FormatCharMode::Unchecked, u8"E#+ {:Z^+#060.30E}", val); - EXPECT_TRUE(formatResult.isOkay()); - EXPECT_STREQ(sink.c_str(), expected.c_str()); -*/ - -/* - double val = 3.0; - expected = std::format("u {:}", val); - expected = std::format("f {:f}", val); - expected = std::format("F {:F}", val); - expected = std::format("a {:a}", val); - expected = std::format("A {:A}", val); - expected = std::format("g {:g}", val); - expected = std::format("G {:G}", val); - expected = std::format("u# {:#}", val); - expected = std::format("f# {:#f}", val); - expected = std::format("F# {:#F}", val); - expected = std::format("a# {:#a}", val); - expected = std::format("A# {:#A}", val); - expected = std::format("g# {:#g}", val); - expected = std::format("G# {:#G}", val); - expected = std::format("u#+ {:+#}", val); - expected = std::format("f#+ {:+#f}", val); - expected = std::format("F#+ {:+#F}", val); - expected = std::format("a#+ {:+#a}", val); - expected = std::format("A#+ {:+#A}", val); - expected = std::format("g#+ {:+#g}", val); - expected = std::format("G#+ {:+#010.3G}", val); -*/ + /* + expected = std::format("E#+ {:Z>+#060.30E}", val); + ASSERT_EQ(sink.clear(), FudStatus::Success); + formatResult = format(sink, FormatCharMode::Unchecked, u8"E#+ {:Z>+#060.30E}", val); + EXPECT_TRUE(formatResult.isOkay()); + EXPECT_STREQ(sink.c_str(), expected.c_str()); + + expected = std::format("E#+ {:Z<+#060.30E}", val); + ASSERT_EQ(sink.clear(), FudStatus::Success); + formatResult = format(sink, FormatCharMode::Unchecked, u8"E#+ {:Z<+#060.30E}", val); + EXPECT_TRUE(formatResult.isOkay()); + EXPECT_STREQ(sink.c_str(), expected.c_str()); + + expected = std::format("E#+ {:Z^+#060.30E}", val); + ASSERT_EQ(sink.clear(), FudStatus::Success); + formatResult = format(sink, FormatCharMode::Unchecked, u8"E#+ {:Z^+#060.30E}", val); + EXPECT_TRUE(formatResult.isOkay()); + EXPECT_STREQ(sink.c_str(), expected.c_str()); + */ + + /* + double val = 3.0; + expected = std::format("u {:}", val); + expected = std::format("f {:f}", val); + expected = std::format("F {:F}", val); + expected = std::format("a {:a}", val); + expected = std::format("A {:A}", val); + expected = std::format("g {:g}", val); + expected = std::format("G {:G}", val); + expected = std::format("u# {:#}", val); + expected = std::format("f# {:#f}", val); + expected = std::format("F# {:#F}", val); + expected = std::format("a# {:#a}", val); + expected = std::format("A# {:#A}", val); + expected = std::format("g# {:#g}", val); + expected = std::format("G# {:#G}", val); + expected = std::format("u#+ {:+#}", val); + expected = std::format("f#+ {:+#f}", val); + expected = std::format("F#+ {:+#F}", val); + expected = std::format("a#+ {:+#a}", val); + expected = std::format("A#+ {:+#A}", val); + expected = std::format("g#+ {:+#g}", val); + expected = std::format("G#+ {:+#010.3G}", val); + */ } TEST(FormatTest, TwoArgFormatTest) @@ -716,6 +719,33 @@ TEST(FormatTest, StringView) auto formatResult = format(sink, FormatCharMode::Unchecked, u8"Test {}", StringView{u8"Hello, World!"}); EXPECT_TRUE(formatResult.isOkay()); EXPECT_STREQ(sink.c_str(), expected.c_str()); + + ASSERT_EQ(sink.clear(), FudStatus::Success); + formatResult = fud::format( + sink, + fud::FormatCharMode::Unchecked, + u8"CREATE TABLE IF NOT EXISTS {} ({} {} {});", + StringView{u8"AtomicSymbol"}.as_string_view(), + StringView{u8"AtomicNumber INTEGER PRIMARY KEY"}.as_string_view(), + StringView{u8"Symbol TEXT NOT NULL"}.as_string_view(), + StringView{u8"Name TEXT NOT NULL"}.as_string_view()); + expected = std::format( + "CREATE TABLE IF NOT EXISTS {} ({} {} {});", + StringView{u8"AtomicSymbol"}.as_string_view(), + StringView{u8"AtomicNumber INTEGER PRIMARY KEY"}.as_string_view(), + StringView{u8"Symbol TEXT NOT NULL"}.as_string_view(), + StringView{u8"Name TEXT NOT NULL"}.as_string_view()); + EXPECT_TRUE(formatResult.isOkay()); + EXPECT_STREQ(sink.c_str(), expected.c_str()); +} + +TEST(FormatTest, NumArgs) +{ + String sink{}; + auto formatResult = format(sink, FormatCharMode::Unchecked, u8"Test {} {} {}", StringView{u8"Hello, World!"}); + ASSERT_EQ(formatResult.status, FudStatus::FormatInvalid); } } // namespace fud + +// NOLINTEND(readability-magic-numbers) diff --git a/test/test_hash_map.cpp b/test/test_hash_map.cpp new file mode 100644 index 0000000..00c4693 --- /dev/null +++ b/test/test_hash_map.cpp @@ -0,0 +1,146 @@ +/* + * 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 { + +TEST(FudHash, InsertMoveKeyMoveValue) +{ + auto stringList{ + Vector::from( + NullOpt, + String::makeFromCString("foo").takeOkay(), + String::makeFromCString("bar").takeOkay(), + String::makeFromCString("baz").takeOkay(), + String::makeFromCString("qux").takeOkay(), + String::makeFromCString("Tom").takeOkay(), + String::makeFromCString("Dick").takeOkay(), + String::makeFromCString("Harry").takeOkay(), + String::makeFromCString("Alice").takeOkay(), + String::makeFromCString("Bob").takeOkay()) + .takeOkay()}; + HashMap mapInt2String{}; + 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::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]); + } +} + +TEST(FudHash, InsertMoveKeyCopyValue) +{ + auto stringList{ + Vector::from( + NullOpt, + String::makeFromCString("foo").takeOkay(), + String::makeFromCString("bar").takeOkay(), + String::makeFromCString("baz").takeOkay(), + String::makeFromCString("qux").takeOkay(), + String::makeFromCString("Tom").takeOkay(), + String::makeFromCString("Dick").takeOkay(), + String::makeFromCString("Harry").takeOkay(), + String::makeFromCString("Alice").takeOkay(), + String::makeFromCString("Bob").takeOkay()) + .takeOkay()}; + HashMap mapString2Int{}; + for (int index = 0; index < static_cast(stringList.size()); ++index) { + auto insertStatus = mapString2Int.insert(String::from(stringList[index]).takeOkay(), index * 1); + 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.getConstRef(stringList[index]).valueOr(invalid), index); + } +} + +TEST(FudHash, InsertCopyKeyMoveValue) +{ + auto stringList{ + Vector::from( + NullOpt, + String::makeFromCString("foo").takeOkay(), + String::makeFromCString("bar").takeOkay(), + String::makeFromCString("baz").takeOkay(), + String::makeFromCString("qux").takeOkay(), + String::makeFromCString("Tom").takeOkay(), + String::makeFromCString("Dick").takeOkay(), + String::makeFromCString("Harry").takeOkay(), + String::makeFromCString("Alice").takeOkay(), + String::makeFromCString("Bob").takeOkay()) + .takeOkay()}; + 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]); + } +} + +TEST(FudHash, InsertCopyKeyCopyValue) +{ + auto stringList{ + Vector::from( + NullOpt, + StringView::makeFromCString("foo"), + StringView::makeFromCString("bar"), + StringView::makeFromCString("baz"), + StringView::makeFromCString("qux"), + StringView::makeFromCString("Tom"), + StringView::makeFromCString("Dick"), + StringView::makeFromCString("Harry"), + StringView::makeFromCString("Alice"), + StringView::makeFromCString("Bob")) + .takeOkay()}; + HashMap mapView2Int{}; + for (int index = 0; index < static_cast(stringList.size()); ++index) { + auto insertStatus = mapView2Int.insert(stringList[index], index); + EXPECT_EQ(insertStatus, FudStatus::Success); + } + + EXPECT_EQ(mapView2Int.size(), stringList.size()); + EXPECT_GT(mapView2Int.capacity(), mapView2Int.size()); + + for (int index = 0; index < static_cast(stringList.size()); ++index) { + const int invalid = -1; + EXPECT_EQ(mapView2Int.getConstRef(stringList[index]).valueOr(invalid), index); + } +} + +} // namespace fud diff --git a/test/test_option.cpp b/test/test_option.cpp index a503a5f..b900858 100644 --- a/test/test_option.cpp +++ b/test/test_option.cpp @@ -16,6 +16,7 @@ */ #include "fud_option.hpp" +#include "fud_string.hpp" #include "gtest/gtest.h" @@ -51,4 +52,11 @@ TEST(OptionTest, OptionRef) ASSERT_EQ(value, 42); } +TEST(OptionTest, OptionString) +{ + Option optString{std::move(String::makeFromCString("foo").takeOkay())}; + EXPECT_TRUE(optString.hasValue()); +} + + } // namespace fud diff --git a/test/test_vector.cpp b/test/test_vector.cpp index ba0272e..3f4e584 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -24,9 +24,14 @@ namespace fud { template struct TestLinearAllocator : public Allocator { - virtual ~TestLinearAllocator() override = default; - - virtual Result allocate(size_t bytes, size_t alignment = alignof(std::max_align_t)) override + TestLinearAllocator() = default; + TestLinearAllocator(const TestLinearAllocator&) = delete; + TestLinearAllocator(TestLinearAllocator&&) = delete; + TestLinearAllocator& operator=(const TestLinearAllocator&) = delete; + TestLinearAllocator& operator=(TestLinearAllocator&&) = delete; + ~TestLinearAllocator() override = default; + + Result allocate(size_t bytes, size_t alignment = alignof(std::max_align_t)) override { auto allocIndex = m_next; if (allocIndex % alignment != 0) { @@ -39,13 +44,13 @@ struct TestLinearAllocator : public Allocator { return Okay{m_backing.data() + allocIndex}; } - virtual void deallocate(std::byte* pointer, size_t bytes) override + void deallocate(std::byte* pointer, size_t bytes) override { static_cast(pointer); static_cast(bytes); } - virtual bool isEqual(const Allocator& rhs) const override { + [[nodiscard]] bool isEqual(const Allocator& rhs) const override { return &rhs == static_cast(this); } @@ -82,7 +87,7 @@ struct NonTrivial { counter++; } NonTrivial(const NonTrivial&) = delete; - NonTrivial(NonTrivial&& rhs) : value{rhs.value}, destroyed{rhs.destroyed} + NonTrivial(NonTrivial&& rhs) noexcept : value{rhs.value}, destroyed{rhs.destroyed} { rhs.destroyed = true; } @@ -94,7 +99,7 @@ struct NonTrivial { } } NonTrivial& operator=(const NonTrivial& rhs) = delete; - NonTrivial& operator=(NonTrivial&& rhs) + NonTrivial& operator=(NonTrivial&& rhs) noexcept { value = rhs.value; destroyed = rhs.destroyed; @@ -183,4 +188,17 @@ TEST(VectorTest, NestedVector) // Result>, FudStatus> } +TEST(VectorTest, WithElements) +{ + auto vectorResult{Vector::from(NullOpt, 1, 2, 3, 42, -100)}; + ASSERT_TRUE(vectorResult.isOkay()); + auto intVector{vectorResult.takeOkay()}; + ASSERT_EQ(intVector.size(), 5); + ASSERT_EQ(intVector[0], 1); + ASSERT_EQ(intVector[1], 2); + ASSERT_EQ(intVector[2], 3); + ASSERT_EQ(intVector[3], 42); + ASSERT_EQ(intVector[4], -100); +} + } // namespace fud -- cgit v1.2.3