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