• 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,PushFront)223 TEST(IntrusiveList, PushFront) {
224   constexpr int kMagicValue = 42;
225   TestItem pushed_item(kMagicValue);
226 
227   TestItem item_array[20];
228   IntrusiveList<TestItem> list;
229   // Fill the list with TestItem objects that have a value of zero.
230   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
231     item_array[i].SetNumber(0);
232     list.push_back(item_array[i]);
233   }
234 
235   // Create a test item to push to the front of the list.
236   list.push_front(pushed_item);
237   EXPECT_EQ(list.front().GetNumber(), kMagicValue);
238 }
239 
TEST(IntrusiveList,Clear_Empty)240 TEST(IntrusiveList, Clear_Empty) {
241   IntrusiveList<TestItem> list;
242   EXPECT_TRUE(list.empty());
243   list.clear();
244   EXPECT_TRUE(list.empty());
245 }
246 
TEST(IntrusiveList,Clear_OneItem)247 TEST(IntrusiveList, Clear_OneItem) {
248   TestItem item(42);
249   IntrusiveList<TestItem> list;
250   list.push_back(item);
251   EXPECT_FALSE(list.empty());
252   list.clear();
253   EXPECT_TRUE(list.empty());
254 }
255 
TEST(IntrusiveList,Clear_TwoItems)256 TEST(IntrusiveList, Clear_TwoItems) {
257   TestItem item1(42);
258   TestItem item2(42);
259   IntrusiveList<TestItem> list;
260   list.push_back(item1);
261   list.push_back(item2);
262   EXPECT_FALSE(list.empty());
263   list.clear();
264   EXPECT_TRUE(list.empty());
265 }
266 
TEST(IntrusiveList,Clear_ReinsertClearedItems)267 TEST(IntrusiveList, Clear_ReinsertClearedItems) {
268   std::array<TestItem, 20> item_array;
269   IntrusiveList<TestItem> list;
270   EXPECT_TRUE(list.empty());
271   list.clear();
272   EXPECT_TRUE(list.empty());
273 
274   // Fill the list with TestItem objects.
275   for (size_t i = 0; i < item_array.size(); ++i) {
276     item_array[i].SetNumber(0);
277     list.push_back(item_array[i]);
278   }
279 
280   // Remove everything.
281   list.clear();
282   EXPECT_TRUE(list.empty());
283 
284   // Ensure all the removed elements can still be added back to a list.
285   for (size_t i = 0; i < item_array.size(); ++i) {
286     item_array[i].SetNumber(0);
287     list.push_back(item_array[i]);
288   }
289 }
290 
TEST(IntrusiveList,PopFront)291 TEST(IntrusiveList, PopFront) {
292   constexpr int kValue1 = 32;
293   constexpr int kValue2 = 4083;
294 
295   TestItem item1(kValue1);
296   TestItem item2(kValue2);
297 
298   IntrusiveList<TestItem> list;
299   EXPECT_TRUE(list.empty());
300 
301   list.push_front(item2);
302   list.push_front(item1);
303   list.pop_front();
304   EXPECT_EQ(list.front().GetNumber(), kValue2);
305   EXPECT_FALSE(list.empty());
306   list.pop_front();
307   EXPECT_TRUE(list.empty());
308 }
309 
TEST(IntrusiveList,PopFrontAndReinsert)310 TEST(IntrusiveList, PopFrontAndReinsert) {
311   constexpr int kValue1 = 32;
312   constexpr int kValue2 = 4083;
313 
314   TestItem item1(kValue1);
315   TestItem item2(kValue2);
316 
317   IntrusiveList<TestItem> list;
318   EXPECT_TRUE(list.empty());
319 
320   list.push_front(item2);
321   list.push_front(item1);
322   list.pop_front();
323   list.push_front(item1);
324   EXPECT_EQ(list.front().GetNumber(), kValue1);
325 }
326 
TEST(IntrusiveList,ListFront)327 TEST(IntrusiveList, ListFront) {
328   TestItem item1(1);
329   TestItem item2(0);
330   TestItem item3(0xffff);
331 
332   IntrusiveList<TestItem> list;
333   list.push_back(item1);
334   list.push_back(item2);
335   list.push_back(item3);
336 
337   EXPECT_EQ(&item1, &list.front());
338   EXPECT_EQ(&item1, &(*list.begin()));
339 }
340 
TEST(IntrusiveList,IteratorIncrement)341 TEST(IntrusiveList, IteratorIncrement) {
342   TestItem item_array[20];
343   IntrusiveList<TestItem> list;
344   for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
345     item_array[i].SetNumber(i);
346     list.push_back(item_array[i]);
347   }
348 
349   auto it = list.begin();
350   int i = 0;
351   while (it != list.end()) {
352     if (i == 0) {
353       // Test pre-incrementing on the first element.
354       EXPECT_EQ((++it)->GetNumber(), item_array[++i].GetNumber());
355     } else {
356       EXPECT_EQ((it++)->GetNumber(), item_array[i++].GetNumber());
357     }
358   }
359 }
360 
TEST(IntrusiveList,ConstIteratorRead)361 TEST(IntrusiveList, ConstIteratorRead) {
362   // For this test, items are checked to be non-zero.
363   TestItem item1(1);
364   TestItem item2(99);
365   IntrusiveList<TestItem> list;
366 
367   const IntrusiveList<TestItem>* const_list = &list;
368 
369   list.push_back(item1);
370   list.push_back(item2);
371 
372   auto it = const_list->begin();
373   while (it != const_list->end()) {
374     EXPECT_NE(it->GetNumber(), 0);
375     it++;
376   }
377 }
378 
379 // TODO(pwbug/47): These tests should fail to compile, enable when no-compile
380 // tests are set up in Pigweed.
381 #define NO_COMPILE_TESTS 0
382 #if NO_COMPILE_TESTS
TEST(IntrusiveList,ConstIteratorModify)383 TEST(IntrusiveList, ConstIteratorModify) {
384   TestItem item1(1);
385   TestItem item2(99);
386   IntrusiveList<TestItem> list;
387 
388   const IntrusiveList<TestItem>* const_list = &list;
389 
390   list.push_back(item1);
391   list.push_back(item2);
392 
393   auto it = const_list->begin();
394   while (it != const_list->end()) {
395     it->SetNumber(0);
396     it++;
397   }
398 }
399 #endif  // NO_COMPILE_TESTS
400 
401 // TODO(pwbug/88): These tests should trigger a CHECK failure. This requires
402 // using a testing version of pw_assert.
403 #define TESTING_CHECK_FAILURES_IS_SUPPORTED 0
404 #if TESTING_CHECK_FAILURES_IS_SUPPORTED
TEST(IntrusiveList,Construct_DuplicateItems)405 TEST(IntrusiveList, Construct_DuplicateItems) {
406   TestItem item(1);
407   IntrusiveList<TestItem> list({&item, &item});
408 }
409 
TEST(IntrusiveList,InsertAfter_SameItem)410 TEST(IntrusiveList, InsertAfter_SameItem) {
411   TestItem item(1);
412   IntrusiveList<TestItem> list({&item});
413 
414   list.insert_after(list.begin(), item);
415 }
416 
TEST(IntrusiveList,InsertAfter_SameItemAfterEnd)417 TEST(IntrusiveList, InsertAfter_SameItemAfterEnd) {
418   TestItem item(1);
419   IntrusiveList<TestItem> list({&item});
420 
421   list.insert_after(list.end(), item);
422 }
423 
TEST(IntrusiveList,PushBack_SameItem)424 TEST(IntrusiveList, PushBack_SameItem) {
425   TestItem item(1);
426   IntrusiveList<TestItem> list({&item});
427 
428   list.push_back(item);
429 }
430 
TEST(IntrusiveList,PushFront_SameItem)431 TEST(IntrusiveList, PushFront_SameItem) {
432   TestItem item(1);
433   IntrusiveList<TestItem> list({&item});
434 
435   list.push_front(item);
436 }
437 #endif  // TESTING_CHECK_FAILURES_IS_SUPPORTED
438 
TEST(IntrusiveList,EraseAfter_FirstItem)439 TEST(IntrusiveList, EraseAfter_FirstItem) {
440   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
441   IntrusiveList<TestItem> list(items.begin(), items.end());
442 
443   auto it = list.erase_after(list.before_begin());
444   EXPECT_EQ(list.begin(), it);
445   EXPECT_EQ(&items[1], &list.front());
446 }
447 
TEST(IntrusiveList,EraseAfter_LastItem)448 TEST(IntrusiveList, EraseAfter_LastItem) {
449   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
450   IntrusiveList<TestItem> list(items.begin(), items.end());
451 
452   auto it = list.begin();
453   ++it;
454 
455   it = list.erase_after(it);
456   EXPECT_EQ(list.end(), it);
457 
458   it = list.begin();
459   ++it;
460 
461   EXPECT_EQ(&items[1], &(*it));
462 }
463 
TEST(IntrusiveList,EraseAfter_AllItems)464 TEST(IntrusiveList, EraseAfter_AllItems) {
465   std::array<TestItem, 3> items{{{0}, {1}, {2}}};
466   IntrusiveList<TestItem> list(items.begin(), items.end());
467 
468   list.erase_after(list.begin());
469   list.erase_after(list.begin());
470   auto it = list.erase_after(list.before_begin());
471 
472   EXPECT_EQ(list.end(), it);
473   EXPECT_TRUE(list.empty());
474 }
475 
TEST(IntrusiveList,Remove_EmptyList)476 TEST(IntrusiveList, Remove_EmptyList) {
477   std::array<TestItem, 1> items{{{3}}};
478   IntrusiveList<TestItem> list(items.begin(), items.begin());  // Add nothing!
479 
480   EXPECT_TRUE(list.empty());
481   EXPECT_FALSE(list.remove(items[0]));
482 }
483 
TEST(IntrusiveList,Remove_SingleItem_NotPresent)484 TEST(IntrusiveList, Remove_SingleItem_NotPresent) {
485   std::array<TestItem, 1> items{{{1}}};
486   IntrusiveList<TestItem> list(items.begin(), items.end());
487 
488   EXPECT_FALSE(list.remove(TestItem(1)));
489   EXPECT_EQ(&items.front(), &list.front());
490 }
491 
TEST(IntrusiveList,Remove_SingleItem_Removed)492 TEST(IntrusiveList, Remove_SingleItem_Removed) {
493   std::array<TestItem, 1> items{{{1}}};
494   IntrusiveList<TestItem> list(items.begin(), items.end());
495 
496   EXPECT_TRUE(list.remove(items[0]));
497   EXPECT_TRUE(list.empty());
498 }
499 
TEST(IntrusiveList,Remove_MultipleItems_NotPresent)500 TEST(IntrusiveList, Remove_MultipleItems_NotPresent) {
501   std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
502   IntrusiveList<TestItem> list(items.begin(), items.end());
503 
504   EXPECT_FALSE(list.remove(TestItem(1)));
505 }
506 
TEST(IntrusiveList,Remove_MultipleItems_RemoveAndPushBack)507 TEST(IntrusiveList, Remove_MultipleItems_RemoveAndPushBack) {
508   std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
509   IntrusiveList<TestItem> list(items.begin(), items.end());
510 
511   EXPECT_TRUE(list.remove(items[0]));
512   EXPECT_TRUE(list.remove(items[3]));
513   list.push_back(items[0]);  // Make sure can add the item after removing it.
514 
515   auto it = list.begin();
516   EXPECT_EQ(&items[1], &(*it++));
517   EXPECT_EQ(&items[2], &(*it++));
518   EXPECT_EQ(&items[4], &(*it++));
519   EXPECT_EQ(&items[0], &(*it++));
520   EXPECT_EQ(list.end(), it);
521 }
522 
TEST(IntrusiveList,ItemsRemoveThemselvesFromListsWhenDestructed)523 TEST(IntrusiveList, ItemsRemoveThemselvesFromListsWhenDestructed) {
524   // Create a list with some items it.
525   TestItem a, b, c, d;
526   IntrusiveList<TestItem> list;
527   list.push_back(a);
528   list.push_back(b);
529   list.push_back(c);
530   list.push_back(d);
531 
532   // Insert items that will be destructed before the list.
533   {
534     TestItem x, y, z, w;
535     list.push_back(x);
536     list.push_back(z);
537     list.push_front(y);
538     list.push_front(w);
539 
540     auto it = list.begin();
541     EXPECT_EQ(&w, &(*it++));
542     EXPECT_EQ(&y, &(*it++));
543     EXPECT_EQ(&a, &(*it++));
544     EXPECT_EQ(&b, &(*it++));
545     EXPECT_EQ(&c, &(*it++));
546     EXPECT_EQ(&d, &(*it++));
547     EXPECT_EQ(&x, &(*it++));
548     EXPECT_EQ(&z, &(*it++));
549     EXPECT_EQ(list.end(), it);
550 
551     // Here, x, y, z, w are removed from the list for the destructor.
552   }
553 
554   // Ensure we get back our original list.
555   auto it = list.begin();
556   EXPECT_EQ(&a, &(*it++));
557   EXPECT_EQ(&b, &(*it++));
558   EXPECT_EQ(&c, &(*it++));
559   EXPECT_EQ(&d, &(*it++));
560   EXPECT_EQ(list.end(), it);
561 }
562 
TEST(IntrusiveList,SizeBasic)563 TEST(IntrusiveList, SizeBasic) {
564   IntrusiveList<TestItem> list;
565   EXPECT_EQ(list.size(), 0u);
566 
567   TestItem one(55);
568   list.push_front(one);
569   EXPECT_EQ(list.size(), static_cast<size_t>(1));
570 
571   TestItem two(66);
572   list.push_back(two);
573   EXPECT_EQ(list.size(), static_cast<size_t>(2));
574 
575   TestItem thr(77);
576   list.push_back(thr);
577   EXPECT_EQ(list.size(), static_cast<size_t>(3));
578 }
579 
TEST(IntrusiveList,SizeScoped)580 TEST(IntrusiveList, SizeScoped) {
581   IntrusiveList<TestItem> list;
582   EXPECT_EQ(list.size(), 0u);
583 
584   // Add elements in new scopes; verify size on the way in and on the way out.
585   {
586     TestItem one(55);
587     list.push_back(one);
588     EXPECT_EQ(list.size(), static_cast<size_t>(1));
589 
590     {
591       TestItem two(66);
592       list.push_back(two);
593       EXPECT_EQ(list.size(), static_cast<size_t>(2));
594       {
595         TestItem thr(77);
596         list.push_back(thr);
597         EXPECT_EQ(list.size(), static_cast<size_t>(3));
598       }
599       EXPECT_EQ(list.size(), static_cast<size_t>(2));
600     }
601     EXPECT_EQ(list.size(), static_cast<size_t>(1));
602   }
603   EXPECT_EQ(list.size(), static_cast<size_t>(0));
604 }
605 
606 }  // namespace
607 }  // namespace pw
608