blob: 8f7dc5a47702c7d610492185ee7beabce1b48125 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// 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
//
// https://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 "pw_containers/intrusive_list.h"
#include <cstddef>
#include <cstdint>
#include "gtest/gtest.h"
#include "pw_preprocessor/util.h"
namespace pw {
namespace {
class TestItem : public IntrusiveList<TestItem>::Item {
public:
TestItem() : number_(0) {}
TestItem(int number) : number_(number) {}
int GetNumber() const { return number_; }
void SetNumber(int num) { number_ = num; }
private:
int number_;
};
TEST(IntrusiveList, PushOne) {
constexpr int kMagicValue = 31;
IntrusiveList<TestItem> test_items;
TestItem item1(kMagicValue);
test_items.push_back(item1);
EXPECT_FALSE(test_items.empty());
EXPECT_EQ(test_items.front().GetNumber(), kMagicValue);
}
TEST(IntrusiveList, PushThree) {
IntrusiveList<TestItem> test_items;
TestItem item1(1);
TestItem item2(2);
TestItem item3(3);
test_items.push_back(item1);
test_items.push_back(item2);
test_items.push_back(item3);
int loop_count = 0;
for (auto& test_item : test_items) {
loop_count++;
EXPECT_EQ(loop_count, test_item.GetNumber());
}
EXPECT_EQ(loop_count, 3);
}
TEST(IntrusiveList, IsEmpty) {
IntrusiveList<TestItem> test_items;
EXPECT_TRUE(test_items.empty());
TestItem item1(1);
test_items.push_back(item1);
EXPECT_FALSE(test_items.empty());
}
TEST(IntrusiveList, InsertAfter) {
constexpr int kMagicValue = 42;
TestItem item_array[20];
IntrusiveList<TestItem> test_list;
// Fill the list with TestItem objects that have a value of zero.
for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
item_array[i].SetNumber(0);
test_list.push_back(item_array[i]);
}
// Create a test item to insert midway through the list.
TestItem inserted_item(kMagicValue);
// Move an iterator to the middle of the list, and then insert the item.
auto it = test_list.begin();
size_t expected_index = 1; // Expected index is iterator index + 1.
for (size_t i = 0; i < PW_ARRAY_SIZE(item_array) / 2; ++i) {
it++;
expected_index++;
}
it = test_list.insert_after(it, inserted_item);
// Ensure the returned iterator from insert_after is the newly inserted
// element.
EXPECT_EQ(it->GetNumber(), kMagicValue);
// Ensure the value is in the expected location (index of the iterator + 1).
size_t i = 0;
for (TestItem& item : test_list) {
if (item.GetNumber() == kMagicValue) {
EXPECT_EQ(i, expected_index);
} else {
EXPECT_EQ(item.GetNumber(), 0);
}
i++;
}
// Ensure the list didn't break and change sizes.
EXPECT_EQ(i, PW_ARRAY_SIZE(item_array) + 1);
}
TEST(IntrusiveList, PushFront) {
constexpr int kMagicValue = 42;
TestItem item_array[20];
IntrusiveList<TestItem> test_list;
// Fill the list with TestItem objects that have a value of zero.
for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
item_array[i].SetNumber(0);
test_list.push_back(item_array[i]);
}
// Create a test item to push to the front of the list.
TestItem pushed_item(kMagicValue);
test_list.push_front(pushed_item);
EXPECT_EQ(test_list.front().GetNumber(), kMagicValue);
}
TEST(IntrusiveList, Clear_Empty) {
IntrusiveList<TestItem> test_list;
EXPECT_TRUE(test_list.empty());
test_list.clear();
EXPECT_TRUE(test_list.empty());
}
TEST(IntrusiveList, Clear_OneItem) {
IntrusiveList<TestItem> test_list;
TestItem item(42);
test_list.push_back(item);
EXPECT_FALSE(test_list.empty());
test_list.clear();
EXPECT_TRUE(test_list.empty());
}
TEST(IntrusiveList, Clear_TwoItems) {
IntrusiveList<TestItem> test_list;
TestItem item1(42);
TestItem item2(42);
test_list.push_back(item1);
test_list.push_back(item2);
EXPECT_FALSE(test_list.empty());
test_list.clear();
EXPECT_TRUE(test_list.empty());
}
TEST(IntrusiveList, Clear_ReinsertClearedItems) {
std::array<TestItem, 20> item_array;
IntrusiveList<TestItem> test_list;
EXPECT_TRUE(test_list.empty());
test_list.clear();
EXPECT_TRUE(test_list.empty());
// Fill the list with TestItem objects.
for (size_t i = 0; i < item_array.size(); ++i) {
item_array[i].SetNumber(0);
test_list.push_back(item_array[i]);
}
// Remove everything.
test_list.clear();
EXPECT_TRUE(test_list.empty());
// Ensure all the removed elements can still be added back to a list.
for (size_t i = 0; i < item_array.size(); ++i) {
item_array[i].SetNumber(0);
test_list.push_back(item_array[i]);
}
}
TEST(IntrusiveList, PopFront) {
constexpr int kValue1 = 32;
constexpr int kValue2 = 4083;
IntrusiveList<TestItem> test_list;
EXPECT_TRUE(test_list.empty());
TestItem item1(kValue1);
TestItem item2(kValue2);
test_list.push_front(item2);
test_list.push_front(item1);
test_list.pop_front();
EXPECT_EQ(test_list.front().GetNumber(), kValue2);
EXPECT_FALSE(test_list.empty());
test_list.pop_front();
EXPECT_TRUE(test_list.empty());
}
TEST(IntrusiveList, PopFrontAndReinsert) {
constexpr int kValue1 = 32;
constexpr int kValue2 = 4083;
IntrusiveList<TestItem> test_list;
EXPECT_TRUE(test_list.empty());
TestItem item1(kValue1);
TestItem item2(kValue2);
test_list.push_front(item2);
test_list.push_front(item1);
test_list.pop_front();
test_list.push_front(item1);
EXPECT_EQ(test_list.front().GetNumber(), kValue1);
}
TEST(IntrusiveList, ListFront) {
IntrusiveList<TestItem> test_items;
EXPECT_EQ(&test_items.front(), nullptr);
TestItem item1(1);
TestItem item2(0);
TestItem item3(0xffff);
test_items.push_back(item1);
test_items.push_back(item2);
test_items.push_back(item3);
EXPECT_EQ(&item1, &test_items.front());
EXPECT_EQ(&item1, &(*test_items.begin()));
}
TEST(IntrusiveList, IteratorIncrement) {
TestItem item_array[20];
IntrusiveList<TestItem> test_list;
for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
item_array[i].SetNumber(i);
test_list.push_back(item_array[i]);
}
auto it = test_list.begin();
int i = 0;
while (it != test_list.end()) {
if (i == 0) {
// Test pre-incrementing on the first element.
EXPECT_EQ((++it)->GetNumber(), item_array[++i].GetNumber());
} else {
EXPECT_EQ((it++)->GetNumber(), item_array[i++].GetNumber());
}
}
}
TEST(IntrusiveList, ConstIteratorRead) {
// For this test, items are checked to be non-zero.
TestItem item1(1);
TestItem item2(99);
IntrusiveList<TestItem> test_items;
const IntrusiveList<TestItem>* const_list = &test_items;
test_items.push_back(item1);
test_items.push_back(item2);
auto it = const_list->begin();
while (it != const_list->end()) {
EXPECT_NE(it->GetNumber(), 0);
it++;
}
}
#if NO_COMPILE_TESTS
// TODO(pwbug/47): These tests should fail to compile, enable when no-compile
// tests are set up in Pigweed.
TEST(IntrusiveList, ConstIteratorModify) {
TestItem item1(1);
TestItem item2(99);
IntrusiveList<TestItem> test_items;
const IntrusiveList<TestItem>* const_list = &test_items;
test_items.push_back(item1);
test_items.push_back(item2);
auto it = const_list->begin();
while (it != const_list->end()) {
it->SetNumber(0);
it++;
}
}
#endif // NO_COMPILE_TESTS
} // namespace
} // namespace pw