257 lines
5.3 KiB
C++
257 lines
5.3 KiB
C++
// SPDX-License-Identifier: BSD-2-Clause
|
|
// Copyright CM4all GmbH
|
|
// author: Max Kellermann <mk@cm4all.com>
|
|
|
|
#include "util/IntrusiveTreeSet.hxx"
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <cstdlib> // for std::rand()
|
|
#include <deque>
|
|
#include <string>
|
|
|
|
namespace {
|
|
|
|
struct IntItem final : IntrusiveTreeSetHook<IntrusiveHookMode::TRACK> {
|
|
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<IntItem,
|
|
IntrusiveTreeSetOperators<IntItem, IntItem::GetKey>> 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<int... values>
|
|
static constexpr auto
|
|
MakeIntItems(std::integer_sequence<int, values...>) noexcept
|
|
-> std::array<IntItem, sizeof...(values)>
|
|
{
|
|
return {values...};
|
|
}
|
|
|
|
TEST(IntrusiveTreeSet, RandomOrder)
|
|
{
|
|
auto items = MakeIntItems(std::make_integer_sequence<int, 32>());
|
|
|
|
IntrusiveTreeSet<IntItem,
|
|
IntrusiveTreeSetOperators<IntItem, IntItem::GetKey>> 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<std::unique_ptr<IntItem>> items;
|
|
IntrusiveTreeSet<IntItem,
|
|
IntrusiveTreeSetOperators<IntItem, IntItem::GetKey>> set;
|
|
|
|
std::srand(42);
|
|
for (unsigned i = 0; i < 1024; ++i) {
|
|
items.emplace_back(std::make_unique<IntItem>(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());
|
|
}
|