Files
enduro2d/untests/sources/untests_utils/intrusive_list.cpp
2020-04-22 17:39:44 +07:00

475 lines
15 KiB
C++

/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#include "_utils.hpp"
using namespace e2d;
#include <random>
namespace
{
struct ilist_tag1 {};
struct ilist_tag2 {};
class obj_t
: public intrusive_list_hook<ilist_tag1>
, public intrusive_list_hook<ilist_tag2>
{
public:
int i{42};
obj_t() = default;
obj_t(int ni) : i(ni) {}
};
bool operator<(const obj_t& l, const obj_t& r) noexcept {
return l.i < r.i;
}
}
TEST_CASE("intrusive_list") {
{
intrusive_list<obj_t, ilist_tag1> l;
REQUIRE(l.empty());
REQUIRE_FALSE(l.size());
}
SECTION("push_back/push_front") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
REQUIRE(l.back().i == 1);
REQUIRE(l.front().i == 1);
l.push_back(*o2);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 1);
l.push_front(*o3);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 3);
l.push_front(*o4);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 4);
}
}
SECTION("pop_back/pop_front") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
l.push_back(*o4);
l.pop_front();
REQUIRE(l.front().i == 2);
REQUIRE(l.back().i == 4);
l.pop_back();
REQUIRE(l.front().i == 2);
REQUIRE(l.back().i == 3);
l.pop_front();
REQUIRE(l.front().i == 3);
REQUIRE(l.back().i == 3);
l.pop_back();
REQUIRE(l.empty());
}
}
SECTION("pop_back_and_dispose/pop_front_and_dispose") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
l.push_back(*o4);
std::size_t dispose_count = 0;
l.pop_front_and_dispose([&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 1);
});
REQUIRE(l.front().i == 2);
REQUIRE(l.back().i == 4);
l.pop_back_and_dispose([&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 4);
});
REQUIRE(l.front().i == 2);
REQUIRE(l.back().i == 3);
l.pop_front_and_dispose([&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 2);
});
REQUIRE(l.front().i == 3);
REQUIRE(l.back().i == 3);
l.pop_front_and_dispose([&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 3);
});
REQUIRE(l.empty());
REQUIRE(dispose_count == 4);
}
}
SECTION("empty/size/clear/clear_and_dispose") {
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
{
intrusive_list<obj_t, ilist_tag1> l;
REQUIRE(l.empty());
REQUIRE_FALSE(l.size());
l.push_back(*o1);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 1u);
l.push_front(*o2);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 2u);
l.push_back(*o3);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 3u);
l.pop_front();
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 2u);
l.pop_back();
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 1u);
l.pop_front();
REQUIRE(l.empty());
REQUIRE_FALSE(l.size());
}
{
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 3u);
}
{
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
l.clear();
REQUIRE(l.empty());
}
{
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
std::size_t dispose_count = 0;
std::array<int, 3> obj_values{3,2,1};
l.clear_and_dispose([&dispose_count, &obj_values](obj_t* o){
REQUIRE(obj_values[dispose_count] == o->i);
++dispose_count;
});
REQUIRE(l.empty());
REQUIRE(dispose_count == 3);
}
}
SECTION("swap") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l1;
intrusive_list<obj_t, ilist_tag1> l2;
l1.push_back(*o1);
l1.push_back(*o2);
l2.push_back(*o3);
l2.push_back(*o4);
l1.swap(l2);
REQUIRE(l1.back().i == 4);
REQUIRE(l1.front().i == 3);
REQUIRE(l2.back().i == 2);
REQUIRE(l2.front().i == 1);
}
}
SECTION("insert/erase/erase_and_dispose") {
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
{
intrusive_list<obj_t, ilist_tag1> l;
REQUIRE(l.insert(l.end(), *o1)->i == 1); // 1
{
auto i = l.insert(++l.begin(), *o2); // 1 2
REQUIRE(std::distance(l.begin(), i) == 1);
}
{
auto i = l.insert(l.begin(), *o3); // 3 1 2
REQUIRE(std::distance(l.begin(), i) == 0);
}
{
auto iter = l.begin();
REQUIRE(iter->i == 3);
REQUIRE((++iter)->i == 1);
REQUIRE((++iter)->i == 2);
REQUIRE(++iter == l.end());
}
// 3 1 2 4
l.push_back(*o4);
// 3 1 2
REQUIRE(l.erase(--l.end()) == l.end());
REQUIRE(l.size() == 3);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 3);
// 3 2
REQUIRE(l.erase(++l.begin())->i == 2);
REQUIRE(l.size() == 2);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 3);
// 2
REQUIRE(l.erase(l.begin())->i == 2);
REQUIRE(l.size() == 1);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 2);
}
{
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
l.push_back(*o4);
std::size_t dispose_count = 0;
l.erase_and_dispose(l.begin(), [&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 1);
});
l.erase_and_dispose(++l.begin(), [&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 3);
});
l.erase_and_dispose(++l.begin(), [&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 4);
});
l.erase_and_dispose(l.begin(), [&dispose_count](obj_t* o){
++dispose_count;
REQUIRE(o->i == 2);
});
REQUIRE(dispose_count == 4);
}
}
SECTION("iterator_to") {
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
l.push_back(*o4);
REQUIRE(l.iterator_to(*o1) == l.begin());
REQUIRE(l.iterator_to(*o2) == ++l.begin());
{
const auto& co3 = o3;
const auto& co4 = o4;
REQUIRE(l.iterator_to(*co3) == --(--l.end()));
REQUIRE(l.iterator_to(*co4) == --l.end());
}
}
SECTION("auto_unlink") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
o1.reset();
REQUIRE(l.size() == 1u);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 2);
}
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
o2.reset();
REQUIRE(l.size() == 1u);
REQUIRE(l.back().i == 1);
REQUIRE(l.front().i == 1);
}
}
SECTION("multitag") {
{
auto o1 = std::make_unique<obj_t>();
intrusive_list<obj_t, ilist_tag1> l1;
intrusive_list<obj_t, ilist_tag2> l2;
l1.push_back(*o1);
l2.push_back(*o1);
REQUIRE(l1.size() == 1);
REQUIRE(l2.size() == 1);
REQUIRE(l1.begin()->i == 42);
REQUIRE(l2.begin()->i == 42);
l2.pop_back();
REQUIRE(l1.size() == 1);
REQUIRE(l1.begin()->i == 42);
REQUIRE(l2.empty());
}
}
SECTION("iterator") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
std::transform(l.begin(), l.end(), l.begin(), [](obj_t& o){
return o.i * 5;
});
{
auto iter = l.begin();
REQUIRE((*iter).i == 5);
REQUIRE((++iter)->i == 10);
}
{
auto iter = l.rbegin();
REQUIRE((*iter).i == 10);
REQUIRE((++iter)->i == 5);
}
REQUIRE(15 == std::accumulate(l.begin(), l.end(), 0, [](int acc, obj_t& o){
return acc + o.i;
}));
REQUIRE(15 == std::accumulate(l.rbegin(), l.rend(), 0, [](int acc, obj_t& o){
return acc + o.i;
}));
{
const auto& cl = l;
REQUIRE(15 == std::accumulate(cl.begin(), cl.end(), 0, [](int acc, const obj_t& o){
return acc + o.i;
}));
REQUIRE(15 == std::accumulate(cl.rbegin(), cl.rend(), 0, [](int acc, const obj_t& o){
return acc + o.i;
}));
}
{
REQUIRE(l.begin() == l.begin());
REQUIRE(l.begin() == l.cbegin());
REQUIRE(l.begin() != l.end());
REQUIRE(l.begin() != l.cend());
REQUIRE(l.cbegin() == l.begin());
REQUIRE(l.cbegin() == l.cbegin());
REQUIRE(l.cbegin() != l.end());
REQUIRE(l.cbegin() != l.cend());
REQUIRE(l.end() == l.end());
REQUIRE(l.end() == l.cend());
REQUIRE(l.end() != l.begin());
REQUIRE(l.end() != l.cbegin());
REQUIRE(l.cend() == l.end());
REQUIRE(l.cend() == l.cend());
REQUIRE(l.cend() != l.begin());
REQUIRE(l.cend() != l.cbegin());
}
{
REQUIRE(l.rbegin() == l.rbegin());
REQUIRE(l.rbegin() == l.crbegin());
REQUIRE(l.rbegin() != l.rend());
REQUIRE(l.rbegin() != l.crend());
REQUIRE(l.crbegin() == l.rbegin());
REQUIRE(l.crbegin() == l.crbegin());
REQUIRE(l.crbegin() != l.rend());
REQUIRE(l.crbegin() != l.crend());
REQUIRE(l.rend() == l.rend());
REQUIRE(l.rend() == l.crend());
REQUIRE(l.rend() != l.rbegin());
REQUIRE(l.rend() != l.crbegin());
REQUIRE(l.crend() == l.rend());
REQUIRE(l.crend() == l.crend());
REQUIRE(l.crend() != l.rbegin());
REQUIRE(l.crend() != l.crbegin());
}
}
}
SECTION("sort") {
{
std::random_device rd;
std::mt19937 rnd(rd());
std::uniform_int_distribution<u32> uni_size(0, 1024);
std::uniform_int_distribution<i32> uni_number(-65536, 65535);
const auto pred = [](const obj_t& l, const obj_t& r) noexcept {
return l.i < r.i;
};
const auto r_pred = [](const obj_t& l, const obj_t& r) noexcept {
return l.i > r.i;
};
for ( std::size_t i = 0; i < 1024; ++i ) {
vector<std::unique_ptr<obj_t>> objs;
intrusive_list<obj_t, ilist_tag1> l;
for ( u32 b = 0, e = uni_size(rnd); b < e; ++b ) {
objs.push_back(std::make_unique<obj_t>(uni_number(rnd)));
l.push_back(*objs.back());
}
l.sort();
REQUIRE(std::is_sorted(l.cbegin(), l.cend()));
l.sort(r_pred);
REQUIRE(std::is_sorted(l.cbegin(), l.cend(), r_pred));
l.sort(pred);
REQUIRE(std::is_sorted(l.cbegin(), l.cend(), pred));
}
}
}
}