1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/spdy/core/spdy_intrusive_list.h"
6
7 #include <algorithm>
8 #include <iterator>
9 #include <list>
10 #include <string>
11 #include <utility>
12
13 #include "quiche/common/platform/api/quiche_test.h"
14
15 namespace spdy {
16 namespace test {
17
18 struct ListId2 {};
19
20 struct TestItem : public SpdyIntrusiveLink<TestItem>,
21 public SpdyIntrusiveLink<TestItem, ListId2> {
22 int n;
23 };
24 typedef SpdyIntrusiveList<TestItem> TestList;
25 typedef std::list<TestItem *> CanonicalList;
26
swap(TestItem & a,TestItem & b)27 void swap(TestItem &a, TestItem &b) {
28 using std::swap;
29 swap(a.n, b.n);
30 }
31
32 class IntrusiveListTest : public quiche::test::QuicheTest {
33 protected:
CheckLists()34 void CheckLists() {
35 CheckLists(l1, ll1);
36 if (quiche::test::QuicheTest::HasFailure()) return;
37 CheckLists(l2, ll2);
38 }
39
CheckLists(const TestList & list_a,const CanonicalList & list_b)40 void CheckLists(const TestList &list_a, const CanonicalList &list_b) {
41 ASSERT_EQ(list_a.size(), list_b.size());
42 TestList::const_iterator it_a = list_a.begin();
43 CanonicalList::const_iterator it_b = list_b.begin();
44 while (it_a != list_a.end()) {
45 EXPECT_EQ(&*it_a++, *it_b++);
46 }
47 EXPECT_EQ(list_a.end(), it_a);
48 EXPECT_EQ(list_b.end(), it_b);
49 }
50
PrepareLists(int num_elems_1,int num_elems_2=0)51 void PrepareLists(int num_elems_1, int num_elems_2 = 0) {
52 FillLists(&l1, &ll1, e, num_elems_1);
53 FillLists(&l2, &ll2, e + num_elems_1, num_elems_2);
54 }
55
FillLists(TestList * list_a,CanonicalList * list_b,TestItem * elems,int num_elems)56 void FillLists(TestList *list_a, CanonicalList *list_b, TestItem *elems,
57 int num_elems) {
58 list_a->clear();
59 list_b->clear();
60 for (int i = 0; i < num_elems; ++i) {
61 list_a->push_back(elems + i);
62 list_b->push_back(elems + i);
63 }
64 CheckLists(*list_a, *list_b);
65 }
66
67 TestItem e[10];
68 TestList l1, l2;
69 CanonicalList ll1, ll2;
70 };
71
TEST(NewIntrusiveListTest,Basic)72 TEST(NewIntrusiveListTest, Basic) {
73 TestList list1;
74
75 EXPECT_EQ(sizeof(SpdyIntrusiveLink<TestItem>), sizeof(void *) * 2);
76
77 for (int i = 0; i < 10; ++i) {
78 TestItem *e = new TestItem;
79 e->n = i;
80 list1.push_front(e);
81 }
82 EXPECT_EQ(list1.size(), 10u);
83
84 // Verify we can reverse a list because we defined swap for TestItem.
85 std::reverse(list1.begin(), list1.end());
86 EXPECT_EQ(list1.size(), 10u);
87
88 // Check both const and non-const forward iteration.
89 const TestList &clist1 = list1;
90 int i = 0;
91 TestList::iterator iter = list1.begin();
92 for (; iter != list1.end(); ++iter, ++i) {
93 EXPECT_EQ(iter->n, i);
94 }
95 EXPECT_EQ(iter, clist1.end());
96 EXPECT_NE(iter, clist1.begin());
97 i = 0;
98 iter = list1.begin();
99 for (; iter != list1.end(); ++iter, ++i) {
100 EXPECT_EQ(iter->n, i);
101 }
102 EXPECT_EQ(iter, clist1.end());
103 EXPECT_NE(iter, clist1.begin());
104
105 EXPECT_EQ(list1.front().n, 0);
106 EXPECT_EQ(list1.back().n, 9);
107
108 // Verify we can swap 2 lists.
109 TestList list2;
110 list2.swap(list1);
111 EXPECT_EQ(list1.size(), 0u);
112 EXPECT_EQ(list2.size(), 10u);
113
114 // Check both const and non-const reverse iteration.
115 const TestList &clist2 = list2;
116 TestList::reverse_iterator riter = list2.rbegin();
117 i = 9;
118 for (; riter != list2.rend(); ++riter, --i) {
119 EXPECT_EQ(riter->n, i);
120 }
121 EXPECT_EQ(riter, clist2.rend());
122 EXPECT_NE(riter, clist2.rbegin());
123
124 riter = list2.rbegin();
125 i = 9;
126 for (; riter != list2.rend(); ++riter, --i) {
127 EXPECT_EQ(riter->n, i);
128 }
129 EXPECT_EQ(riter, clist2.rend());
130 EXPECT_NE(riter, clist2.rbegin());
131
132 while (!list2.empty()) {
133 TestItem *e = &list2.front();
134 list2.pop_front();
135 delete e;
136 }
137 }
138
TEST(NewIntrusiveListTest,Erase)139 TEST(NewIntrusiveListTest, Erase) {
140 TestList l;
141 TestItem *e[10];
142
143 // Create a list with 10 items.
144 for (int i = 0; i < 10; ++i) {
145 e[i] = new TestItem;
146 l.push_front(e[i]);
147 }
148
149 // Test that erase works.
150 for (int i = 0; i < 10; ++i) {
151 EXPECT_EQ(l.size(), (10u - i));
152
153 TestList::iterator iter = l.erase(e[i]);
154 EXPECT_NE(iter, TestList::iterator(e[i]));
155
156 EXPECT_EQ(l.size(), (10u - i - 1));
157 delete e[i];
158 }
159 }
160
TEST(NewIntrusiveListTest,Insert)161 TEST(NewIntrusiveListTest, Insert) {
162 TestList l;
163 TestList::iterator iter = l.end();
164 TestItem *e[10];
165
166 // Create a list with 10 items.
167 for (int i = 9; i >= 0; --i) {
168 e[i] = new TestItem;
169 iter = l.insert(iter, e[i]);
170 EXPECT_EQ(&(*iter), e[i]);
171 }
172
173 EXPECT_EQ(l.size(), 10u);
174
175 // Verify insertion order.
176 iter = l.begin();
177 for (TestItem *item : e) {
178 EXPECT_EQ(&(*iter), item);
179 iter = l.erase(item);
180 delete item;
181 }
182 }
183
TEST(NewIntrusiveListTest,Move)184 TEST(NewIntrusiveListTest, Move) {
185 // Move contructible.
186
187 { // Move-construct from an empty list.
188 TestList src;
189 TestList dest(std::move(src));
190 EXPECT_TRUE(dest.empty());
191 }
192
193 { // Move-construct from a single item list.
194 TestItem e;
195 TestList src;
196 src.push_front(&e);
197
198 TestList dest(std::move(src));
199 EXPECT_TRUE(src.empty()); // NOLINT bugprone-use-after-move
200 ASSERT_THAT(dest.size(), 1);
201 EXPECT_THAT(&dest.front(), &e);
202 EXPECT_THAT(&dest.back(), &e);
203 }
204
205 { // Move-construct from a list with multiple items.
206 TestItem items[10];
207 TestList src;
208 for (TestItem &e : items) src.push_back(&e);
209
210 TestList dest(std::move(src));
211 EXPECT_TRUE(src.empty()); // NOLINT bugprone-use-after-move
212 // Verify the items on the destination list.
213 ASSERT_THAT(dest.size(), 10);
214 int i = 0;
215 for (TestItem &e : dest) {
216 EXPECT_THAT(&e, &items[i++]) << " for index " << i;
217 }
218 }
219 }
220
TEST(NewIntrusiveListTest,StaticInsertErase)221 TEST(NewIntrusiveListTest, StaticInsertErase) {
222 TestList l;
223 TestItem e[2];
224 TestList::iterator i = l.begin();
225 TestList::insert(i, &e[0]);
226 TestList::insert(&e[0], &e[1]);
227 TestList::erase(&e[0]);
228 TestList::erase(TestList::iterator(&e[1]));
229 EXPECT_TRUE(l.empty());
230 }
231
TEST_F(IntrusiveListTest,Splice)232 TEST_F(IntrusiveListTest, Splice) {
233 // We verify that the contents of this secondary list aren't affected by any
234 // of the splices.
235 SpdyIntrusiveList<TestItem, ListId2> secondary_list;
236 for (int i = 0; i < 3; ++i) {
237 secondary_list.push_back(&e[i]);
238 }
239
240 // Test the basic cases:
241 // - The lists range from 0 to 2 elements.
242 // - The insertion point ranges from begin() to end()
243 // - The transfered range has multiple sizes and locations in the source.
244 for (int l1_count = 0; l1_count < 3; ++l1_count) {
245 for (int l2_count = 0; l2_count < 3; ++l2_count) {
246 for (int pos = 0; pos <= l1_count; ++pos) {
247 for (int first = 0; first <= l2_count; ++first) {
248 for (int last = first; last <= l2_count; ++last) {
249 PrepareLists(l1_count, l2_count);
250
251 l1.splice(std::next(l1.begin(), pos), std::next(l2.begin(), first),
252 std::next(l2.begin(), last));
253 ll1.splice(std::next(ll1.begin(), pos), ll2,
254 std::next(ll2.begin(), first),
255 std::next(ll2.begin(), last));
256
257 CheckLists();
258
259 ASSERT_EQ(3u, secondary_list.size());
260 for (int i = 0; i < 3; ++i) {
261 EXPECT_EQ(&e[i], &*std::next(secondary_list.begin(), i));
262 }
263 }
264 }
265 }
266 }
267 }
268 }
269
270 // Build up a set of classes which form "challenging" type hierarchies to use
271 // with an SpdyIntrusiveList.
272 struct BaseLinkId {};
273 struct DerivedLinkId {};
274
275 struct AbstractBase : public SpdyIntrusiveLink<AbstractBase, BaseLinkId> {
276 virtual ~AbstractBase() = 0;
namespdy::test::AbstractBase277 virtual std::string name() { return "AbstractBase"; }
278 };
~AbstractBase()279 AbstractBase::~AbstractBase() {}
280 struct DerivedClass : public SpdyIntrusiveLink<DerivedClass, DerivedLinkId>,
281 public AbstractBase {
~DerivedClassspdy::test::DerivedClass282 ~DerivedClass() override {}
namespdy::test::DerivedClass283 std::string name() override { return "DerivedClass"; }
284 };
285 struct VirtuallyDerivedBaseClass : public virtual AbstractBase {
286 ~VirtuallyDerivedBaseClass() override = 0;
namespdy::test::VirtuallyDerivedBaseClass287 std::string name() override { return "VirtuallyDerivedBaseClass"; }
288 };
~VirtuallyDerivedBaseClass()289 VirtuallyDerivedBaseClass::~VirtuallyDerivedBaseClass() {}
290 struct VirtuallyDerivedClassA
291 : public SpdyIntrusiveLink<VirtuallyDerivedClassA, DerivedLinkId>,
292 public virtual VirtuallyDerivedBaseClass {
~VirtuallyDerivedClassAspdy::test::VirtuallyDerivedClassA293 ~VirtuallyDerivedClassA() override {}
namespdy::test::VirtuallyDerivedClassA294 std::string name() override { return "VirtuallyDerivedClassA"; }
295 };
296 struct NonceClass {
~NonceClassspdy::test::NonceClass297 virtual ~NonceClass() {}
298 int data_;
299 };
300 struct VirtuallyDerivedClassB
301 : public SpdyIntrusiveLink<VirtuallyDerivedClassB, DerivedLinkId>,
302 public virtual NonceClass,
303 public virtual VirtuallyDerivedBaseClass {
~VirtuallyDerivedClassBspdy::test::VirtuallyDerivedClassB304 ~VirtuallyDerivedClassB() override {}
namespdy::test::VirtuallyDerivedClassB305 std::string name() override { return "VirtuallyDerivedClassB"; }
306 };
307 struct VirtuallyDerivedClassC
308 : public SpdyIntrusiveLink<VirtuallyDerivedClassC, DerivedLinkId>,
309 public virtual AbstractBase,
310 public virtual NonceClass,
311 public virtual VirtuallyDerivedBaseClass {
~VirtuallyDerivedClassCspdy::test::VirtuallyDerivedClassC312 ~VirtuallyDerivedClassC() override {}
namespdy::test::VirtuallyDerivedClassC313 std::string name() override { return "VirtuallyDerivedClassC"; }
314 };
315
316 // Test for multiple layers between the element type and the link.
317 namespace templated_base_link {
318 template <typename T>
319 struct AbstractBase : public SpdyIntrusiveLink<T> {
320 virtual ~AbstractBase() = 0;
321 };
322 template <typename T>
~AbstractBase()323 AbstractBase<T>::~AbstractBase() {}
324 struct DerivedClass : public AbstractBase<DerivedClass> {
325 int n;
326 };
327 } // namespace templated_base_link
328
TEST(NewIntrusiveListTest,HandleInheritanceHierarchies)329 TEST(NewIntrusiveListTest, HandleInheritanceHierarchies) {
330 {
331 SpdyIntrusiveList<DerivedClass, DerivedLinkId> list;
332 DerivedClass elements[2];
333 EXPECT_TRUE(list.empty());
334 list.push_back(&elements[0]);
335 EXPECT_EQ(1u, list.size());
336 list.push_back(&elements[1]);
337 EXPECT_EQ(2u, list.size());
338 list.pop_back();
339 EXPECT_EQ(1u, list.size());
340 list.pop_back();
341 EXPECT_TRUE(list.empty());
342 }
343 {
344 SpdyIntrusiveList<VirtuallyDerivedClassA, DerivedLinkId> list;
345 VirtuallyDerivedClassA elements[2];
346 EXPECT_TRUE(list.empty());
347 list.push_back(&elements[0]);
348 EXPECT_EQ(1u, list.size());
349 list.push_back(&elements[1]);
350 EXPECT_EQ(2u, list.size());
351 list.pop_back();
352 EXPECT_EQ(1u, list.size());
353 list.pop_back();
354 EXPECT_TRUE(list.empty());
355 }
356 {
357 SpdyIntrusiveList<VirtuallyDerivedClassC, DerivedLinkId> list;
358 VirtuallyDerivedClassC elements[2];
359 EXPECT_TRUE(list.empty());
360 list.push_back(&elements[0]);
361 EXPECT_EQ(1u, list.size());
362 list.push_back(&elements[1]);
363 EXPECT_EQ(2u, list.size());
364 list.pop_back();
365 EXPECT_EQ(1u, list.size());
366 list.pop_back();
367 EXPECT_TRUE(list.empty());
368 }
369 {
370 SpdyIntrusiveList<AbstractBase, BaseLinkId> list;
371 DerivedClass d1;
372 VirtuallyDerivedClassA d2;
373 VirtuallyDerivedClassB d3;
374 VirtuallyDerivedClassC d4;
375 EXPECT_TRUE(list.empty());
376 list.push_back(&d1);
377 EXPECT_EQ(1u, list.size());
378 list.push_back(&d2);
379 EXPECT_EQ(2u, list.size());
380 list.push_back(&d3);
381 EXPECT_EQ(3u, list.size());
382 list.push_back(&d4);
383 EXPECT_EQ(4u, list.size());
384 SpdyIntrusiveList<AbstractBase, BaseLinkId>::iterator it = list.begin();
385 EXPECT_EQ("DerivedClass", (it++)->name());
386 EXPECT_EQ("VirtuallyDerivedClassA", (it++)->name());
387 EXPECT_EQ("VirtuallyDerivedClassB", (it++)->name());
388 EXPECT_EQ("VirtuallyDerivedClassC", (it++)->name());
389 }
390 {
391 SpdyIntrusiveList<templated_base_link::DerivedClass> list;
392 templated_base_link::DerivedClass elements[2];
393 EXPECT_TRUE(list.empty());
394 list.push_back(&elements[0]);
395 EXPECT_EQ(1u, list.size());
396 list.push_back(&elements[1]);
397 EXPECT_EQ(2u, list.size());
398 list.pop_back();
399 EXPECT_EQ(1u, list.size());
400 list.pop_back();
401 EXPECT_TRUE(list.empty());
402 }
403 }
404
405 class IntrusiveListTagTypeTest : public quiche::test::QuicheTest {
406 protected:
407 struct Tag {};
408 class Element : public SpdyIntrusiveLink<Element, Tag> {};
409 };
410
TEST_F(IntrusiveListTagTypeTest,TagTypeListID)411 TEST_F(IntrusiveListTagTypeTest, TagTypeListID) {
412 SpdyIntrusiveList<Element, Tag> list;
413 {
414 Element e;
415 list.push_back(&e);
416 }
417 }
418
419 } // namespace test
420 } // namespace spdy
421