//===- llvm/unittest/ADT/RadixTreeTest.cpp --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/RadixTree.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include #include using namespace llvm; namespace { using ::testing::ElementsAre; using ::testing::ElementsAreArray; using ::testing::Pair; using ::testing::UnorderedElementsAre; // Test with StringRef. TEST(RadixTreeTest, Empty) { RadixTree T; EXPECT_TRUE(T.empty()); EXPECT_EQ(T.size(), 0u); EXPECT_TRUE(T.find_prefixes("").empty()); EXPECT_TRUE(T.find_prefixes("A").empty()); EXPECT_EQ(T.countNodes(), 1u); } TEST(RadixTreeTest, InsertEmpty) { RadixTree T; auto [It, IsNew] = T.emplace("", 4); EXPECT_TRUE(!T.empty()); EXPECT_EQ(T.size(), 1u); EXPECT_TRUE(IsNew); const auto &[K, V] = *It; EXPECT_TRUE(K.empty()); EXPECT_EQ(4, V); EXPECT_THAT(T, ElementsAre(Pair("", 4))); EXPECT_THAT(T.find_prefixes(""), ElementsAre(Pair("", 4))); EXPECT_THAT(T.find_prefixes("a"), ElementsAre(Pair("", 4))); EXPECT_EQ(T.countNodes(), 1u); } TEST(RadixTreeTest, Complex) { RadixTree T; T.emplace("abcd", 1); EXPECT_EQ(T.countNodes(), 2u); T.emplace("abklm", 2); EXPECT_EQ(T.countNodes(), 4u); T.emplace("123abklm", 3); EXPECT_EQ(T.countNodes(), 5u); T.emplace("123abklm", 4); EXPECT_EQ(T.countNodes(), 5u); T.emplace("ab", 5); EXPECT_EQ(T.countNodes(), 5u); T.emplace("1234567", 6); EXPECT_EQ(T.countNodes(), 7u); T.emplace("123456", 7); EXPECT_EQ(T.countNodes(), 8u); T.emplace("123456789", 8); EXPECT_EQ(T.countNodes(), 9u); EXPECT_THAT(T, UnorderedElementsAre(Pair("abcd", 1), Pair("abklm", 2), Pair("123abklm", 3), Pair("ab", 5), Pair("1234567", 6), Pair("123456", 7), Pair("123456789", 8))); EXPECT_THAT(T.find_prefixes("1234567890"), UnorderedElementsAre(Pair("1234567", 6), Pair("123456", 7), Pair("123456789", 8))); EXPECT_THAT(T.find_prefixes("123abklm"), UnorderedElementsAre(Pair("123abklm", 3))); EXPECT_THAT(T.find_prefixes("abcdefg"), UnorderedElementsAre(Pair("abcd", 1), Pair("ab", 5))); EXPECT_EQ(T.countNodes(), 9u); } TEST(RadixTreeTest, ValueWith2Parameters) { RadixTree> T; T.emplace("abcd", "a", 3); EXPECT_THAT(T, UnorderedElementsAre(Pair("abcd", Pair("a", 3)))); } // Test different types, less readable. template struct TestData { static const T Data1[]; static const T Data2[]; }; template <> const char TestData::Data1[] = "abcdedcba"; template <> const char TestData::Data2[] = "abCDEDCba"; template <> const int TestData::Data1[] = {1, 2, 3, 4, 5, 4, 3, 2, 1}; template <> const int TestData::Data2[] = {1, 2, 4, 8, 16, 8, 4, 2, 1}; template class RadixTreeTypeTest : public ::testing::Test { public: using IteratorType = decltype(adl_begin(std::declval())); using CharType = remove_cvref_t()))>; T make(const CharType *Data, size_t N) { return T(StringRef(Data, N)); } T make1(size_t N) { return make(TestData::Data1, N); } T make2(size_t N) { return make(TestData::Data2, N); } }; template <> iterator_range RadixTreeTypeTest>::make( const char *Data, size_t N) { return StringRef(Data).take_front(N); } template <> iterator_range RadixTreeTypeTest>::make( const char *Data, size_t N) { return reverse(StringRef(Data).take_back(N)); } template <> ArrayRef RadixTreeTypeTest>::make(const int *Data, size_t N) { return ArrayRef(Data, Data + N); } template <> std::vector RadixTreeTypeTest>::make(const int *Data, size_t N) { return std::vector(Data, Data + N); } template <> std::list RadixTreeTypeTest>::make(const int *Data, size_t N) { return std::list(Data, Data + N); } class TypeNameGenerator { public: template static std::string GetName(int) { if (std::is_same_v) return "StringRef"; if (std::is_same_v) return "string"; if (std::is_same_v>) return "iterator_range"; if (std::is_same_v>) return "reverse_iterator_range"; if (std::is_same_v>) return "ArrayRef"; if (std::is_same_v>) return "vector"; if (std::is_same_v>) return "list"; return "Unknown"; } }; using TestTypes = ::testing::Types, iterator_range, ArrayRef, std::vector, std::list>; TYPED_TEST_SUITE(RadixTreeTypeTest, TestTypes, TypeNameGenerator); TYPED_TEST(RadixTreeTypeTest, Helpers) { for (size_t i = 0; i < 9; ++i) { auto R1 = this->make1(i); auto R2 = this->make2(i); EXPECT_EQ(llvm::range_size(R1), i); EXPECT_EQ(llvm::range_size(R2), i); auto [I1, I2] = llvm::mismatch(R1, R2); // Exactly 2 first elements of Data1 and Data2 must match. EXPECT_EQ(std::distance(R1.begin(), I1), std::min(2, i)); } } TYPED_TEST(RadixTreeTypeTest, Empty) { RadixTree T; EXPECT_TRUE(T.empty()); EXPECT_EQ(T.size(), 0u); EXPECT_TRUE(T.find_prefixes(this->make1(0)).empty()); EXPECT_TRUE(T.find_prefixes(this->make2(1)).empty()); EXPECT_EQ(T.countNodes(), 1u); } TYPED_TEST(RadixTreeTypeTest, InsertEmpty) { using TreeType = RadixTree; TreeType T; auto [It, IsNew] = T.emplace(this->make1(0), 5); EXPECT_TRUE(!T.empty()); EXPECT_EQ(T.size(), 1u); EXPECT_TRUE(IsNew); const auto &[K, V] = *It; EXPECT_TRUE(K.empty()); EXPECT_EQ(5, V); EXPECT_THAT(T.find_prefixes(this->make1(0)), ElementsAre(Pair(ElementsAre(), 5))); EXPECT_THAT(T.find_prefixes(this->make2(1)), ElementsAre(Pair(ElementsAre(), 5))); EXPECT_THAT(T, ElementsAre(Pair(ElementsAre(), 5))); EXPECT_EQ(T.countNodes(), 1u); } TYPED_TEST(RadixTreeTypeTest, InsertEmptyTwice) { using TreeType = RadixTree; TreeType T; T.emplace(this->make1(0), 5); auto [It, IsNew] = T.emplace(this->make1(0), 6); EXPECT_TRUE(!T.empty()); EXPECT_EQ(T.size(), 1u); EXPECT_TRUE(!IsNew); const auto &[K, V] = *It; EXPECT_TRUE(K.empty()); EXPECT_EQ(5, V); EXPECT_THAT(T.find_prefixes(this->make1(0)), ElementsAre(Pair(ElementsAre(), 5))); EXPECT_THAT(T.find_prefixes(this->make2(1)), ElementsAre(Pair(ElementsAre(), 5))); EXPECT_THAT(T, ElementsAre(Pair(ElementsAre(), 5))); EXPECT_EQ(T.countNodes(), 1u); } TYPED_TEST(RadixTreeTypeTest, InsertOne) { using TreeType = RadixTree; TreeType T; auto [It, IsNew] = T.emplace(this->make1(1), 4); EXPECT_TRUE(!T.empty()); EXPECT_EQ(T.size(), 1u); EXPECT_TRUE(IsNew); const auto &[K, V] = *It; EXPECT_THAT(K, ElementsAreArray(this->make1(1))); EXPECT_EQ(4, V); EXPECT_THAT(T, ElementsAre(Pair(ElementsAreArray(this->make1(1)), 4))); EXPECT_THAT(T.find_prefixes(this->make1(1)), ElementsAre(Pair(ElementsAreArray(this->make1(1)), 4))); EXPECT_THAT(T.find_prefixes(this->make1(2)), ElementsAre(Pair(ElementsAreArray(this->make1(1)), 4))); EXPECT_EQ(T.countNodes(), 2u); } TYPED_TEST(RadixTreeTypeTest, InsertOneTwice) { using TreeType = RadixTree; TreeType T; T.emplace(this->make1(1), 4); auto [It, IsNew] = T.emplace(this->make1(1), 4); EXPECT_TRUE(!T.empty()); EXPECT_EQ(T.size(), 1u); EXPECT_TRUE(!IsNew); EXPECT_THAT(T, ElementsAre(Pair(ElementsAreArray(this->make1(1)), 4))); EXPECT_EQ(T.countNodes(), 2u); } TYPED_TEST(RadixTreeTypeTest, InsertSuperStrings) { using TreeType = RadixTree; TreeType T; for (size_t Len = 0; Len < 7; Len += 2) { auto [It, IsNew] = T.emplace(this->make1(Len), Len); EXPECT_TRUE(IsNew); } EXPECT_THAT(T, UnorderedElementsAre(Pair(ElementsAreArray(this->make1(0)), 0), Pair(ElementsAreArray(this->make1(2)), 2), Pair(ElementsAreArray(this->make1(4)), 4), Pair(ElementsAreArray(this->make1(6)), 6))); EXPECT_THAT(T.find_prefixes(this->make1(0)), UnorderedElementsAre(Pair(ElementsAreArray(this->make1(0)), 0))); EXPECT_THAT(T.find_prefixes(this->make1(3)), UnorderedElementsAre(Pair(ElementsAreArray(this->make1(0)), 0), Pair(ElementsAreArray(this->make1(2)), 2))); EXPECT_THAT(T.find_prefixes(this->make1(7)), UnorderedElementsAre(Pair(ElementsAreArray(this->make1(0)), 0), Pair(ElementsAreArray(this->make1(2)), 2), Pair(ElementsAreArray(this->make1(4)), 4), Pair(ElementsAreArray(this->make1(6)), 6))); EXPECT_EQ(T.countNodes(), 4u); } TYPED_TEST(RadixTreeTypeTest, InsertSubStrings) { using TreeType = RadixTree; TreeType T; for (size_t Len = 0; Len < 7; Len += 2) { auto [It, IsNew] = T.emplace(this->make1(7 - Len), 7 - Len); EXPECT_TRUE(IsNew); } EXPECT_THAT(T, UnorderedElementsAre(Pair(ElementsAreArray(this->make1(1)), 1), Pair(ElementsAreArray(this->make1(3)), 3), Pair(ElementsAreArray(this->make1(5)), 5), Pair(ElementsAreArray(this->make1(7)), 7))); EXPECT_THAT(T.find_prefixes(this->make1(0)), UnorderedElementsAre()); EXPECT_THAT(T.find_prefixes(this->make1(3)), UnorderedElementsAre(Pair(ElementsAreArray(this->make1(1)), 1), Pair(ElementsAreArray(this->make1(3)), 3))); EXPECT_THAT(T.find_prefixes(this->make1(6)), UnorderedElementsAre(Pair(ElementsAreArray(this->make1(1)), 1), Pair(ElementsAreArray(this->make1(3)), 3), Pair(ElementsAreArray(this->make1(5)), 5))); EXPECT_EQ(T.countNodes(), 5u); } TYPED_TEST(RadixTreeTypeTest, InsertVShape) { using TreeType = RadixTree; TreeType T; EXPECT_EQ(T.countNodes(), 1u); T.emplace(this->make1(5), 15); EXPECT_EQ(T.countNodes(), 2u); T.emplace(this->make2(6), 26); EXPECT_EQ(T.countNodes(), 4u); T.emplace(this->make2(1), 21); EXPECT_EQ(T.countNodes(), 5u); EXPECT_THAT(T, UnorderedElementsAre(Pair(ElementsAreArray(this->make1(5)), 15), Pair(ElementsAreArray(this->make2(6)), 26), Pair(ElementsAreArray(this->make2(1)), 21))); EXPECT_THAT(T.find_prefixes(this->make1(7)), UnorderedElementsAre(Pair(ElementsAreArray(this->make2(1)), 21), Pair(ElementsAreArray(this->make1(5)), 15))); EXPECT_THAT(T.find_prefixes(this->make2(7)), UnorderedElementsAre(Pair(ElementsAreArray(this->make2(1)), 21), Pair(ElementsAreArray(this->make2(6)), 26))); EXPECT_EQ(T.countNodes(), 5u); } } // namespace