• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Pigweed Authors
2 //
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
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
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
13 // the License.
14 
15 #include "pw_containers/intrusive_list.h"
16 
17 #include <array>
18 #include <cstddef>
19 #include <cstdint>
20 
21 #include "pw_compilation_testing/negative_compilation.h"
22 #include "pw_containers/vector.h"
23 #include "pw_preprocessor/util.h"
24 #include "pw_unit_test/framework.h"
25 
26 namespace pw {
27 namespace {
28 
29 class TestItem : public IntrusiveList<TestItem>::Item {
30  public:
TestItem()31   constexpr TestItem() : number_(0) {}
TestItem(int number)32   constexpr TestItem(int number) : number_(number) {}
33 
GetNumber() const34   int GetNumber() const { return number_; }
SetNumber(int num)35   void SetNumber(int num) { number_ = num; }
36 
37   // Add equality comparison to ensure comparisons are done by identity rather
38   // than equality for the remove function.
operator ==(const TestItem & other) const39   bool operator==(const TestItem& other) const {
40     return number_ == other.number_;
41   }
42 
43  private:
44   int number_;
45 };
46 
TEST(IntrusiveList,Construct_InitializerList_Empty)47 TEST(IntrusiveList, Construct_InitializerList_Empty) {
48   IntrusiveList<TestItem> list({});
49   EXPECT_TRUE(list.empty());
50 }
51 
TEST(IntrusiveList,Construct_InitializerList_One)52 TEST(IntrusiveList, Construct_InitializerList_One) {
53   TestItem one(1);
54   IntrusiveList<TestItem> list({&one});
55 
56   EXPECT_EQ(&one, &list.front());
57 }
58 
TEST(IntrusiveList,Construct_InitializerList_Multiple)59 TEST(IntrusiveList, Construct_InitializerList_Multiple) {
60   TestItem one(1);
61   TestItem two(2);
62   TestItem thr(3);
63 
64   IntrusiveList<TestItem> list({&one, &two, &thr});
65   auto it = list.begin();
66   EXPECT_EQ(&one, &(*it++));
67   EXPECT_EQ(&two, &(*it++));
68   EXPECT_EQ(&thr, &(*it++));
69   EXPECT_EQ(list.end(), it);
70 }
71 
TEST(IntrusiveList,Construct_ObjectIterator_Empty)72 TEST(IntrusiveList, Construct_ObjectIterator_Empty) {
73   std::array<TestItem, 0> array;
74   IntrusiveList<TestItem> list(array.begin(), array.end());
75 
76   EXPECT_TRUE(list.empty());
77 }
78 
TEST(IntrusiveList,Construct_ObjectIterator_One)79 TEST(IntrusiveList, Construct_ObjectIterator_One) {
80   std::array<TestItem, 1> array{{{1}}};
81   IntrusiveList<TestItem> list(array.begin(), array.end());
82 
83   EXPECT_EQ(&array.front(), &list.front());
84 }
85 
TEST(IntrusiveList,Construct_ObjectIterator_Multiple)86 TEST(IntrusiveList, Construct_ObjectIterator_Multiple) {
87   std::array<TestItem, 3> array{{{1}, {2}, {3}}};
88 
89   IntrusiveList<TestItem> list(array.begin(), array.end());
90   auto it = list.begin();
91   EXPECT_EQ(&array[0], &(*it++));
92   EXPECT_EQ(&array[1], &(*it++));
93   EXPECT_EQ(&array[2], &(*it++));
94   EXPECT_EQ(list.end(), it);
95 }
96 
TEST(IntrusiveList,Construct_PointerIterator_Empty)97 TEST(IntrusiveList, Construct_PointerIterator_Empty) {
98   std::array<TestItem*, 0> array;
99   IntrusiveList<TestItem> list(array.begin(), array.end());
100 
101   EXPECT_TRUE(list.empty());
102 }
103 
TEST(IntrusiveList,Construct_PointerIterator_One)104 TEST(IntrusiveList, Construct_PointerIterator_One) {
105   std::array<TestItem, 1> array{{{1}}};
106   std::array<TestItem*, 1> ptrs{{&array[0]}};
107 
108   IntrusiveList<TestItem> list(ptrs.begin(), ptrs.end());
109 
110   EXPECT_EQ(ptrs[0], &list.front());
111 }
112 
TEST(IntrusiveList,Construct_PointerIterator_Multiple)113 TEST(IntrusiveList, Construct_PointerIterator_Multiple) {
114   std::array<TestItem, 3> array{{{1}, {2}, {3}}};
115   std::array<TestItem*, 3> ptrs{{&array[0], &array[1], &array[2]}};
116 
117   IntrusiveList<TestItem> list(ptrs.begin(), ptrs.end());
118   auto it = list.begin();
119   EXPECT_EQ(ptrs[0], &(*it++));
120   EXPECT_EQ(ptrs[1], &(*it++));
121   EXPECT_EQ(ptrs[2], &(*it++));
122   EXPECT_EQ(list.end(), it);
123 }
124 
TEST(IntrusiveList,Assign_ReplacesPriorContents)125 TEST(IntrusiveList, Assign_ReplacesPriorContents) {
126   std::array<TestItem, 3> array{{{0}, {100}, {200}}};
127   IntrusiveList<TestItem> list(array.begin(), array.end());
128 
129   list.assign(array.begin() + 1, array.begin() + 2);
130 
131   auto it = list.begin();
132   EXPECT_EQ(&array[1], &(*it++));
133   EXPECT_EQ(list.end(), it);
134 }
135 
TEST(IntrusiveList,Assign_EmptyRange)136 TEST(IntrusiveList, Assign_EmptyRange) {
137   std::array<TestItem, 3> array{{{0}, {100}, {200}}};
138   IntrusiveList<TestItem> list(array.begin(), array.end());
139 
140   list.assign(array.begin() + 1, array.begin() + 1);
141 
142   EXPECT_TRUE(list.empty());
143 }
144 
TEST(IntrusiveList,PushOne)145 TEST(IntrusiveList, PushOne) {
146   constexpr int kMagicValue = 31;
147   TestItem item1(kMagicValue);
148   IntrusiveList<TestItem> list;
149   list.push_back(item1);
150   EXPECT_FALSE(list.empty());
151   EXPECT_EQ(list.front().GetNumber(), kMagicValue);
152 }
153 
TEST(IntrusiveList,PushThree)154 TEST(IntrusiveList, PushThree) {
155   TestItem item1(1);
156   TestItem item2(2);
157   TestItem item3(3);
158 
159   IntrusiveList<TestItem> list;
160   list.push_back(item1);
161   list.push_back(item2);
162   list.push_back(item3);
163 
164   int loop_count = 0;
165   for (auto& test_item : list) {
166     loop_count++;
167     EXPECT_EQ(loop_count, test_item.GetNumber());
168   }
169   EXPECT_EQ(loop_count, 3);
170 }
171 
TEST(IntrusiveList,IsEmpty)172 TEST(IntrusiveList, IsEmpty) {
173   TestItem item1(1);
174 
175   IntrusiveList<TestItem> list;
176   EXPECT_TRUE(list.empty());
177 
178   list.push_back(item1);
179   EXPECT_FALSE(list.empty());
180 }
181 
TEST(IntrusiveList,InsertAfter)182 TEST(IntrusiveList, InsertAfter) {
183   // Create a test item to insert midway through the list.
184   constexpr int kMagicValue = 42;
185   TestItem inserted_item(kMagicValue);
186 
187   // Create initial values to fill in the start/end.
188   TestItem item_array[20];
189 
190   IntrusiveList<TestItem> list;
191   // Fill the list with TestItem objects that have a value of zero.
192   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
193     item_array[i].SetNumber(0);
194     list.push_back(item_array[i]);
195   }
196 
197   // Move an iterator to the middle of the list, and then insert the magic item.
198   auto it = list.begin();
199   size_t expected_index = 1;  // Expected index is iterator index + 1.
200   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array) / 2; ++i) {
201     it++;
202     expected_index++;
203   }
204   it = list.insert_after(it, inserted_item);
205 
206   // Ensure the returned iterator from insert_after is the newly inserted
207   // element.
208   EXPECT_EQ(it->GetNumber(), kMagicValue);
209 
210   // Ensure the value is in the expected location (index of the iterator + 1).
211   size_t i = 0;
212   for (TestItem& item : list) {
213     if (item.GetNumber() == kMagicValue) {
214       EXPECT_EQ(i, expected_index);
215     } else {
216       EXPECT_EQ(item.GetNumber(), 0);
217     }
218     i++;
219   }
220 
221   // Ensure the list didn't break and change sizes.
222   EXPECT_EQ(i, PW_ARRAY_SIZE(item_array) + 1);
223 }
224 
TEST(IntrusiveList,InsertAfterBeforeBegin)225 TEST(IntrusiveList, InsertAfterBeforeBegin) {
226   // Create a test item to insert at the beginning of the list.
227   constexpr int kMagicValue = 42;
228   TestItem inserted_item(kMagicValue);
229 
230   // Create initial values to fill in the start/end.
231   TestItem item_array[20];
232 
233   IntrusiveList<TestItem> list;
234   // Fill the list with TestItem objects that have a value of zero.
235   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
236     item_array[i].SetNumber(0);
237     list.push_back(item_array[i]);
238   }
239 
240   auto it = list.insert_after(list.before_begin(), inserted_item);
241 
242   // Ensure the returned iterator from insert_after is the newly inserted
243   // element.
244   EXPECT_EQ(it->GetNumber(), kMagicValue);
245 
246   // Ensure the value is at the beginning of the list.
247   size_t i = 0;
248   for (TestItem& item : list) {
249     if (item.GetNumber() == kMagicValue) {
250       EXPECT_EQ(i, static_cast<size_t>(0));
251     } else {
252       EXPECT_EQ(item.GetNumber(), 0);
253     }
254     i++;
255   }
256 }
257 
TEST(IntrusiveList,PushFront)258 TEST(IntrusiveList, PushFront) {
259   constexpr int kMagicValue = 42;
260   TestItem pushed_item(kMagicValue);
261 
262   TestItem item_array[20];
263   IntrusiveList<TestItem> list;
264   // Fill the list with TestItem objects that have a value of zero.
265   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
266     item_array[i].SetNumber(0);
267     list.push_back(item_array[i]);
268   }
269 
270   // Create a test item to push to the front of the list.
271   list.push_front(pushed_item);
272   EXPECT_EQ(list.front().GetNumber(), kMagicValue);
273 }
274 
TEST(IntrusiveList,Clear_Empty)275 TEST(IntrusiveList, Clear_Empty) {
276   IntrusiveList<TestItem> list;
277   EXPECT_TRUE(list.empty());
278   list.clear();
279   EXPECT_TRUE(list.empty());
280 }
281 
TEST(IntrusiveList,Clear_OneItem)282 TEST(IntrusiveList, Clear_OneItem) {
283   TestItem item(42);
284   IntrusiveList<TestItem> list;
285   list.push_back(item);
286   EXPECT_FALSE(list.empty());
287   list.clear();
288   EXPECT_TRUE(list.empty());
289 }
290 
TEST(IntrusiveList,Clear_TwoItems)291 TEST(IntrusiveList, Clear_TwoItems) {
292   TestItem item1(42);
293   TestItem item2(42);
294   IntrusiveList<TestItem> list;
295   list.push_back(item1);
296   list.push_back(item2);
297   EXPECT_FALSE(list.empty());
298   list.clear();
299   EXPECT_TRUE(list.empty());
300 }
301 
TEST(IntrusiveList,Clear_ReinsertClearedItems)302 TEST(IntrusiveList, Clear_ReinsertClearedItems) {
303   std::array<TestItem, 20> item_array;
304   IntrusiveList<TestItem> list;
305   EXPECT_TRUE(list.empty());
306   list.clear();
307   EXPECT_TRUE(list.empty());
308 
309   // Fill the list with TestItem objects.
310   for (size_t i = 0; i < item_array.size(); ++i) {
311     item_array[i].SetNumber(0);
312     list.push_back(item_array[i]);
313   }
314 
315   // Remove everything.
316   list.clear();
317   EXPECT_TRUE(list.empty());
318 
319   // Ensure all the removed elements can still be added back to a list.
320   for (size_t i = 0; i < item_array.size(); ++i) {
321     item_array[i].SetNumber(0);
322     list.push_back(item_array[i]);
323   }
324 }
325 
TEST(IntrusiveList,PopFront)326 TEST(IntrusiveList, PopFront) {
327   constexpr int kValue1 = 32;
328   constexpr int kValue2 = 4083;
329 
330   TestItem item1(kValue1);
331   TestItem item2(kValue2);
332 
333   IntrusiveList<TestItem> list;
334   EXPECT_TRUE(list.empty());
335 
336   list.push_front(item2);
337   list.push_front(item1);
338   list.pop_front();
339   EXPECT_EQ(list.front().GetNumber(), kValue2);
340   EXPECT_FALSE(list.empty());
341   list.pop_front();
342   EXPECT_TRUE(list.empty());
343 }
344 
TEST(IntrusiveList,PopFrontAndReinsert)345 TEST(IntrusiveList, PopFrontAndReinsert) {
346   constexpr int kValue1 = 32;
347   constexpr int kValue2 = 4083;
348 
349   TestItem item1(kValue1);
350   TestItem item2(kValue2);
351 
352   IntrusiveList<TestItem> list;
353   EXPECT_TRUE(list.empty());
354 
355   list.push_front(item2);
356   list.push_front(item1);
357   list.pop_front();
358   list.push_front(item1);
359   EXPECT_EQ(list.front().GetNumber(), kValue1);
360 }
361 
TEST(IntrusiveList,ListFront)362 TEST(IntrusiveList, ListFront) {
363   TestItem item1(1);
364   TestItem item2(0);
365   TestItem item3(0xffff);
366 
367   IntrusiveList<TestItem> list;
368   list.push_back(item1);
369   list.push_back(item2);
370   list.push_back(item3);
371 
372   EXPECT_EQ(&item1, &list.front());
373   EXPECT_EQ(&item1, &(*list.begin()));
374 }
375 
TEST(IntrusiveList,IteratorIncrement)376 TEST(IntrusiveList, IteratorIncrement) {
377   TestItem item_array[20];
378   IntrusiveList<TestItem> list;
379   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
380     item_array[i].SetNumber(static_cast<int>(i));
381     list.push_back(item_array[i]);
382   }
383 
384   auto it = list.begin();
385   int i = 0;
386   while (it != list.end()) {
387     if (i == 0) {
388       // Test pre-incrementing on the first element.
389       EXPECT_EQ((++it)->GetNumber(), item_array[++i].GetNumber());
390     } else {
391       EXPECT_EQ((it++)->GetNumber(), item_array[i++].GetNumber());
392     }
393   }
394 }
395 
TEST(IntrusiveList,ConstIteratorRead)396 TEST(IntrusiveList, ConstIteratorRead) {
397   // For this test, items are checked to be non-zero.
398   TestItem item1(1);
399   TestItem item2(99);
400   IntrusiveList<TestItem> list;
401 
402   const IntrusiveList<TestItem>* const_list = &list;
403 
404   list.push_back(item1);
405   list.push_back(item2);
406 
407   auto it = const_list->begin();
408   while (it != const_list->end()) {
409     EXPECT_NE(it->GetNumber(), 0);
410     it++;
411   }
412 }
413 
TEST(IntrusiveList,CompareConstAndNonConstIterator)414 TEST(IntrusiveList, CompareConstAndNonConstIterator) {
415   IntrusiveList<TestItem> list;
416   EXPECT_EQ(list.end(), list.cend());
417 }
418 
419 #if PW_NC_TEST(IncompatibleIteratorTypes)
420 PW_NC_EXPECT("comparison (of|between) distinct pointer types");
421 
422 struct OtherItem : public IntrusiveList<OtherItem>::Item {};
423 
TEST(IntrusiveList,CompareConstAndNonConstIterator_CompilationFails)424 TEST(IntrusiveList, CompareConstAndNonConstIterator_CompilationFails) {
425   IntrusiveList<TestItem> list;
426   IntrusiveList<OtherItem> list2;
427   static_cast<void>(list.end() == list2.end());
428 }
429 
430 #endif  // PW_NC_TEST
431 
432 #if PW_NC_TEST(CannotModifyThroughConstIterator)
433 PW_NC_EXPECT("function is not marked const|discards qualifiers");
434 
TEST(IntrusiveList,ConstIteratorModify)435 TEST(IntrusiveList, ConstIteratorModify) {
436   TestItem item1(1);
437   TestItem item2(99);
438   IntrusiveList<TestItem> list;
439 
440   const IntrusiveList<TestItem>* const_list = &list;
441 
442   list.push_back(item1);
443   list.push_back(item2);
444 
445   auto it = const_list->begin();
446   while (it != const_list->end()) {
447     it->SetNumber(0);
448     it++;
449   }
450 }
451 #endif  // PW_NC_TEST
452 
453 // TODO: b/235289499 - These tests should trigger a CHECK failure. This requires
454 // using a testing version of pw_assert.
455 #define TESTING_CHECK_FAILURES_IS_SUPPORTED 0
456 #if TESTING_CHECK_FAILURES_IS_SUPPORTED
TEST(IntrusiveList,Construct_DuplicateItems)457 TEST(IntrusiveList, Construct_DuplicateItems) {
458   TestItem item(1);
459   IntrusiveList<TestItem> list({&item, &item});
460 }
461 
TEST(IntrusiveList,InsertAfter_SameItem)462 TEST(IntrusiveList, InsertAfter_SameItem) {
463   TestItem item(1);
464   IntrusiveList<TestItem> list({&item});
465 
466   list.insert_after(list.begin(), item);
467 }
468 
TEST(IntrusiveList,InsertAfter_SameItemAfterEnd)469 TEST(IntrusiveList, InsertAfter_SameItemAfterEnd) {
470   TestItem item(1);
471   IntrusiveList<TestItem> list({&item});
472 
473   list.insert_after(list.end(), item);
474 }
475 
TEST(IntrusiveList,PushBack_SameItem)476 TEST(IntrusiveList, PushBack_SameItem) {
477   TestItem item(1);
478   IntrusiveList<TestItem> list({&item});
479 
480   list.push_back(item);
481 }
482 
TEST(IntrusiveList,PushFront_SameItem)483 TEST(IntrusiveList, PushFront_SameItem) {
484   TestItem item(1);
485   IntrusiveList<TestItem> list({&item});
486 
487   list.push_front(item);
488 }
489 #endif  // TESTING_CHECK_FAILURES_IS_SUPPORTED
490 
TEST(IntrusiveList,EraseAfter_FirstItem)491 TEST(IntrusiveList, EraseAfter_FirstItem) {
492   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
493   IntrusiveList<TestItem> list(items.begin(), items.end());
494 
495   auto it = list.erase_after(list.before_begin());
496   EXPECT_EQ(list.begin(), it);
497   EXPECT_EQ(&items[1], &list.front());
498 }
499 
TEST(IntrusiveList,EraseAfter_LastItem)500 TEST(IntrusiveList, EraseAfter_LastItem) {
501   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
502   IntrusiveList<TestItem> list(items.begin(), items.end());
503 
504   auto it = list.begin();
505   ++it;
506 
507   it = list.erase_after(it);
508   EXPECT_EQ(list.end(), it);
509 
510   it = list.begin();
511   ++it;
512 
513   EXPECT_EQ(&items[1], &(*it));
514 }
515 
TEST(IntrusiveList,EraseAfter_AllItems)516 TEST(IntrusiveList, EraseAfter_AllItems) {
517   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
518   IntrusiveList<TestItem> list(items.begin(), items.end());
519 
520   list.erase_after(list.begin());
521   list.erase_after(list.begin());
522   auto it = list.erase_after(list.before_begin());
523 
524   EXPECT_EQ(list.end(), it);
525   EXPECT_TRUE(list.empty());
526 }
527 
TEST(IntrusiveList,Remove_EmptyList)528 TEST(IntrusiveList, Remove_EmptyList) {
529   std::array<TestItem, 1> items{{{3}}};
530   IntrusiveList<TestItem> list(items.begin(), items.begin());  // Add nothing!
531 
532   EXPECT_TRUE(list.empty());
533   EXPECT_FALSE(list.remove(items[0]));
534 }
535 
TEST(IntrusiveList,Remove_SingleItem_NotPresent)536 TEST(IntrusiveList, Remove_SingleItem_NotPresent) {
537   std::array<TestItem, 1> items{{{1}}};
538   IntrusiveList<TestItem> list(items.begin(), items.end());
539 
540   EXPECT_FALSE(list.remove(TestItem(1)));
541   EXPECT_EQ(&items.front(), &list.front());
542 }
543 
TEST(IntrusiveList,Remove_SingleItem_Removed)544 TEST(IntrusiveList, Remove_SingleItem_Removed) {
545   std::array<TestItem, 1> items{{{1}}};
546   IntrusiveList<TestItem> list(items.begin(), items.end());
547 
548   EXPECT_TRUE(list.remove(items[0]));
549   EXPECT_TRUE(list.empty());
550 }
551 
TEST(IntrusiveList,Remove_MultipleItems_NotPresent)552 TEST(IntrusiveList, Remove_MultipleItems_NotPresent) {
553   std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
554   IntrusiveList<TestItem> list(items.begin(), items.end());
555 
556   EXPECT_FALSE(list.remove(TestItem(1)));
557 }
558 
TEST(IntrusiveList,Remove_MultipleItems_RemoveAndPushBack)559 TEST(IntrusiveList, Remove_MultipleItems_RemoveAndPushBack) {
560   std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
561   IntrusiveList<TestItem> list(items.begin(), items.end());
562 
563   EXPECT_TRUE(list.remove(items[0]));
564   EXPECT_TRUE(list.remove(items[3]));
565   list.push_back(items[0]);  // Make sure can add the item after removing it.
566 
567   auto it = list.begin();
568   EXPECT_EQ(&items[1], &(*it++));
569   EXPECT_EQ(&items[2], &(*it++));
570   EXPECT_EQ(&items[4], &(*it++));
571   EXPECT_EQ(&items[0], &(*it++));
572   EXPECT_EQ(list.end(), it);
573 }
574 
TEST(IntrusiveList,ItemsRemoveThemselvesFromListsWhenDestructed)575 TEST(IntrusiveList, ItemsRemoveThemselvesFromListsWhenDestructed) {
576   // Create a list with some items it.
577   TestItem a, b, c, d;
578   IntrusiveList<TestItem> list;
579   list.push_back(a);
580   list.push_back(b);
581   list.push_back(c);
582   list.push_back(d);
583 
584   // Insert items that will be destructed before the list.
585   {
586     TestItem x, y, z, w;
587     list.push_back(x);
588     list.push_back(z);
589     list.push_front(y);
590     list.push_front(w);
591 
592     auto it = list.begin();
593     EXPECT_EQ(&w, &(*it++));
594     EXPECT_EQ(&y, &(*it++));
595     EXPECT_EQ(&a, &(*it++));
596     EXPECT_EQ(&b, &(*it++));
597     EXPECT_EQ(&c, &(*it++));
598     EXPECT_EQ(&d, &(*it++));
599     EXPECT_EQ(&x, &(*it++));
600     EXPECT_EQ(&z, &(*it++));
601     EXPECT_EQ(list.end(), it);
602 
603     // Here, x, y, z, w are removed from the list for the destructor.
604   }
605 
606   // Ensure we get back our original list.
607   auto it = list.begin();
608   EXPECT_EQ(&a, &(*it++));
609   EXPECT_EQ(&b, &(*it++));
610   EXPECT_EQ(&c, &(*it++));
611   EXPECT_EQ(&d, &(*it++));
612   EXPECT_EQ(list.end(), it);
613 }
614 
TEST(IntrusiveList,SizeBasic)615 TEST(IntrusiveList, SizeBasic) {
616   IntrusiveList<TestItem> list;
617   EXPECT_EQ(list.size(), 0u);
618 
619   TestItem one(55);
620   list.push_front(one);
621   EXPECT_EQ(list.size(), static_cast<size_t>(1));
622 
623   TestItem two(66);
624   list.push_back(two);
625   EXPECT_EQ(list.size(), static_cast<size_t>(2));
626 
627   TestItem thr(77);
628   list.push_back(thr);
629   EXPECT_EQ(list.size(), static_cast<size_t>(3));
630 }
631 
TEST(IntrusiveList,SizeScoped)632 TEST(IntrusiveList, SizeScoped) {
633   IntrusiveList<TestItem> list;
634   EXPECT_EQ(list.size(), 0u);
635 
636   // Add elements in new scopes; verify size on the way in and on the way out.
637   {
638     TestItem one(55);
639     list.push_back(one);
640     EXPECT_EQ(list.size(), static_cast<size_t>(1));
641 
642     {
643       TestItem two(66);
644       list.push_back(two);
645       EXPECT_EQ(list.size(), static_cast<size_t>(2));
646       {
647         TestItem thr(77);
648         list.push_back(thr);
649         EXPECT_EQ(list.size(), static_cast<size_t>(3));
650       }
651       EXPECT_EQ(list.size(), static_cast<size_t>(2));
652     }
653     EXPECT_EQ(list.size(), static_cast<size_t>(1));
654   }
655   EXPECT_EQ(list.size(), static_cast<size_t>(0));
656 }
657 
658 // Test that a list of items derived from a different Item class can be created.
659 class DerivedTestItem : public TestItem {};
660 
TEST(IntrusiveList,AddItemsOfDerivedClassToList)661 TEST(IntrusiveList, AddItemsOfDerivedClassToList) {
662   IntrusiveList<TestItem> list;
663 
664   DerivedTestItem item1;
665   list.push_front(item1);
666 
667   TestItem item2;
668   list.push_front(item2);
669 
670   EXPECT_EQ(2u, list.size());
671 }
672 
TEST(IntrusiveList,ListOfDerivedClassItems)673 TEST(IntrusiveList, ListOfDerivedClassItems) {
674   IntrusiveList<DerivedTestItem> derived_from_compatible_item_type;
675 
676   DerivedTestItem item1;
677   derived_from_compatible_item_type.push_front(item1);
678 
679   EXPECT_EQ(1u, derived_from_compatible_item_type.size());
680 
681 #if PW_NC_TEST(CannotAddBaseClassToDerivedClassList)
682   PW_NC_EXPECT_CLANG("cannot bind to a value of unrelated type");
683   PW_NC_EXPECT_GCC("cannot convert");
684 
685   TestItem item2;
686   derived_from_compatible_item_type.push_front(item2);
687 #endif
688 }
689 
690 #if PW_NC_TEST(IncompatibileItemType)
691 PW_NC_EXPECT("IntrusiveList items must be derived from IntrusiveList<T>::Item");
692 
693 struct Foo {};
694 
695 class BadItem : public IntrusiveList<Foo>::Item {};
696 
697 [[maybe_unused]] IntrusiveList<BadItem> derived_from_incompatible_item_type;
698 
699 #elif PW_NC_TEST(DoesNotInheritFromItem)
700 PW_NC_EXPECT("IntrusiveList items must be derived from IntrusiveList<T>::Item");
701 
702 struct NotAnItem {};
703 
704 [[maybe_unused]] IntrusiveList<NotAnItem> list;
705 
706 #endif  // PW_NC_TEST
707 
TEST(IntrusiveList,MoveUnlistedItems)708 TEST(IntrusiveList, MoveUnlistedItems) {
709   TestItem item1(3);
710   EXPECT_EQ(item1.GetNumber(), 3);
711 
712   TestItem item2(std::move(item1));
713   EXPECT_EQ(item2.GetNumber(), 3);
714 
715   TestItem item3;
716   item3 = std::move(item2);
717   EXPECT_EQ(item3.GetNumber(), 3);
718 }
719 
TEST(IntrusiveList,MoveListedItemsToUnlistedItems)720 TEST(IntrusiveList, MoveListedItemsToUnlistedItems) {
721   std::array<TestItem, 2> items{{{1}, {2}}};
722   IntrusiveList<TestItem> list(items.begin(), items.end());
723 
724   TestItem item1(std::move(items[0]));
725   TestItem item2 = std::move(items[1]);
726 
727   ASSERT_EQ(list.size(), 2U);
728   auto iter = list.begin();
729   EXPECT_EQ(iter->GetNumber(), item1.GetNumber());
730   ++iter;
731   EXPECT_EQ(iter->GetNumber(), item2.GetNumber());
732 }
733 
TEST(IntrusiveList,MoveUnlistedItemsToListedItems)734 TEST(IntrusiveList, MoveUnlistedItemsToListedItems) {
735   TestItem item1(1);
736   TestItem item2(2);
737   TestItem item3(3);
738   std::array<TestItem, 3> items{{{4}, {5}, {6}}};
739   IntrusiveList<TestItem> list(items.begin(), items.end());
740   items[0] = std::move(item1);
741   items[1] = std::move(item2);
742   items[2] = std::move(item3);
743   int i = 0;
744   for (const auto& item : list) {
745     EXPECT_EQ(item.GetNumber(), ++i);
746   }
747 }
748 
TEST(IntrusiveList,MoveListedItems)749 TEST(IntrusiveList, MoveListedItems) {
750   std::array<TestItem, 3> src{{{1}, {2}, {3}}};
751   IntrusiveList<TestItem> list1(src.begin(), src.end());
752 
753   std::array<TestItem, 3> dst{{{4}, {5}, {6}}};
754   IntrusiveList<TestItem> list2(dst.begin(), dst.end());
755 
756   for (size_t i = 0; i < 3; ++i) {
757     dst[i] = std::move(src[i]);
758   }
759   int i = 0;
760   for (const auto& item : list1) {
761     EXPECT_EQ(item.GetNumber(), ++i);
762   }
763   EXPECT_TRUE(list2.empty());
764 }
765 
TEST(IntrusiveList,MoveItemsToVector)766 TEST(IntrusiveList, MoveItemsToVector) {
767   Vector<TestItem, 3> vec;
768   vec.emplace_back(TestItem(1));
769   vec.emplace_back(TestItem(2));
770   vec.emplace_back(TestItem(3));
771   IntrusiveList<TestItem> list;
772   list.assign(vec.begin(), vec.end());
773 
774   auto iter = list.begin();
775   for (const auto& item : vec) {
776     EXPECT_NE(iter, list.end());
777     if (iter == list.end()) {
778       break;
779     }
780     EXPECT_EQ(item.GetNumber(), iter->GetNumber());
781     ++iter;
782   }
783 
784   // TODO(b/313899658): Vector has an MSAN bug in its destructor when clearing
785   // items that reference themselves. Workaround it by manually clearing.
786   vec.clear();
787 }
788 
789 }  // namespace
790 }  // namespace pw
791