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