• 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 "gtest/gtest.h"
22 #include "pw_preprocessor/util.h"
23 
24 namespace pw {
25 namespace {
26 
27 class TestItem : public IntrusiveList<TestItem>::Item {
28  public:
TestItem()29   TestItem() : number_(0) {}
TestItem(int number)30   TestItem(int number) : number_(number) {}
31 
GetNumber() const32   int GetNumber() const { return number_; }
SetNumber(int num)33   void SetNumber(int num) { number_ = num; }
34 
35   // Add equality comparison to ensure comparisons are done by identity rather
36   // than equality for the remove function.
operator ==(const TestItem & other) const37   bool operator==(const TestItem& other) const {
38     return number_ == other.number_;
39   }
40 
41  private:
42   int number_;
43 };
44 
TEST(IntrusiveList,Construct_InitializerList_Empty)45 TEST(IntrusiveList, Construct_InitializerList_Empty) {
46   IntrusiveList<TestItem> list({});
47   EXPECT_TRUE(list.empty());
48 }
49 
TEST(IntrusiveList,Construct_InitializerList_One)50 TEST(IntrusiveList, Construct_InitializerList_One) {
51   TestItem one(1);
52   IntrusiveList<TestItem> list({&one});
53 
54   EXPECT_EQ(&one, &list.front());
55 }
56 
TEST(IntrusiveList,Construct_InitializerList_Multiple)57 TEST(IntrusiveList, Construct_InitializerList_Multiple) {
58   TestItem one(1);
59   TestItem two(2);
60   TestItem thr(3);
61 
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);
68 }
69 
TEST(IntrusiveList,Construct_ObjectIterator_Empty)70 TEST(IntrusiveList, Construct_ObjectIterator_Empty) {
71   std::array<TestItem, 0> array;
72   IntrusiveList<TestItem> list(array.begin(), array.end());
73 
74   EXPECT_TRUE(list.empty());
75 }
76 
TEST(IntrusiveList,Construct_ObjectIterator_One)77 TEST(IntrusiveList, Construct_ObjectIterator_One) {
78   std::array<TestItem, 1> array{{{1}}};
79   IntrusiveList<TestItem> list(array.begin(), array.end());
80 
81   EXPECT_EQ(&array.front(), &list.front());
82 }
83 
TEST(IntrusiveList,Construct_ObjectIterator_Multiple)84 TEST(IntrusiveList, Construct_ObjectIterator_Multiple) {
85   std::array<TestItem, 3> array{{{1}, {2}, {3}}};
86 
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);
93 }
94 
TEST(IntrusiveList,Construct_PointerIterator_Empty)95 TEST(IntrusiveList, Construct_PointerIterator_Empty) {
96   std::array<TestItem*, 0> array;
97   IntrusiveList<TestItem> list(array.begin(), array.end());
98 
99   EXPECT_TRUE(list.empty());
100 }
101 
TEST(IntrusiveList,Construct_PointerIterator_One)102 TEST(IntrusiveList, Construct_PointerIterator_One) {
103   std::array<TestItem, 1> array{{{1}}};
104   std::array<TestItem*, 1> ptrs{{&array[0]}};
105 
106   IntrusiveList<TestItem> list(ptrs.begin(), ptrs.end());
107 
108   EXPECT_EQ(ptrs[0], &list.front());
109 }
110 
TEST(IntrusiveList,Construct_PointerIterator_Multiple)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]}};
114 
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);
121 }
122 
TEST(IntrusiveList,Assign_ReplacesPriorContents)123 TEST(IntrusiveList, Assign_ReplacesPriorContents) {
124   std::array<TestItem, 3> array{{{0}, {100}, {200}}};
125   IntrusiveList<TestItem> list(array.begin(), array.end());
126 
127   list.assign(array.begin() + 1, array.begin() + 2);
128 
129   auto it = list.begin();
130   EXPECT_EQ(&array[1], &(*it++));
131   EXPECT_EQ(list.end(), it);
132 }
133 
TEST(IntrusiveList,Assign_EmptyRange)134 TEST(IntrusiveList, Assign_EmptyRange) {
135   std::array<TestItem, 3> array{{{0}, {100}, {200}}};
136   IntrusiveList<TestItem> list(array.begin(), array.end());
137 
138   list.assign(array.begin() + 1, array.begin() + 1);
139 
140   EXPECT_TRUE(list.empty());
141 }
142 
TEST(IntrusiveList,PushOne)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);
150 }
151 
TEST(IntrusiveList,PushThree)152 TEST(IntrusiveList, PushThree) {
153   TestItem item1(1);
154   TestItem item2(2);
155   TestItem item3(3);
156 
157   IntrusiveList<TestItem> list;
158   list.push_back(item1);
159   list.push_back(item2);
160   list.push_back(item3);
161 
162   int loop_count = 0;
163   for (auto& test_item : list) {
164     loop_count++;
165     EXPECT_EQ(loop_count, test_item.GetNumber());
166   }
167   EXPECT_EQ(loop_count, 3);
168 }
169 
TEST(IntrusiveList,IsEmpty)170 TEST(IntrusiveList, IsEmpty) {
171   TestItem item1(1);
172 
173   IntrusiveList<TestItem> list;
174   EXPECT_TRUE(list.empty());
175 
176   list.push_back(item1);
177   EXPECT_FALSE(list.empty());
178 }
179 
TEST(IntrusiveList,InsertAfter)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);
184 
185   // Create initial values to fill in the start/end.
186   TestItem item_array[20];
187 
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]);
193   }
194 
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) {
199     it++;
200     expected_index++;
201   }
202   it = list.insert_after(it, inserted_item);
203 
204   // Ensure the returned iterator from insert_after is the newly inserted
205   // element.
206   EXPECT_EQ(it->GetNumber(), kMagicValue);
207 
208   // Ensure the value is in the expected location (index of the iterator + 1).
209   size_t i = 0;
210   for (TestItem& item : list) {
211     if (item.GetNumber() == kMagicValue) {
212       EXPECT_EQ(i, expected_index);
213     } else {
214       EXPECT_EQ(item.GetNumber(), 0);
215     }
216     i++;
217   }
218 
219   // Ensure the list didn't break and change sizes.
220   EXPECT_EQ(i, PW_ARRAY_SIZE(item_array) + 1);
221 }
222 
TEST(IntrusiveList,InsertAfterBeforeBegin)223 TEST(IntrusiveList, InsertAfterBeforeBegin) {
224   // Create a test item to insert at the beginning of the list.
225   constexpr int kMagicValue = 42;
226   TestItem inserted_item(kMagicValue);
227 
228   // Create initial values to fill in the start/end.
229   TestItem item_array[20];
230 
231   IntrusiveList<TestItem> list;
232   // Fill the list with TestItem objects that have a value of zero.
233   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
234     item_array[i].SetNumber(0);
235     list.push_back(item_array[i]);
236   }
237 
238   auto it = list.insert_after(list.before_begin(), inserted_item);
239 
240   // Ensure the returned iterator from insert_after is the newly inserted
241   // element.
242   EXPECT_EQ(it->GetNumber(), kMagicValue);
243 
244   // Ensure the value is at the beginning of the list.
245   size_t i = 0;
246   for (TestItem& item : list) {
247     if (item.GetNumber() == kMagicValue) {
248       EXPECT_EQ(i, static_cast<size_t>(0));
249     } else {
250       EXPECT_EQ(item.GetNumber(), 0);
251     }
252     i++;
253   }
254 }
255 
TEST(IntrusiveList,PushFront)256 TEST(IntrusiveList, PushFront) {
257   constexpr int kMagicValue = 42;
258   TestItem pushed_item(kMagicValue);
259 
260   TestItem item_array[20];
261   IntrusiveList<TestItem> list;
262   // Fill the list with TestItem objects that have a value of zero.
263   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
264     item_array[i].SetNumber(0);
265     list.push_back(item_array[i]);
266   }
267 
268   // Create a test item to push to the front of the list.
269   list.push_front(pushed_item);
270   EXPECT_EQ(list.front().GetNumber(), kMagicValue);
271 }
272 
TEST(IntrusiveList,Clear_Empty)273 TEST(IntrusiveList, Clear_Empty) {
274   IntrusiveList<TestItem> list;
275   EXPECT_TRUE(list.empty());
276   list.clear();
277   EXPECT_TRUE(list.empty());
278 }
279 
TEST(IntrusiveList,Clear_OneItem)280 TEST(IntrusiveList, Clear_OneItem) {
281   TestItem item(42);
282   IntrusiveList<TestItem> list;
283   list.push_back(item);
284   EXPECT_FALSE(list.empty());
285   list.clear();
286   EXPECT_TRUE(list.empty());
287 }
288 
TEST(IntrusiveList,Clear_TwoItems)289 TEST(IntrusiveList, Clear_TwoItems) {
290   TestItem item1(42);
291   TestItem item2(42);
292   IntrusiveList<TestItem> list;
293   list.push_back(item1);
294   list.push_back(item2);
295   EXPECT_FALSE(list.empty());
296   list.clear();
297   EXPECT_TRUE(list.empty());
298 }
299 
TEST(IntrusiveList,Clear_ReinsertClearedItems)300 TEST(IntrusiveList, Clear_ReinsertClearedItems) {
301   std::array<TestItem, 20> item_array;
302   IntrusiveList<TestItem> list;
303   EXPECT_TRUE(list.empty());
304   list.clear();
305   EXPECT_TRUE(list.empty());
306 
307   // Fill the list with TestItem objects.
308   for (size_t i = 0; i < item_array.size(); ++i) {
309     item_array[i].SetNumber(0);
310     list.push_back(item_array[i]);
311   }
312 
313   // Remove everything.
314   list.clear();
315   EXPECT_TRUE(list.empty());
316 
317   // Ensure all the removed elements can still be added back to a list.
318   for (size_t i = 0; i < item_array.size(); ++i) {
319     item_array[i].SetNumber(0);
320     list.push_back(item_array[i]);
321   }
322 }
323 
TEST(IntrusiveList,PopFront)324 TEST(IntrusiveList, PopFront) {
325   constexpr int kValue1 = 32;
326   constexpr int kValue2 = 4083;
327 
328   TestItem item1(kValue1);
329   TestItem item2(kValue2);
330 
331   IntrusiveList<TestItem> list;
332   EXPECT_TRUE(list.empty());
333 
334   list.push_front(item2);
335   list.push_front(item1);
336   list.pop_front();
337   EXPECT_EQ(list.front().GetNumber(), kValue2);
338   EXPECT_FALSE(list.empty());
339   list.pop_front();
340   EXPECT_TRUE(list.empty());
341 }
342 
TEST(IntrusiveList,PopFrontAndReinsert)343 TEST(IntrusiveList, PopFrontAndReinsert) {
344   constexpr int kValue1 = 32;
345   constexpr int kValue2 = 4083;
346 
347   TestItem item1(kValue1);
348   TestItem item2(kValue2);
349 
350   IntrusiveList<TestItem> list;
351   EXPECT_TRUE(list.empty());
352 
353   list.push_front(item2);
354   list.push_front(item1);
355   list.pop_front();
356   list.push_front(item1);
357   EXPECT_EQ(list.front().GetNumber(), kValue1);
358 }
359 
TEST(IntrusiveList,ListFront)360 TEST(IntrusiveList, ListFront) {
361   TestItem item1(1);
362   TestItem item2(0);
363   TestItem item3(0xffff);
364 
365   IntrusiveList<TestItem> list;
366   list.push_back(item1);
367   list.push_back(item2);
368   list.push_back(item3);
369 
370   EXPECT_EQ(&item1, &list.front());
371   EXPECT_EQ(&item1, &(*list.begin()));
372 }
373 
TEST(IntrusiveList,IteratorIncrement)374 TEST(IntrusiveList, IteratorIncrement) {
375   TestItem item_array[20];
376   IntrusiveList<TestItem> list;
377   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
378     item_array[i].SetNumber(i);
379     list.push_back(item_array[i]);
380   }
381 
382   auto it = list.begin();
383   int i = 0;
384   while (it != list.end()) {
385     if (i == 0) {
386       // Test pre-incrementing on the first element.
387       EXPECT_EQ((++it)->GetNumber(), item_array[++i].GetNumber());
388     } else {
389       EXPECT_EQ((it++)->GetNumber(), item_array[i++].GetNumber());
390     }
391   }
392 }
393 
TEST(IntrusiveList,ConstIteratorRead)394 TEST(IntrusiveList, ConstIteratorRead) {
395   // For this test, items are checked to be non-zero.
396   TestItem item1(1);
397   TestItem item2(99);
398   IntrusiveList<TestItem> list;
399 
400   const IntrusiveList<TestItem>* const_list = &list;
401 
402   list.push_back(item1);
403   list.push_back(item2);
404 
405   auto it = const_list->begin();
406   while (it != const_list->end()) {
407     EXPECT_NE(it->GetNumber(), 0);
408     it++;
409   }
410 }
411 
TEST(IntrusiveList,CompareConstAndNonConstIterator)412 TEST(IntrusiveList, CompareConstAndNonConstIterator) {
413   IntrusiveList<TestItem> list;
414   EXPECT_EQ(list.end(), list.cend());
415 }
416 
417 #if defined(PW_COMPILE_FAIL_TEST_incompatible_iterator_types)
418 
419 struct OtherItem : public IntrusiveList<OtherItem>::Item {};
420 
TEST(IntrusiveList,CompareConstAndNonConstIterator_CompilationFails)421 TEST(IntrusiveList, CompareConstAndNonConstIterator_CompilationFails) {
422   IntrusiveList<TestItem> list;
423   IntrusiveList<OtherItem> list2;
424   static_cast<void>(list.end() == list2.end());
425 }
426 
427 #endif
428 
429 // TODO(pwbug/47): These tests should fail to compile, enable when no-compile
430 // tests are set up in Pigweed.
431 #if defined(PW_COMPILE_FAIL_TEST_cannot_modify_through_const_iterator)
TEST(IntrusiveList,ConstIteratorModify)432 TEST(IntrusiveList, ConstIteratorModify) {
433   TestItem item1(1);
434   TestItem item2(99);
435   IntrusiveList<TestItem> list;
436 
437   const IntrusiveList<TestItem>* const_list = &list;
438 
439   list.push_back(item1);
440   list.push_back(item2);
441 
442   auto it = const_list->begin();
443   while (it != const_list->end()) {
444     it->SetNumber(0);
445     it++;
446   }
447 }
448 #endif  // Compile failure test
449 
450 // TODO(pwbug/88): These tests should trigger a CHECK failure. This requires
451 // using a testing version of pw_assert.
452 #define TESTING_CHECK_FAILURES_IS_SUPPORTED 0
453 #if TESTING_CHECK_FAILURES_IS_SUPPORTED
TEST(IntrusiveList,Construct_DuplicateItems)454 TEST(IntrusiveList, Construct_DuplicateItems) {
455   TestItem item(1);
456   IntrusiveList<TestItem> list({&item, &item});
457 }
458 
TEST(IntrusiveList,InsertAfter_SameItem)459 TEST(IntrusiveList, InsertAfter_SameItem) {
460   TestItem item(1);
461   IntrusiveList<TestItem> list({&item});
462 
463   list.insert_after(list.begin(), item);
464 }
465 
TEST(IntrusiveList,InsertAfter_SameItemAfterEnd)466 TEST(IntrusiveList, InsertAfter_SameItemAfterEnd) {
467   TestItem item(1);
468   IntrusiveList<TestItem> list({&item});
469 
470   list.insert_after(list.end(), item);
471 }
472 
TEST(IntrusiveList,PushBack_SameItem)473 TEST(IntrusiveList, PushBack_SameItem) {
474   TestItem item(1);
475   IntrusiveList<TestItem> list({&item});
476 
477   list.push_back(item);
478 }
479 
TEST(IntrusiveList,PushFront_SameItem)480 TEST(IntrusiveList, PushFront_SameItem) {
481   TestItem item(1);
482   IntrusiveList<TestItem> list({&item});
483 
484   list.push_front(item);
485 }
486 #endif  // TESTING_CHECK_FAILURES_IS_SUPPORTED
487 
TEST(IntrusiveList,EraseAfter_FirstItem)488 TEST(IntrusiveList, EraseAfter_FirstItem) {
489   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
490   IntrusiveList<TestItem> list(items.begin(), items.end());
491 
492   auto it = list.erase_after(list.before_begin());
493   EXPECT_EQ(list.begin(), it);
494   EXPECT_EQ(&items[1], &list.front());
495 }
496 
TEST(IntrusiveList,EraseAfter_LastItem)497 TEST(IntrusiveList, EraseAfter_LastItem) {
498   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
499   IntrusiveList<TestItem> list(items.begin(), items.end());
500 
501   auto it = list.begin();
502   ++it;
503 
504   it = list.erase_after(it);
505   EXPECT_EQ(list.end(), it);
506 
507   it = list.begin();
508   ++it;
509 
510   EXPECT_EQ(&items[1], &(*it));
511 }
512 
TEST(IntrusiveList,EraseAfter_AllItems)513 TEST(IntrusiveList, EraseAfter_AllItems) {
514   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
515   IntrusiveList<TestItem> list(items.begin(), items.end());
516 
517   list.erase_after(list.begin());
518   list.erase_after(list.begin());
519   auto it = list.erase_after(list.before_begin());
520 
521   EXPECT_EQ(list.end(), it);
522   EXPECT_TRUE(list.empty());
523 }
524 
TEST(IntrusiveList,Remove_EmptyList)525 TEST(IntrusiveList, Remove_EmptyList) {
526   std::array<TestItem, 1> items{{{3}}};
527   IntrusiveList<TestItem> list(items.begin(), items.begin());  // Add nothing!
528 
529   EXPECT_TRUE(list.empty());
530   EXPECT_FALSE(list.remove(items[0]));
531 }
532 
TEST(IntrusiveList,Remove_SingleItem_NotPresent)533 TEST(IntrusiveList, Remove_SingleItem_NotPresent) {
534   std::array<TestItem, 1> items{{{1}}};
535   IntrusiveList<TestItem> list(items.begin(), items.end());
536 
537   EXPECT_FALSE(list.remove(TestItem(1)));
538   EXPECT_EQ(&items.front(), &list.front());
539 }
540 
TEST(IntrusiveList,Remove_SingleItem_Removed)541 TEST(IntrusiveList, Remove_SingleItem_Removed) {
542   std::array<TestItem, 1> items{{{1}}};
543   IntrusiveList<TestItem> list(items.begin(), items.end());
544 
545   EXPECT_TRUE(list.remove(items[0]));
546   EXPECT_TRUE(list.empty());
547 }
548 
TEST(IntrusiveList,Remove_MultipleItems_NotPresent)549 TEST(IntrusiveList, Remove_MultipleItems_NotPresent) {
550   std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
551   IntrusiveList<TestItem> list(items.begin(), items.end());
552 
553   EXPECT_FALSE(list.remove(TestItem(1)));
554 }
555 
TEST(IntrusiveList,Remove_MultipleItems_RemoveAndPushBack)556 TEST(IntrusiveList, Remove_MultipleItems_RemoveAndPushBack) {
557   std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
558   IntrusiveList<TestItem> list(items.begin(), items.end());
559 
560   EXPECT_TRUE(list.remove(items[0]));
561   EXPECT_TRUE(list.remove(items[3]));
562   list.push_back(items[0]);  // Make sure can add the item after removing it.
563 
564   auto it = list.begin();
565   EXPECT_EQ(&items[1], &(*it++));
566   EXPECT_EQ(&items[2], &(*it++));
567   EXPECT_EQ(&items[4], &(*it++));
568   EXPECT_EQ(&items[0], &(*it++));
569   EXPECT_EQ(list.end(), it);
570 }
571 
TEST(IntrusiveList,ItemsRemoveThemselvesFromListsWhenDestructed)572 TEST(IntrusiveList, ItemsRemoveThemselvesFromListsWhenDestructed) {
573   // Create a list with some items it.
574   TestItem a, b, c, d;
575   IntrusiveList<TestItem> list;
576   list.push_back(a);
577   list.push_back(b);
578   list.push_back(c);
579   list.push_back(d);
580 
581   // Insert items that will be destructed before the list.
582   {
583     TestItem x, y, z, w;
584     list.push_back(x);
585     list.push_back(z);
586     list.push_front(y);
587     list.push_front(w);
588 
589     auto it = list.begin();
590     EXPECT_EQ(&w, &(*it++));
591     EXPECT_EQ(&y, &(*it++));
592     EXPECT_EQ(&a, &(*it++));
593     EXPECT_EQ(&b, &(*it++));
594     EXPECT_EQ(&c, &(*it++));
595     EXPECT_EQ(&d, &(*it++));
596     EXPECT_EQ(&x, &(*it++));
597     EXPECT_EQ(&z, &(*it++));
598     EXPECT_EQ(list.end(), it);
599 
600     // Here, x, y, z, w are removed from the list for the destructor.
601   }
602 
603   // Ensure we get back our original list.
604   auto it = list.begin();
605   EXPECT_EQ(&a, &(*it++));
606   EXPECT_EQ(&b, &(*it++));
607   EXPECT_EQ(&c, &(*it++));
608   EXPECT_EQ(&d, &(*it++));
609   EXPECT_EQ(list.end(), it);
610 }
611 
TEST(IntrusiveList,SizeBasic)612 TEST(IntrusiveList, SizeBasic) {
613   IntrusiveList<TestItem> list;
614   EXPECT_EQ(list.size(), 0u);
615 
616   TestItem one(55);
617   list.push_front(one);
618   EXPECT_EQ(list.size(), static_cast<size_t>(1));
619 
620   TestItem two(66);
621   list.push_back(two);
622   EXPECT_EQ(list.size(), static_cast<size_t>(2));
623 
624   TestItem thr(77);
625   list.push_back(thr);
626   EXPECT_EQ(list.size(), static_cast<size_t>(3));
627 }
628 
TEST(IntrusiveList,SizeScoped)629 TEST(IntrusiveList, SizeScoped) {
630   IntrusiveList<TestItem> list;
631   EXPECT_EQ(list.size(), 0u);
632 
633   // Add elements in new scopes; verify size on the way in and on the way out.
634   {
635     TestItem one(55);
636     list.push_back(one);
637     EXPECT_EQ(list.size(), static_cast<size_t>(1));
638 
639     {
640       TestItem two(66);
641       list.push_back(two);
642       EXPECT_EQ(list.size(), static_cast<size_t>(2));
643       {
644         TestItem thr(77);
645         list.push_back(thr);
646         EXPECT_EQ(list.size(), static_cast<size_t>(3));
647       }
648       EXPECT_EQ(list.size(), static_cast<size_t>(2));
649     }
650     EXPECT_EQ(list.size(), static_cast<size_t>(1));
651   }
652   EXPECT_EQ(list.size(), static_cast<size_t>(0));
653 }
654 
655 // Test that a list of items derived from a different Item class can be created.
656 class DerivedTestItem : public TestItem {};
657 
TEST(InstrusiveList,AddItemsOfDerivedClassToList)658 TEST(InstrusiveList, AddItemsOfDerivedClassToList) {
659   IntrusiveList<TestItem> list;
660 
661   DerivedTestItem item1;
662   list.push_front(item1);
663 
664   TestItem item2;
665   list.push_front(item2);
666 
667   EXPECT_EQ(2u, list.size());
668 }
669 
TEST(InstrusiveList,ListOfDerivedClassItems)670 TEST(InstrusiveList, ListOfDerivedClassItems) {
671   IntrusiveList<DerivedTestItem> derived_from_compatible_item_type;
672 
673   DerivedTestItem item1;
674   derived_from_compatible_item_type.push_front(item1);
675 
676   EXPECT_EQ(1u, derived_from_compatible_item_type.size());
677 
678 // TODO(pwbug/47): Make these proper automated compilation failure tests.
679 #if defined(PW_COMPILE_FAIL_TEST_cannot_add_base_class_to_derived_class_list)
680   TestItem item2;
681   derived_from_compatible_item_type.push_front(item2);
682 #endif
683 }
684 
685 #if defined(PW_COMPILE_FAIL_TEST_incompatibile_item_type)
686 
687 struct Foo {};
688 
689 class BadItem : public IntrusiveList<Foo>::Item {};
690 
691 [[maybe_unused]] IntrusiveList<BadItem> derived_from_incompatible_item_type;
692 
693 #elif defined(PW_COMPILE_FAIL_TEST_does_not_inherit_from_item)
694 
695 struct NotAnItem {};
696 
697 [[maybe_unused]] IntrusiveList<NotAnItem> list;
698 
699 #endif
700 
701 }  // namespace
702 }  // namespace pw
703