1 // Copyright 2020 The Pigweed Authors
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
7 // https://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
15 #include "pw_containers/intrusive_list.h"
21 #include "gtest/gtest.h"
22 #include "pw_preprocessor/util.h"
27 class TestItem : public IntrusiveList<TestItem>::Item {
29 TestItem() : number_(0) {}
30 TestItem(int number) : number_(number) {}
32 int GetNumber() const { return number_; }
33 void SetNumber(int num) { number_ = num; }
35 // Add equality comparison to ensure comparisons are done by identity rather
36 // than equality for the remove function.
37 bool operator==(const TestItem& other) const {
38 return number_ == other.number_;
45 TEST(IntrusiveList, Construct_InitializerList_Empty) {
46 IntrusiveList<TestItem> list({});
47 EXPECT_TRUE(list.empty());
50 TEST(IntrusiveList, Construct_InitializerList_One) {
52 IntrusiveList<TestItem> list({&one});
54 EXPECT_EQ(&one, &list.front());
57 TEST(IntrusiveList, Construct_InitializerList_Multiple) {
62 IntrusiveList<TestItem> list({&one, &two, &thr});
63 auto it = list.begin();
64 EXPECT_EQ(&one, &(*it++));
65 EXPECT_EQ(&two, &(*it++));
66 EXPECT_EQ(&thr, &(*it++));
67 EXPECT_EQ(list.end(), it);
70 TEST(IntrusiveList, Construct_ObjectIterator_Empty) {
71 std::array<TestItem, 0> array;
72 IntrusiveList<TestItem> list(array.begin(), array.end());
74 EXPECT_TRUE(list.empty());
77 TEST(IntrusiveList, Construct_ObjectIterator_One) {
78 std::array<TestItem, 1> array{{{1}}};
79 IntrusiveList<TestItem> list(array.begin(), array.end());
81 EXPECT_EQ(&array.front(), &list.front());
84 TEST(IntrusiveList, Construct_ObjectIterator_Multiple) {
85 std::array<TestItem, 3> array{{{1}, {2}, {3}}};
87 IntrusiveList<TestItem> list(array.begin(), array.end());
88 auto it = list.begin();
89 EXPECT_EQ(&array[0], &(*it++));
90 EXPECT_EQ(&array[1], &(*it++));
91 EXPECT_EQ(&array[2], &(*it++));
92 EXPECT_EQ(list.end(), it);
95 TEST(IntrusiveList, Construct_PointerIterator_Empty) {
96 std::array<TestItem*, 0> array;
97 IntrusiveList<TestItem> list(array.begin(), array.end());
99 EXPECT_TRUE(list.empty());
102 TEST(IntrusiveList, Construct_PointerIterator_One) {
103 std::array<TestItem, 1> array{{{1}}};
104 std::array<TestItem*, 1> ptrs{{&array[0]}};
106 IntrusiveList<TestItem> list(ptrs.begin(), ptrs.end());
108 EXPECT_EQ(ptrs[0], &list.front());
111 TEST(IntrusiveList, Construct_PointerIterator_Multiple) {
112 std::array<TestItem, 3> array{{{1}, {2}, {3}}};
113 std::array<TestItem*, 3> ptrs{{&array[0], &array[1], &array[2]}};
115 IntrusiveList<TestItem> list(ptrs.begin(), ptrs.end());
116 auto it = list.begin();
117 EXPECT_EQ(ptrs[0], &(*it++));
118 EXPECT_EQ(ptrs[1], &(*it++));
119 EXPECT_EQ(ptrs[2], &(*it++));
120 EXPECT_EQ(list.end(), it);
123 TEST(IntrusiveList, Assign_ReplacesPriorContents) {
124 std::array<TestItem, 3> array{{{0}, {100}, {200}}};
125 IntrusiveList<TestItem> list(array.begin(), array.end());
127 list.assign(array.begin() + 1, array.begin() + 2);
129 auto it = list.begin();
130 EXPECT_EQ(&array[1], &(*it++));
131 EXPECT_EQ(list.end(), it);
134 TEST(IntrusiveList, Assign_EmptyRange) {
135 std::array<TestItem, 3> array{{{0}, {100}, {200}}};
136 IntrusiveList<TestItem> list(array.begin(), array.end());
138 list.assign(array.begin() + 1, array.begin() + 1);
140 EXPECT_TRUE(list.empty());
143 TEST(IntrusiveList, PushOne) {
144 constexpr int kMagicValue = 31;
145 TestItem item1(kMagicValue);
146 IntrusiveList<TestItem> list;
147 list.push_back(item1);
148 EXPECT_FALSE(list.empty());
149 EXPECT_EQ(list.front().GetNumber(), kMagicValue);
152 TEST(IntrusiveList, PushThree) {
157 IntrusiveList<TestItem> list;
158 list.push_back(item1);
159 list.push_back(item2);
160 list.push_back(item3);
163 for (auto& test_item : list) {
165 EXPECT_EQ(loop_count, test_item.GetNumber());
167 EXPECT_EQ(loop_count, 3);
170 TEST(IntrusiveList, IsEmpty) {
173 IntrusiveList<TestItem> list;
174 EXPECT_TRUE(list.empty());
176 list.push_back(item1);
177 EXPECT_FALSE(list.empty());
180 TEST(IntrusiveList, InsertAfter) {
181 // Create a test item to insert midway through the list.
182 constexpr int kMagicValue = 42;
183 TestItem inserted_item(kMagicValue);
185 // Create initial values to fill in the start/end.
186 TestItem item_array[20];
188 IntrusiveList<TestItem> list;
189 // Fill the list with TestItem objects that have a value of zero.
190 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
191 item_array[i].SetNumber(0);
192 list.push_back(item_array[i]);
195 // Move an iterator to the middle of the list, and then insert the magic item.
196 auto it = list.begin();
197 size_t expected_index = 1; // Expected index is iterator index + 1.
198 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array) / 2; ++i) {
202 it = list.insert_after(it, inserted_item);
204 // Ensure the returned iterator from insert_after is the newly inserted
206 EXPECT_EQ(it->GetNumber(), kMagicValue);
208 // Ensure the value is in the expected location (index of the iterator + 1).
210 for (TestItem& item : list) {
211 if (item.GetNumber() == kMagicValue) {
212 EXPECT_EQ(i, expected_index);
214 EXPECT_EQ(item.GetNumber(), 0);
219 // Ensure the list didn't break and change sizes.
220 EXPECT_EQ(i, PW_ARRAY_SIZE(item_array) + 1);
223 TEST(IntrusiveList, PushFront) {
224 constexpr int kMagicValue = 42;
225 TestItem pushed_item(kMagicValue);
227 TestItem item_array[20];
228 IntrusiveList<TestItem> list;
229 // Fill the list with TestItem objects that have a value of zero.
230 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
231 item_array[i].SetNumber(0);
232 list.push_back(item_array[i]);
235 // Create a test item to push to the front of the list.
236 list.push_front(pushed_item);
237 EXPECT_EQ(list.front().GetNumber(), kMagicValue);
240 TEST(IntrusiveList, Clear_Empty) {
241 IntrusiveList<TestItem> list;
242 EXPECT_TRUE(list.empty());
244 EXPECT_TRUE(list.empty());
247 TEST(IntrusiveList, Clear_OneItem) {
249 IntrusiveList<TestItem> list;
250 list.push_back(item);
251 EXPECT_FALSE(list.empty());
253 EXPECT_TRUE(list.empty());
256 TEST(IntrusiveList, Clear_TwoItems) {
259 IntrusiveList<TestItem> list;
260 list.push_back(item1);
261 list.push_back(item2);
262 EXPECT_FALSE(list.empty());
264 EXPECT_TRUE(list.empty());
267 TEST(IntrusiveList, Clear_ReinsertClearedItems) {
268 std::array<TestItem, 20> item_array;
269 IntrusiveList<TestItem> list;
270 EXPECT_TRUE(list.empty());
272 EXPECT_TRUE(list.empty());
274 // Fill the list with TestItem objects.
275 for (size_t i = 0; i < item_array.size(); ++i) {
276 item_array[i].SetNumber(0);
277 list.push_back(item_array[i]);
280 // Remove everything.
282 EXPECT_TRUE(list.empty());
284 // Ensure all the removed elements can still be added back to a list.
285 for (size_t i = 0; i < item_array.size(); ++i) {
286 item_array[i].SetNumber(0);
287 list.push_back(item_array[i]);
291 TEST(IntrusiveList, PopFront) {
292 constexpr int kValue1 = 32;
293 constexpr int kValue2 = 4083;
295 TestItem item1(kValue1);
296 TestItem item2(kValue2);
298 IntrusiveList<TestItem> list;
299 EXPECT_TRUE(list.empty());
301 list.push_front(item2);
302 list.push_front(item1);
304 EXPECT_EQ(list.front().GetNumber(), kValue2);
305 EXPECT_FALSE(list.empty());
307 EXPECT_TRUE(list.empty());
310 TEST(IntrusiveList, PopFrontAndReinsert) {
311 constexpr int kValue1 = 32;
312 constexpr int kValue2 = 4083;
314 TestItem item1(kValue1);
315 TestItem item2(kValue2);
317 IntrusiveList<TestItem> list;
318 EXPECT_TRUE(list.empty());
320 list.push_front(item2);
321 list.push_front(item1);
323 list.push_front(item1);
324 EXPECT_EQ(list.front().GetNumber(), kValue1);
327 TEST(IntrusiveList, ListFront) {
330 TestItem item3(0xffff);
332 IntrusiveList<TestItem> list;
333 list.push_back(item1);
334 list.push_back(item2);
335 list.push_back(item3);
337 EXPECT_EQ(&item1, &list.front());
338 EXPECT_EQ(&item1, &(*list.begin()));
341 TEST(IntrusiveList, IteratorIncrement) {
342 TestItem item_array[20];
343 IntrusiveList<TestItem> list;
344 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
345 item_array[i].SetNumber(i);
346 list.push_back(item_array[i]);
349 auto it = list.begin();
351 while (it != list.end()) {
353 // Test pre-incrementing on the first element.
354 EXPECT_EQ((++it)->GetNumber(), item_array[++i].GetNumber());
356 EXPECT_EQ((it++)->GetNumber(), item_array[i++].GetNumber());
361 TEST(IntrusiveList, ConstIteratorRead) {
362 // For this test, items are checked to be non-zero.
365 IntrusiveList<TestItem> list;
367 const IntrusiveList<TestItem>* const_list = &list;
369 list.push_back(item1);
370 list.push_back(item2);
372 auto it = const_list->begin();
373 while (it != const_list->end()) {
374 EXPECT_NE(it->GetNumber(), 0);
379 // TODO(pwbug/47): These tests should fail to compile, enable when no-compile
380 // tests are set up in Pigweed.
381 #define NO_COMPILE_TESTS 0
383 TEST(IntrusiveList, ConstIteratorModify) {
386 IntrusiveList<TestItem> list;
388 const IntrusiveList<TestItem>* const_list = &list;
390 list.push_back(item1);
391 list.push_back(item2);
393 auto it = const_list->begin();
394 while (it != const_list->end()) {
399 #endif // NO_COMPILE_TESTS
401 // TODO(pwbug/88): These tests should trigger a CHECK failure. This requires
402 // using a testing version of pw_assert.
403 #define TESTING_CHECK_FAILURES_IS_SUPPORTED 0
404 #if TESTING_CHECK_FAILURES_IS_SUPPORTED
405 TEST(IntrusiveList, Construct_DuplicateItems) {
407 IntrusiveList<TestItem> list({&item, &item});
410 TEST(IntrusiveList, InsertAfter_SameItem) {
412 IntrusiveList<TestItem> list({&item});
414 list.insert_after(list.begin(), item);
417 TEST(IntrusiveList, InsertAfter_SameItemAfterEnd) {
419 IntrusiveList<TestItem> list({&item});
421 list.insert_after(list.end(), item);
424 TEST(IntrusiveList, PushBack_SameItem) {
426 IntrusiveList<TestItem> list({&item});
428 list.push_back(item);
431 TEST(IntrusiveList, PushFront_SameItem) {
433 IntrusiveList<TestItem> list({&item});
435 list.push_front(item);
437 #endif // TESTING_CHECK_FAILURES_IS_SUPPORTED
439 TEST(IntrusiveList, EraseAfter_FirstItem) {
440 std::array<TestItem, 3> items{{{0}, {1}, {2}}};
441 IntrusiveList<TestItem> list(items.begin(), items.end());
443 auto it = list.erase_after(list.before_begin());
444 EXPECT_EQ(list.begin(), it);
445 EXPECT_EQ(&items[1], &list.front());
448 TEST(IntrusiveList, EraseAfter_LastItem) {
449 std::array<TestItem, 3> items{{{0}, {1}, {2}}};
450 IntrusiveList<TestItem> list(items.begin(), items.end());
452 auto it = list.begin();
455 it = list.erase_after(it);
456 EXPECT_EQ(list.end(), it);
461 EXPECT_EQ(&items[1], &(*it));
464 TEST(IntrusiveList, EraseAfter_AllItems) {
465 std::array<TestItem, 3> items{{{0}, {1}, {2}}};
466 IntrusiveList<TestItem> list(items.begin(), items.end());
468 list.erase_after(list.begin());
469 list.erase_after(list.begin());
470 auto it = list.erase_after(list.before_begin());
472 EXPECT_EQ(list.end(), it);
473 EXPECT_TRUE(list.empty());
476 TEST(IntrusiveList, Remove_EmptyList) {
477 std::array<TestItem, 1> items{{{3}}};
478 IntrusiveList<TestItem> list(items.begin(), items.begin()); // Add nothing!
480 EXPECT_TRUE(list.empty());
481 EXPECT_FALSE(list.remove(items[0]));
484 TEST(IntrusiveList, Remove_SingleItem_NotPresent) {
485 std::array<TestItem, 1> items{{{1}}};
486 IntrusiveList<TestItem> list(items.begin(), items.end());
488 EXPECT_FALSE(list.remove(TestItem(1)));
489 EXPECT_EQ(&items.front(), &list.front());
492 TEST(IntrusiveList, Remove_SingleItem_Removed) {
493 std::array<TestItem, 1> items{{{1}}};
494 IntrusiveList<TestItem> list(items.begin(), items.end());
496 EXPECT_TRUE(list.remove(items[0]));
497 EXPECT_TRUE(list.empty());
500 TEST(IntrusiveList, Remove_MultipleItems_NotPresent) {
501 std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
502 IntrusiveList<TestItem> list(items.begin(), items.end());
504 EXPECT_FALSE(list.remove(TestItem(1)));
507 TEST(IntrusiveList, Remove_MultipleItems_RemoveAndPushBack) {
508 std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
509 IntrusiveList<TestItem> list(items.begin(), items.end());
511 EXPECT_TRUE(list.remove(items[0]));
512 EXPECT_TRUE(list.remove(items[3]));
513 list.push_back(items[0]); // Make sure can add the item after removing it.
515 auto it = list.begin();
516 EXPECT_EQ(&items[1], &(*it++));
517 EXPECT_EQ(&items[2], &(*it++));
518 EXPECT_EQ(&items[4], &(*it++));
519 EXPECT_EQ(&items[0], &(*it++));
520 EXPECT_EQ(list.end(), it);
523 TEST(IntrusiveList, ItemsRemoveThemselvesFromListsWhenDestructed) {
524 // Create a list with some items it.
526 IntrusiveList<TestItem> list;
532 // Insert items that will be destructed before the list.
540 auto it = list.begin();
541 EXPECT_EQ(&w, &(*it++));
542 EXPECT_EQ(&y, &(*it++));
543 EXPECT_EQ(&a, &(*it++));
544 EXPECT_EQ(&b, &(*it++));
545 EXPECT_EQ(&c, &(*it++));
546 EXPECT_EQ(&d, &(*it++));
547 EXPECT_EQ(&x, &(*it++));
548 EXPECT_EQ(&z, &(*it++));
549 EXPECT_EQ(list.end(), it);
551 // Here, x, y, z, w are removed from the list for the destructor.
554 // Ensure we get back our original list.
555 auto it = list.begin();
556 EXPECT_EQ(&a, &(*it++));
557 EXPECT_EQ(&b, &(*it++));
558 EXPECT_EQ(&c, &(*it++));
559 EXPECT_EQ(&d, &(*it++));
560 EXPECT_EQ(list.end(), it);
563 TEST(IntrusiveList, SizeBasic) {
564 IntrusiveList<TestItem> list;
565 EXPECT_EQ(list.size(), 0u);
568 list.push_front(one);
569 EXPECT_EQ(list.size(), static_cast<size_t>(1));
573 EXPECT_EQ(list.size(), static_cast<size_t>(2));
577 EXPECT_EQ(list.size(), static_cast<size_t>(3));
580 TEST(IntrusiveList, SizeScoped) {
581 IntrusiveList<TestItem> list;
582 EXPECT_EQ(list.size(), 0u);
584 // Add elements in new scopes; verify size on the way in and on the way out.
588 EXPECT_EQ(list.size(), static_cast<size_t>(1));
593 EXPECT_EQ(list.size(), static_cast<size_t>(2));
597 EXPECT_EQ(list.size(), static_cast<size_t>(3));
599 EXPECT_EQ(list.size(), static_cast<size_t>(2));
601 EXPECT_EQ(list.size(), static_cast<size_t>(1));
603 EXPECT_EQ(list.size(), static_cast<size_t>(0));