blob: 3d666e464fda40beb758ae8116da1edc36eac774 [file] [log] [blame]
Armando Montanez516022c2020-05-14 09:12:19 -07001// 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
Wyatt Hepler70f033e2020-06-10 22:22:27 -070017#include <array>
Armando Montanez516022c2020-05-14 09:12:19 -070018#include <cstddef>
19#include <cstdint>
20
21#include "gtest/gtest.h"
22#include "pw_preprocessor/util.h"
23
24namespace pw {
25namespace {
26
27class TestItem : public IntrusiveList<TestItem>::Item {
28 public:
29 TestItem() : number_(0) {}
30 TestItem(int number) : number_(number) {}
Wyatt Hepler70f033e2020-06-10 22:22:27 -070031
Armando Montanez516022c2020-05-14 09:12:19 -070032 int GetNumber() const { return number_; }
33 void SetNumber(int num) { number_ = num; }
34
Wyatt Hepler70f033e2020-06-10 22:22:27 -070035 // Add equality comparison to ensure comparisons are done by identity rather
36 // than equality for the remove function.
37 bool operator==(const TestItem& other) const {
38 return number_ == other.number_;
39 }
40
Armando Montanez516022c2020-05-14 09:12:19 -070041 private:
42 int number_;
43};
44
Wyatt Hepler70f033e2020-06-10 22:22:27 -070045TEST(IntrusiveList, Construct_InitializerList_Empty) {
46 IntrusiveList<TestItem> list({});
47 EXPECT_TRUE(list.empty());
48}
49
50TEST(IntrusiveList, Construct_InitializerList_One) {
51 TestItem one(1);
52 IntrusiveList<TestItem> list({&one});
53
54 EXPECT_EQ(&one, &list.front());
55}
56
57TEST(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
70TEST(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
77TEST(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
84TEST(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
95TEST(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
102TEST(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
111TEST(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
123TEST(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
134TEST(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
Armando Montanez516022c2020-05-14 09:12:19 -0700143TEST(IntrusiveList, PushOne) {
144 constexpr int kMagicValue = 31;
145 IntrusiveList<TestItem> test_items;
146 TestItem item1(kMagicValue);
147 test_items.push_back(item1);
148 EXPECT_FALSE(test_items.empty());
149 EXPECT_EQ(test_items.front().GetNumber(), kMagicValue);
150}
151
152TEST(IntrusiveList, PushThree) {
153 IntrusiveList<TestItem> test_items;
154 TestItem item1(1);
155 TestItem item2(2);
156 TestItem item3(3);
157 test_items.push_back(item1);
158 test_items.push_back(item2);
159 test_items.push_back(item3);
160
161 int loop_count = 0;
162 for (auto& test_item : test_items) {
163 loop_count++;
164 EXPECT_EQ(loop_count, test_item.GetNumber());
165 }
166 EXPECT_EQ(loop_count, 3);
167}
168
169TEST(IntrusiveList, IsEmpty) {
170 IntrusiveList<TestItem> test_items;
171 EXPECT_TRUE(test_items.empty());
172
173 TestItem item1(1);
174 test_items.push_back(item1);
175 EXPECT_FALSE(test_items.empty());
176}
177
178TEST(IntrusiveList, InsertAfter) {
179 constexpr int kMagicValue = 42;
180 TestItem item_array[20];
181 IntrusiveList<TestItem> test_list;
182 // Fill the list with TestItem objects that have a value of zero.
183 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
184 item_array[i].SetNumber(0);
185 test_list.push_back(item_array[i]);
186 }
187
188 // Create a test item to insert midway through the list.
189 TestItem inserted_item(kMagicValue);
190
191 // Move an iterator to the middle of the list, and then insert the item.
192 auto it = test_list.begin();
193 size_t expected_index = 1; // Expected index is iterator index + 1.
194 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array) / 2; ++i) {
195 it++;
196 expected_index++;
197 }
198 it = test_list.insert_after(it, inserted_item);
199
200 // Ensure the returned iterator from insert_after is the newly inserted
201 // element.
202 EXPECT_EQ(it->GetNumber(), kMagicValue);
203
204 // Ensure the value is in the expected location (index of the iterator + 1).
205 size_t i = 0;
206 for (TestItem& item : test_list) {
207 if (item.GetNumber() == kMagicValue) {
208 EXPECT_EQ(i, expected_index);
209 } else {
210 EXPECT_EQ(item.GetNumber(), 0);
211 }
212 i++;
213 }
214
215 // Ensure the list didn't break and change sizes.
216 EXPECT_EQ(i, PW_ARRAY_SIZE(item_array) + 1);
217}
218
219TEST(IntrusiveList, PushFront) {
220 constexpr int kMagicValue = 42;
221 TestItem item_array[20];
222 IntrusiveList<TestItem> test_list;
223 // Fill the list with TestItem objects that have a value of zero.
224 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
225 item_array[i].SetNumber(0);
226 test_list.push_back(item_array[i]);
227 }
228
229 // Create a test item to push to the front of the list.
230 TestItem pushed_item(kMagicValue);
231 test_list.push_front(pushed_item);
232 EXPECT_EQ(test_list.front().GetNumber(), kMagicValue);
233}
234
235TEST(IntrusiveList, Clear_Empty) {
236 IntrusiveList<TestItem> test_list;
237 EXPECT_TRUE(test_list.empty());
238 test_list.clear();
239 EXPECT_TRUE(test_list.empty());
240}
241
242TEST(IntrusiveList, Clear_OneItem) {
243 IntrusiveList<TestItem> test_list;
244 TestItem item(42);
245 test_list.push_back(item);
246 EXPECT_FALSE(test_list.empty());
247 test_list.clear();
248 EXPECT_TRUE(test_list.empty());
249}
250
251TEST(IntrusiveList, Clear_TwoItems) {
252 IntrusiveList<TestItem> test_list;
253 TestItem item1(42);
254 TestItem item2(42);
255 test_list.push_back(item1);
256 test_list.push_back(item2);
257 EXPECT_FALSE(test_list.empty());
258 test_list.clear();
259 EXPECT_TRUE(test_list.empty());
260}
261
262TEST(IntrusiveList, Clear_ReinsertClearedItems) {
263 std::array<TestItem, 20> item_array;
264 IntrusiveList<TestItem> test_list;
265 EXPECT_TRUE(test_list.empty());
266 test_list.clear();
267 EXPECT_TRUE(test_list.empty());
268
269 // Fill the list with TestItem objects.
270 for (size_t i = 0; i < item_array.size(); ++i) {
271 item_array[i].SetNumber(0);
272 test_list.push_back(item_array[i]);
273 }
274
275 // Remove everything.
276 test_list.clear();
277 EXPECT_TRUE(test_list.empty());
278
279 // Ensure all the removed elements can still be added back to a list.
280 for (size_t i = 0; i < item_array.size(); ++i) {
281 item_array[i].SetNumber(0);
282 test_list.push_back(item_array[i]);
283 }
284}
285
286TEST(IntrusiveList, PopFront) {
287 constexpr int kValue1 = 32;
288 constexpr int kValue2 = 4083;
289
290 IntrusiveList<TestItem> test_list;
291 EXPECT_TRUE(test_list.empty());
292
293 TestItem item1(kValue1);
294 TestItem item2(kValue2);
295
296 test_list.push_front(item2);
297 test_list.push_front(item1);
298 test_list.pop_front();
299 EXPECT_EQ(test_list.front().GetNumber(), kValue2);
300 EXPECT_FALSE(test_list.empty());
301 test_list.pop_front();
302 EXPECT_TRUE(test_list.empty());
303}
304
305TEST(IntrusiveList, PopFrontAndReinsert) {
306 constexpr int kValue1 = 32;
307 constexpr int kValue2 = 4083;
308
309 IntrusiveList<TestItem> test_list;
310 EXPECT_TRUE(test_list.empty());
311
312 TestItem item1(kValue1);
313 TestItem item2(kValue2);
314
315 test_list.push_front(item2);
316 test_list.push_front(item1);
317 test_list.pop_front();
318 test_list.push_front(item1);
319 EXPECT_EQ(test_list.front().GetNumber(), kValue1);
320}
321
322TEST(IntrusiveList, ListFront) {
323 IntrusiveList<TestItem> test_items;
324
Armando Montanez516022c2020-05-14 09:12:19 -0700325 TestItem item1(1);
326 TestItem item2(0);
327 TestItem item3(0xffff);
328
329 test_items.push_back(item1);
330 test_items.push_back(item2);
331 test_items.push_back(item3);
332
333 EXPECT_EQ(&item1, &test_items.front());
334 EXPECT_EQ(&item1, &(*test_items.begin()));
335}
336
337TEST(IntrusiveList, IteratorIncrement) {
338 TestItem item_array[20];
339 IntrusiveList<TestItem> test_list;
340 for (size_t i = 0; i < PW_ARRAY_SIZE(item_array); ++i) {
341 item_array[i].SetNumber(i);
342 test_list.push_back(item_array[i]);
343 }
344
345 auto it = test_list.begin();
346 int i = 0;
347 while (it != test_list.end()) {
348 if (i == 0) {
349 // Test pre-incrementing on the first element.
350 EXPECT_EQ((++it)->GetNumber(), item_array[++i].GetNumber());
351 } else {
352 EXPECT_EQ((it++)->GetNumber(), item_array[i++].GetNumber());
353 }
354 }
355}
356
357TEST(IntrusiveList, ConstIteratorRead) {
358 // For this test, items are checked to be non-zero.
359 TestItem item1(1);
360 TestItem item2(99);
361 IntrusiveList<TestItem> test_items;
362
363 const IntrusiveList<TestItem>* const_list = &test_items;
364
365 test_items.push_back(item1);
366 test_items.push_back(item2);
367
368 auto it = const_list->begin();
369 while (it != const_list->end()) {
370 EXPECT_NE(it->GetNumber(), 0);
371 it++;
372 }
373}
374
375#if NO_COMPILE_TESTS
376// TODO(pwbug/47): These tests should fail to compile, enable when no-compile
377// tests are set up in Pigweed.
378TEST(IntrusiveList, ConstIteratorModify) {
379 TestItem item1(1);
380 TestItem item2(99);
381 IntrusiveList<TestItem> test_items;
382
383 const IntrusiveList<TestItem>* const_list = &test_items;
384
385 test_items.push_back(item1);
386 test_items.push_back(item2);
387
388 auto it = const_list->begin();
389 while (it != const_list->end()) {
390 it->SetNumber(0);
391 it++;
392 }
393}
Wyatt Hepler70f033e2020-06-10 22:22:27 -0700394
Armando Montanez516022c2020-05-14 09:12:19 -0700395#endif // NO_COMPILE_TESTS
396
Wyatt Hepler70f033e2020-06-10 22:22:27 -0700397// TODO(pwbug/88): These tests should trigger a CHECK failure. This requires
398// using a testing version of pw_assert.
399#if TESTING_CHECK_FAILURES_IS_SUPPORTED
400
401TEST(IntrusiveList, Construct_DuplicateItems) {
402 TestItem item(1);
403 IntrusiveList<TestItem> list({&item, &item});
404}
405
406TEST(IntrusiveList, InsertAfter_SameItem) {
407 TestItem item(1);
408 IntrusiveList<TestItem> list({&item});
409
410 list.insert_after(list.begin(), item);
411}
412
413TEST(IntrusiveList, InsertAfter_SameItemAfterEnd) {
414 TestItem item(1);
415 IntrusiveList<TestItem> list({&item});
416
417 list.insert_after(list.end(), item);
418}
419
420TEST(IntrusiveList, PushBack_SameItem) {
421 TestItem item(1);
422 IntrusiveList<TestItem> list({&item});
423
424 list.push_back(item);
425}
426
427TEST(IntrusiveList, PushFront_SameItem) {
428 TestItem item(1);
429 IntrusiveList<TestItem> list({&item});
430
431 list.push_front(item);
432}
433
434#endif // TESTING_CHECK_FAILURES_IS_SUPPORTED
435
436TEST(IntrusiveList, EraseAfter_FirstItem) {
437 std::array<TestItem, 3> items{{{0}, {1}, {2}}};
438 IntrusiveList<TestItem> list(items.begin(), items.end());
439
440 auto it = list.erase_after(list.before_begin());
441 EXPECT_EQ(list.begin(), it);
442 EXPECT_EQ(&items[1], &list.front());
443}
444
445TEST(IntrusiveList, EraseAfter_LastItem) {
446 std::array<TestItem, 3> items{{{0}, {1}, {2}}};
447 IntrusiveList<TestItem> list(items.begin(), items.end());
448
449 auto it = list.begin();
450 ++it;
451
452 it = list.erase_after(it);
453 EXPECT_EQ(list.end(), it);
454
455 it = list.begin();
456 ++it;
457
458 EXPECT_EQ(&items[1], &(*it));
459}
460
461TEST(IntrusiveList, EraseAfter_AllItems) {
462 std::array<TestItem, 3> items{{{0}, {1}, {2}}};
463 IntrusiveList<TestItem> list(items.begin(), items.end());
464
465 list.erase_after(list.begin());
466 list.erase_after(list.begin());
467 auto it = list.erase_after(list.before_begin());
468
469 EXPECT_EQ(list.end(), it);
470 EXPECT_TRUE(list.empty());
471}
472
473TEST(IntrusiveList, Remove_EmptyList) {
474 std::array<TestItem, 1> items{{{3}}};
475 IntrusiveList<TestItem> list(items.begin(), items.begin()); // Add nothing!
476
477 EXPECT_TRUE(list.empty());
478 EXPECT_FALSE(list.remove(items[0]));
479}
480
481TEST(IntrusiveList, Remove_SingleItem_NotPresent) {
482 std::array<TestItem, 1> items{{{1}}};
483 IntrusiveList<TestItem> list(items.begin(), items.end());
484
485 EXPECT_FALSE(list.remove(TestItem(1)));
486 EXPECT_EQ(&items.front(), &list.front());
487}
488
489TEST(IntrusiveList, Remove_SingleItem_Removed) {
490 std::array<TestItem, 1> items{{{1}}};
491 IntrusiveList<TestItem> list(items.begin(), items.end());
492
493 EXPECT_TRUE(list.remove(items[0]));
494 EXPECT_TRUE(list.empty());
495}
496
497TEST(IntrusiveList, Remove_MultipleItems_NotPresent) {
498 std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
499 IntrusiveList<TestItem> list(items.begin(), items.end());
500
501 EXPECT_FALSE(list.remove(TestItem(1)));
502}
503
504TEST(IntrusiveList, Remove_MultipleItems_RemoveAndPushBack) {
505 std::array<TestItem, 5> items{{{1}, {1}, {2}, {3}, {4}}};
506 IntrusiveList<TestItem> list(items.begin(), items.end());
507
508 EXPECT_TRUE(list.remove(items[0]));
509 EXPECT_TRUE(list.remove(items[3]));
510 list.push_back(items[0]); // Make sure can add the item after removing it.
511
512 auto it = list.begin();
513 EXPECT_EQ(&items[1], &(*it++));
514 EXPECT_EQ(&items[2], &(*it++));
515 EXPECT_EQ(&items[4], &(*it++));
516 EXPECT_EQ(&items[0], &(*it++));
517 EXPECT_EQ(list.end(), it);
518}
519
Armando Montanez516022c2020-05-14 09:12:19 -0700520} // namespace
521} // namespace pw