• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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