// SPDX-License-Identifier: BSD-2-Clause // Copyright CM4all GmbH // author: Max Kellermann #include "util/IntrusiveTreeSet.hxx" #include #include // for std::rand() #include #include namespace { struct IntItem final : IntrusiveTreeSetHook { int value; IntItem(int _value) noexcept:value(_value) {} struct GetKey { constexpr int operator()(const IntItem &item) const noexcept { return item.value; } }; }; } // anonymous namespace TEST(IntrusiveTreeSet, Basic) { IntItem a{1}, b{2}, c{3}, d{4}, e{5}, f{1}; IntrusiveTreeSet> set; EXPECT_EQ(set.size(), 0U); EXPECT_TRUE(set.empty()); EXPECT_FALSE(a.is_linked()); EXPECT_FALSE(b.is_linked()); set.insert(b); EXPECT_FALSE(a.is_linked()); EXPECT_TRUE(b.is_linked()); EXPECT_EQ(set.size(), 1U); EXPECT_EQ(set.find(2), set.iterator_to(b)); EXPECT_EQ(&set.front(), &b); set.insert(a); EXPECT_EQ(&set.front(), &a); EXPECT_TRUE(a.is_linked()); EXPECT_TRUE(b.is_linked()); set.insert(c); EXPECT_EQ(&set.front(), &a); EXPECT_EQ(set.size(), 3U); EXPECT_NE(set.find(3), set.end()); EXPECT_EQ(set.find(3), set.iterator_to(c)); EXPECT_EQ(set.find(4), set.end()); EXPECT_TRUE(c.is_linked()); set.erase(set.iterator_to(c)); EXPECT_FALSE(c.is_linked()); EXPECT_EQ(set.size(), 2U); EXPECT_EQ(set.find(3), set.end()); EXPECT_EQ(&set.front(), &a); set.insert(c); set.insert(d); set.insert(e); EXPECT_EQ(set.size(), 5U); EXPECT_EQ(&set.front(), &a); EXPECT_EQ(set.find(1), set.iterator_to(a)); EXPECT_EQ(set.find(2), set.iterator_to(b)); EXPECT_EQ(set.find(3), set.iterator_to(c)); EXPECT_EQ(set.find(4), set.iterator_to(d)); EXPECT_EQ(set.find(5), set.iterator_to(e)); EXPECT_TRUE(a.is_linked()); EXPECT_FALSE(f.is_linked()); set.erase(set.iterator_to(a)); EXPECT_FALSE(a.is_linked()); EXPECT_FALSE(f.is_linked()); EXPECT_EQ(set.find(1), set.end()); EXPECT_EQ(set.size(), 4U); EXPECT_EQ(&set.front(), &b); set.insert(f); EXPECT_FALSE(a.is_linked()); EXPECT_TRUE(f.is_linked()); EXPECT_EQ(set.find(1), set.iterator_to(f)); EXPECT_EQ(set.size(), 5U); EXPECT_EQ(&set.front(), &f); set.pop_front(); EXPECT_FALSE(f.is_linked()); set.clear_and_dispose([](auto *i){ i->value = -1; }); EXPECT_EQ(set.size(), 0U); EXPECT_TRUE(set.empty()); EXPECT_EQ(a.value, 1); EXPECT_EQ(b.value, -1); EXPECT_EQ(c.value, -1); EXPECT_EQ(d.value, -1); EXPECT_EQ(e.value, -1); EXPECT_EQ(f.value, 1); } template static constexpr auto MakeIntItems(std::integer_sequence) noexcept -> std::array { return {values...}; } TEST(IntrusiveTreeSet, RandomOrder) { auto items = MakeIntItems(std::make_integer_sequence()); IntrusiveTreeSet> set; set.insert(items[0]); set.insert(items[5]); set.insert(items[10]); set.insert(items[15]); set.insert(items[20]); set.insert(items[25]); set.insert(items[30]); set.insert(items[1]); set.insert(items[2]); set.insert(items[3]); set.insert(items[31]); set.insert(items[4]); set.insert(items[6]); set.insert(items[7]); set.insert(items[21]); set.insert(items[22]); set.insert(items[23]); set.insert(items[24]); set.insert(items[26]); set.insert(items[8]); set.insert(items[9]); set.insert(items[11]); set.insert(items[12]); set.insert(items[13]); set.insert(items[14]); set.insert(items[27]); set.insert(items[28]); set.insert(items[29]); set.insert(items[16]); set.insert(items[17]); set.insert(items[18]); set.insert(items[19]); EXPECT_EQ(set.size(), items.size()); for (const auto &i : items) { EXPECT_TRUE(i.is_linked()); } int expected = 0; for (const auto &i : set) { EXPECT_EQ(i.value, expected++); } for (std::size_t remove = 0; remove < items.size(); ++remove) { EXPECT_TRUE(items[remove].is_linked()); set.pop_front(); EXPECT_FALSE(items[remove].is_linked()); #ifndef NDEBUG set.Check(); #endif expected = remove + 1; for (const auto &i : set) { EXPECT_EQ(i.value, expected++); } } } TEST(IntrusiveTreeSet, LargeRandom) { std::deque> items; IntrusiveTreeSet> set; std::srand(42); for (unsigned i = 0; i < 1024; ++i) { items.emplace_back(std::make_unique(std::rand())); set.insert(*items.back()); #ifndef NDEBUG set.Check(); #endif } std::sort(items.begin(), items.end(), [](const auto &a, const auto &b){ return a->value < b->value; }); std::size_t idx = 0; for (const auto &i : set) { EXPECT_EQ(i.value, items[idx++]->value); } while (!set.empty()) { EXPECT_FALSE(items.empty()); EXPECT_TRUE(items.front()->is_linked()); EXPECT_EQ(items.front()->value, set.front().value); // erase the front element set.pop_front(); EXPECT_FALSE(items.front()->is_linked()); items.pop_front(); #ifndef NDEBUG set.Check(); #endif // erase a random element const auto r = std::next(items.begin(), std::rand() % items.size()); EXPECT_TRUE((*r)->is_linked()); set.erase(set.iterator_to(**r)); EXPECT_FALSE((*r)->is_linked()); items.erase(r); #ifndef NDEBUG set.Check(); #endif idx = 0; for (const auto &i : set) { EXPECT_EQ(i.value, items[idx++]->value); } } EXPECT_TRUE(items.empty()); }