/*
* Copyright 2017 WebAssembly Community Group participants
*
* 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 "gtest/gtest.h"
#include <memory>
#include "wabt/intrusive-list.h"
using namespace wabt;
namespace {
struct TestObject : intrusive_list_base<TestObject> {
static int creation_count;
TestObject(int data = 0 , int data2 = 0 ) : data(data), data2(data2) {
++creation_count;
}
// Allow move.
TestObject(TestObject&& other) {
// Don't increment creation_count; we're moving from other.
*this = std::move(other);
}
TestObject& operator =(TestObject&& other) {
data = other.data;
data2 = other.data2;
other.moved = true ;
return *this ;
}
// Disallow copy.
TestObject(const TestObject&) = delete ;
TestObject& operator =(const TestObject&) = delete ;
~TestObject() {
if (!moved) {
creation_count--;
}
}
int data;
int data2;
bool moved = false;
};
// static
int TestObject::creation_count = 0 ;
using TestObjectList = intrusive_list<TestObject>;
class IntrusiveListTest : public ::testing::Test {
protected :
virtual void SetUp() {
// Reset to 0 even if the previous test leaked objects to keep the tests
// independent.
TestObject::creation_count = 0 ;
}
virtual void TearDown() { ASSERT_EQ(0 , TestObject::creation_count); }
TestObjectList NewList(const std::vector<int>& data_values) {
TestObjectList result;
for (auto data_value : data_values)
result.emplace_back(data_value);
return result;
}
void AssertListEq(const TestObjectList& list,
const std::vector<int>& expected) {
size_t count = 0 ;
for (const TestObject& node : list) {
ASSERT_EQ(expected[count++], node.data);
}
ASSERT_EQ(count, expected.size());
}
void AssertListEq(const TestObjectList& list,
const std::vector<int>& expected,
const std::vector<int>& expected2) {
assert(expected.size() == expected2.size());
size_t count = 0 ;
for (const TestObject& node : list) {
ASSERT_EQ(expected[count], node.data);
ASSERT_EQ(expected2[count], node.data2);
count++;
}
ASSERT_EQ(count, expected.size());
}
};
} // end anonymous namespace
TEST_F(IntrusiveListTest, default_constructor) {
TestObjectList list;
}
TEST_F(IntrusiveListTest, node_constructor) {
TestObjectList list(std::make_unique<TestObject>(1 ));
AssertListEq(list, {1 });
}
TEST_F(IntrusiveListTest, node_move_constructor) {
TestObjectList list(TestObject(1 ));
AssertListEq(list, {1 });
}
TEST_F(IntrusiveListTest, move_constructor) {
TestObjectList list1 = NewList({1 , 2 , 3 });
TestObjectList list2(std::move(list1));
AssertListEq(list1, {});
AssertListEq(list2, {1 , 2 , 3 });
}
TEST_F(IntrusiveListTest, move_assignment_operator) {
TestObjectList list1 = NewList({1 , 2 , 3 });
TestObjectList list2;
list2 = std::move(list1);
AssertListEq(list1, {});
AssertListEq(list2, {1 , 2 , 3 });
}
namespace {
class IntrusiveListIteratorTest : public IntrusiveListTest {
protected :
virtual void SetUp() {
IntrusiveListTest::SetUp();
list_.emplace_back(1 );
list_.emplace_back(2 );
list_.emplace_back(3 );
}
virtual void TearDown() {
list_.clear();
IntrusiveListTest::TearDown();
}
template <typename Iter>
void TestForward(Iter first, Iter last, const std::vector<int>& expected) {
size_t count = 0 ;
while (first != last) {
ASSERT_EQ(expected[count], first->data);
++first;
++count;
}
ASSERT_EQ(count, expected.size());
}
template <typename Iter>
void TestBackward(Iter first, Iter last, const std::vector<int>& expected) {
size_t count = 0 ;
while (first != last) {
--last;
ASSERT_EQ(expected[count], last->data);
++count;
}
ASSERT_EQ(count, expected.size());
}
TestObjectList list_;
const TestObjectList& clist_ = list_;
};
} // end of anonymous namespace
TEST_F(IntrusiveListIteratorTest, begin_end_forward) {
TestForward(list_.begin(), list_.end(), {1 , 2 , 3 });
TestForward(clist_.begin(), clist_.end(), {1 , 2 , 3 });
}
TEST_F(IntrusiveListIteratorTest, rbegin_rend_forward) {
TestForward(list_.rbegin(), list_.rend(), {3 , 2 , 1 });
TestForward(clist_.rbegin(), clist_.rend(), {3 , 2 , 1 });
}
TEST_F(IntrusiveListIteratorTest, cbegin_cend_forward) {
TestForward(list_.cbegin(), list_.cend(), {1 , 2 , 3 });
TestForward(clist_.cbegin(), clist_.cend(), {1 , 2 , 3 });
}
TEST_F(IntrusiveListIteratorTest, crbegin_crend_forward) {
TestForward(list_.crbegin(), list_.crend(), {3 , 2 , 1 });
TestForward(clist_.crbegin(), clist_.crend(), {3 , 2 , 1 });
}
TEST_F(IntrusiveListIteratorTest, begin_end_backward) {
TestBackward(list_.begin(), list_.end(), {3 , 2 , 1 });
TestBackward(clist_.begin(), clist_.end(), {3 , 2 , 1 });
}
TEST_F(IntrusiveListIteratorTest, rbegin_rend_backward) {
TestBackward(list_.rbegin(), list_.rend(), {1 , 2 , 3 });
TestBackward(clist_.rbegin(), clist_.rend(), {1 , 2 , 3 });
}
TEST_F(IntrusiveListIteratorTest, cbegin_cend_backward) {
TestBackward(list_.cbegin(), list_.cend(), {3 , 2 , 1 });
TestBackward(clist_.cbegin(), clist_.cend(), {3 , 2 , 1 });
}
TEST_F(IntrusiveListIteratorTest, crbegin_crend_backward) {
TestBackward(list_.crbegin(), list_.crend(), {1 , 2 , 3 });
TestBackward(clist_.crbegin(), clist_.crend(), {1 , 2 , 3 });
}
TEST_F(IntrusiveListTest, size_empty) {
TestObjectList list;
ASSERT_EQ(0 U, list.size());
ASSERT_TRUE(list.empty());
list.emplace_back(1 );
ASSERT_EQ(1 U, list.size());
ASSERT_FALSE(list.empty());
}
TEST_F(IntrusiveListTest, front_back) {
TestObjectList list;
list.emplace_back(1 );
ASSERT_EQ(1 , list.front().data);
ASSERT_EQ(1 , list.back().data);
list.emplace_back(2 );
ASSERT_EQ(1 , list.front().data);
ASSERT_EQ(2 , list.back().data);
const TestObjectList& clist = list;
ASSERT_EQ(1 , clist.front().data);
ASSERT_EQ(2 , clist.back().data);
}
TEST_F(IntrusiveListTest, emplace_front) {
TestObjectList list;
// Pass an additional arg to show that forwarding works properly.
list.emplace_front(1 , 100 );
list.emplace_front(2 , 200 );
list.emplace_front(3 , 300 );
list.emplace_front(4 , 400 );
AssertListEq(list, {4 , 3 , 2 , 1 }, {400 , 300 , 200 , 100 });
}
TEST_F(IntrusiveListTest, emplace_back) {
TestObjectList list;
// Pass an additional arg to show that forwarding works properly.
list.emplace_back(1 , 100 );
list.emplace_back(2 , 200 );
list.emplace_back(3 , 300 );
list.emplace_back(4 , 400 );
AssertListEq(list, {1 , 2 , 3 , 4 }, {100 , 200 , 300 , 400 });
}
TEST_F(IntrusiveListTest, push_front_pointer) {
TestObjectList list;
list.push_front(std::make_unique<TestObject>(1 ));
list.push_front(std::make_unique<TestObject>(2 ));
list.push_front(std::make_unique<TestObject>(3 ));
AssertListEq(list, {3 , 2 , 1 });
}
TEST_F(IntrusiveListTest, push_back_pointer) {
TestObjectList list;
list.push_back(std::make_unique<TestObject>(1 ));
list.push_back(std::make_unique<TestObject>(2 ));
list.push_back(std::make_unique<TestObject>(3 ));
AssertListEq(list, {1 , 2 , 3 });
}
TEST_F(IntrusiveListTest, push_front_move) {
TestObjectList list;
list.push_front(TestObject(1 ));
list.push_front(TestObject(2 ));
list.push_front(TestObject(3 ));
AssertListEq(list, {3 , 2 , 1 });
}
TEST_F(IntrusiveListTest, push_back_move) {
TestObjectList list;
list.push_back(TestObject(1 ));
list.push_back(TestObject(2 ));
list.push_back(TestObject(3 ));
AssertListEq(list, {1 , 2 , 3 });
}
TEST_F(IntrusiveListTest, pop_front) {
TestObjectList list = NewList({1 , 2 , 3 , 4 });
list.pop_front();
AssertListEq(list, {2 , 3 , 4 });
list.pop_front();
AssertListEq(list, {3 , 4 });
list.pop_front();
AssertListEq(list, {4 });
list.pop_front();
AssertListEq(list, {});
}
TEST_F(IntrusiveListTest, pop_back) {
TestObjectList list = NewList({1 , 2 , 3 , 4 });
list.pop_back();
AssertListEq(list, {1 , 2 , 3 });
list.pop_back();
AssertListEq(list, {1 , 2 });
list.pop_back();
AssertListEq(list, {1 });
list.pop_back();
AssertListEq(list, {});
}
TEST_F(IntrusiveListTest, extract_front) {
TestObjectList list = NewList({1 , 2 , 3 });
std::unique_ptr<TestObject> t1(list.extract_front());
ASSERT_EQ(1 , t1->data);
AssertListEq(list, {2 , 3 });
std::unique_ptr<TestObject> t2(list.extract_front());
ASSERT_EQ(2 , t2->data);
AssertListEq(list, {3 });
std::unique_ptr<TestObject> t3(list.extract_front());
ASSERT_EQ(3 , t3->data);
AssertListEq(list, {});
}
TEST_F(IntrusiveListTest, extract_back) {
TestObjectList list = NewList({1 , 2 , 3 });
std::unique_ptr<TestObject> t1(list.extract_back());
ASSERT_EQ(3 , t1->data);
AssertListEq(list, {1 , 2 });
std::unique_ptr<TestObject> t2(list.extract_back());
ASSERT_EQ(2 , t2->data);
AssertListEq(list, {1 });
std::unique_ptr<TestObject> t3(list.extract_back());
ASSERT_EQ(1 , t3->data);
AssertListEq(list, {});
}
TEST_F(IntrusiveListTest, emplace) {
TestObjectList list;
// Pass an additional arg to show that forwarding works properly.
list.emplace(list.begin(), 2 , 200 );
list.emplace(list.end(), 4 , 400 );
list.emplace(std::next(list.begin()), 3 , 300 );
list.emplace(list.begin(), 1 , 100 );
AssertListEq(list, {1 , 2 , 3 , 4 }, {100 , 200 , 300 , 400 });
}
TEST_F(IntrusiveListTest, insert_pointer) {
TestObjectList list;
list.insert(list.begin(), std::make_unique<TestObject>(2 ));
list.insert(list.end(), std::make_unique<TestObject>(4 ));
list.insert(std::next(list.begin()), std::make_unique<TestObject>(3 ));
list.insert(list.begin(), std::make_unique<TestObject>(1 ));
AssertListEq(list, {1 , 2 , 3 , 4 });
}
TEST_F(IntrusiveListTest, insert_move) {
TestObjectList list;
list.insert(list.begin(), TestObject(2 ));
list.insert(list.end(), TestObject(4 ));
list.insert(std::next(list.begin()), TestObject(3 ));
list.insert(list.begin(), TestObject(1 ));
AssertListEq(list, {1 , 2 , 3 , 4 });
}
TEST_F(IntrusiveListTest, extract) {
TestObjectList list = NewList({1 , 2 , 3 , 4 });
TestObjectList::iterator t1_iter = std::next(list.begin(), 0 );
TestObjectList::iterator t2_iter = std::next(list.begin(), 1 );
TestObjectList::iterator t3_iter = std::next(list.begin(), 2 );
TestObjectList::iterator t4_iter = std::next(list.begin(), 3 );
std::unique_ptr<TestObject> t2(list.extract(t2_iter));
ASSERT_EQ(2 , t2->data);
AssertListEq(list, {1 , 3 , 4 });
std::unique_ptr<TestObject> t4(list.extract(t4_iter));
ASSERT_EQ(4 , t4->data);
AssertListEq(list, {1 , 3 });
std::unique_ptr<TestObject> t1(list.extract(t1_iter));
ASSERT_EQ(1 , t1->data);
AssertListEq(list, {3 });
std::unique_ptr<TestObject> t3(list.extract(t3_iter));
ASSERT_EQ(3 , t3->data);
AssertListEq(list, {});
}
TEST_F(IntrusiveListTest, erase) {
TestObjectList list = NewList({1 , 2 , 3 , 4 });
TestObjectList::iterator t1_iter = std::next(list.begin(), 0 );
TestObjectList::iterator t2_iter = std::next(list.begin(), 1 );
TestObjectList::iterator t3_iter = std::next(list.begin(), 2 );
TestObjectList::iterator t4_iter = std::next(list.begin(), 3 );
// erase returns an iterator to the following node.
ASSERT_EQ(3 , list.erase(t2_iter)->data);
AssertListEq(list, {1 , 3 , 4 });
ASSERT_EQ(list.end(), list.erase(t4_iter));
AssertListEq(list, {1 , 3 });
ASSERT_EQ(3 , list.erase(t1_iter)->data);
AssertListEq(list, {3 });
ASSERT_EQ(list.end(), list.erase(t3_iter));
AssertListEq(list, {});
}
TEST_F(IntrusiveListTest, erase_range) {
TestObjectList list = NewList({1 , 2 , 3 , 4 , 5 , 6 });
// OK to erase an empty range.
list.erase(list.begin(), list.begin());
list.erase(list.end(), list.end());
// Erase the first element (1).
list.erase(list.begin(), std::next(list.begin()));
AssertListEq(list, {2 , 3 , 4 , 5 , 6 });
// Erase the last element (6).
list.erase(std::prev(list.end()), list.end());
AssertListEq(list, {2 , 3 , 4 , 5 });
// Erase [3, 4] => [2, 5]
list.erase(std::next(list.begin()), std::prev(list.end()));
AssertListEq(list, {2 , 5 });
// Erase the rest.
list.erase(list.begin(), list.end());
AssertListEq(list, {});
}
TEST_F(IntrusiveListTest, swap) {
TestObjectList list1 = NewList({1 , 2 , 3 , 4 });
TestObjectList list2 = NewList({100 , 200 , 300 });
AssertListEq(list1, {1 , 2 , 3 , 4 });
AssertListEq(list2, {100 , 200 , 300 });
list1.swap(list2);
AssertListEq(list1, {100 , 200 , 300 });
AssertListEq(list2, {1 , 2 , 3 , 4 });
}
TEST_F(IntrusiveListTest, clear) {
TestObjectList list = NewList({1 , 2 , 3 , 4 });
ASSERT_FALSE(list.empty());
list.clear();
ASSERT_EQ(0 U, list.size());
ASSERT_TRUE(list.empty());
}
TEST_F(IntrusiveListTest, splice_list) {
TestObjectList list1 = NewList({1 , 2 , 3 });
// Splice at beginning.
TestObjectList list2 = NewList({100 , 200 });
list1.splice(list1.begin(), list2);
AssertListEq(list1, {100 , 200 , 1 , 2 , 3 });
AssertListEq(list2, {});
// Splice at end.
TestObjectList list3 = NewList({500 , 600 , 700 });
list1.splice(list1.end(), list3);
AssertListEq(list1, {100 , 200 , 1 , 2 , 3 , 500 , 600 , 700 });
AssertListEq(list3, {});
// Splice in the middle.
TestObjectList list4 = NewList({400 });
list1.splice(std::next(list1.begin(), 4 ), list4);
AssertListEq(list1, {100 , 200 , 1 , 2 , 400 , 3 , 500 , 600 , 700 });
AssertListEq(list4, {});
}
TEST_F(IntrusiveListTest, splice_list_move) {
TestObjectList list1 = NewList({1 , 2 , 3 });
// Splice at beginning.
list1.splice(list1.begin(), NewList({100 , 200 }));
AssertListEq(list1, {100 , 200 , 1 , 2 , 3 });
// Splice at end.
list1.splice(list1.end(), NewList({500 , 600 , 700 }));
AssertListEq(list1, {100 , 200 , 1 , 2 , 3 , 500 , 600 , 700 });
// Splice in the middle.
list1.splice(std::next(list1.begin(), 4 ), NewList({400 }));
AssertListEq(list1, {100 , 200 , 1 , 2 , 400 , 3 , 500 , 600 , 700 });
}
TEST_F(IntrusiveListTest, splice_node) {
TestObjectList list1 = NewList({1 , 2 , 3 });
// Splice at beginning.
TestObjectList list2 = NewList({100 , 200 });
list1.splice(list1.begin(), list2, list2.begin());
AssertListEq(list1, {100 , 1 , 2 , 3 });
AssertListEq(list2, {200 });
// Splice at end.
TestObjectList list3 = NewList({500 , 600 , 700 });
list1.splice(list1.end(), list3, std::next(list3.begin(), 2 ));
AssertListEq(list1, {100 , 1 , 2 , 3 , 700 });
AssertListEq(list3, {500 , 600 });
// Splice in the middle.
TestObjectList list4 = NewList({400 });
list1.splice(std::next(list1.begin(), 3 ), list4, list4.begin());
AssertListEq(list1, {100 , 1 , 2 , 400 , 3 , 700 });
AssertListEq(list4, {});
}
TEST_F(IntrusiveListTest, splice_range) {
TestObjectList list1 = NewList({1 , 2 , 3 });
// Splice at beginning.
TestObjectList list2 = NewList({100 , 200 , 300 });
list1.splice(list1.begin(), list2, list2.begin(), std::prev(list2.end()));
AssertListEq(list1, {100 , 200 , 1 , 2 , 3 });
AssertListEq(list2, {300 });
// Splice at end.
TestObjectList list3 = NewList({500 , 600 , 700 });
list1.splice(list1.end(), list3, std::next(list3.begin()), list3.end());
AssertListEq(list1, {100 , 200 , 1 , 2 , 3 , 600 , 700 });
AssertListEq(list3, {500 });
// Splice in the middle.
TestObjectList list4 = NewList({400 });
list1.splice(std::next(list1.begin(), 4 ), list4, list4.begin(), list4.end());
AssertListEq(list1, {100 , 200 , 1 , 2 , 400 , 3 , 600 , 700 });
AssertListEq(list4, {});
}
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-28)
¤
*© Formatika GbR, Deutschland