Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkRandom.h"
      9 #include "SkTInternalLList.h"
     10 #include "SkTLList.h"
     11 #include "Test.h"
     12 
     13 class ListElement {
     14 public:
     15     ListElement(int id) : fID(id) {
     16     }
     17     bool operator== (const ListElement& other) { return fID == other.fID; }
     18 
     19     int fID;
     20 
     21 private:
     22 
     23     SK_DECLARE_INTERNAL_LLIST_INTERFACE(ListElement);
     24 };
     25 
     26 static void check_list(const SkTInternalLList<ListElement>& list,
     27                        skiatest::Reporter* reporter,
     28                        bool empty,
     29                        int numElements,
     30                        bool in0, bool in1, bool in2, bool in3,
     31                        ListElement elements[4]) {
     32 
     33     REPORTER_ASSERT(reporter, empty == list.isEmpty());
     34 #ifdef SK_DEBUG
     35     list.validate();
     36     REPORTER_ASSERT(reporter, numElements == list.countEntries());
     37     REPORTER_ASSERT(reporter, in0 == list.isInList(&elements[0]));
     38     REPORTER_ASSERT(reporter, in1 == list.isInList(&elements[1]));
     39     REPORTER_ASSERT(reporter, in2 == list.isInList(&elements[2]));
     40     REPORTER_ASSERT(reporter, in3 == list.isInList(&elements[3]));
     41 #endif
     42 }
     43 
     44 static void test_tinternallist(skiatest::Reporter* reporter) {
     45     SkTInternalLList<ListElement> list;
     46     ListElement elements[4] = {
     47         ListElement(0),
     48         ListElement(1),
     49         ListElement(2),
     50         ListElement(3),
     51     };
     52 
     53     // list should be empty to start with
     54     check_list(list, reporter, true, 0, false, false, false, false, elements);
     55 
     56     list.addToHead(&elements[0]);
     57 
     58     check_list(list, reporter, false, 1, true, false, false, false, elements);
     59 
     60     list.addToHead(&elements[1]);
     61     list.addToHead(&elements[2]);
     62     list.addToHead(&elements[3]);
     63 
     64     check_list(list, reporter, false, 4, true, true, true, true, elements);
     65 
     66     // test out iterators
     67     typedef SkTInternalLList<ListElement>::Iter Iter;
     68     Iter iter;
     69 
     70     ListElement* cur = iter.init(list, Iter::kHead_IterStart);
     71     for (int i = 0; cur; ++i, cur = iter.next()) {
     72         REPORTER_ASSERT(reporter, cur->fID == 3-i);
     73     }
     74 
     75     cur = iter.init(list, Iter::kTail_IterStart);
     76     for (int i = 0; cur; ++i, cur = iter.prev()) {
     77         REPORTER_ASSERT(reporter, cur->fID == i);
     78     }
     79 
     80     // remove middle, frontmost then backmost
     81     list.remove(&elements[1]);
     82     list.remove(&elements[3]);
     83     list.remove(&elements[0]);
     84 
     85     check_list(list, reporter, false, 1, false, false, true, false, elements);
     86 
     87     // remove last element
     88     list.remove(&elements[2]);
     89 
     90     // list should be empty again
     91     check_list(list, reporter, true, 0, false, false, false, false, elements);
     92 
     93     // test out methods that add to the middle of the list.
     94     list.addAfter(&elements[1], nullptr);
     95     check_list(list, reporter, false, 1, false, true, false, false, elements);
     96 
     97     list.remove(&elements[1]);
     98 
     99     list.addBefore(&elements[1], nullptr);
    100     check_list(list, reporter, false, 1, false, true, false, false, elements);
    101 
    102     list.addBefore(&elements[0], &elements[1]);
    103     check_list(list, reporter, false, 2, true, true, false, false, elements);
    104 
    105     list.addAfter(&elements[3], &elements[1]);
    106     check_list(list, reporter, false, 3, true, true, false, true, elements);
    107 
    108     list.addBefore(&elements[2], &elements[3]);
    109     check_list(list, reporter, false, 4, true, true, true, true, elements);
    110 
    111     cur = iter.init(list, Iter::kHead_IterStart);
    112     for (int i = 0; cur; ++i, cur = iter.next()) {
    113         REPORTER_ASSERT(reporter, cur->fID == i);
    114     }
    115 }
    116 
    117 template <unsigned int N> static void test_tllist(skiatest::Reporter* reporter) {
    118     typedef SkTLList<ListElement, N> ElList;
    119     typedef typename ElList::Iter Iter;
    120     SkRandom random;
    121 
    122     ElList list1;
    123     ElList list2;
    124     Iter iter1;
    125     Iter iter2;
    126     Iter iter3;
    127     Iter iter4;
    128 
    129     REPORTER_ASSERT(reporter, list1.isEmpty());
    130     REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kHead_IterStart));
    131     REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kTail_IterStart));
    132     // Try popping an empty list
    133     list1.popHead();
    134     list1.popTail();
    135     REPORTER_ASSERT(reporter, list1.isEmpty());
    136     REPORTER_ASSERT(reporter, list1 == list2);
    137 
    138     // Create two identical lists, one by appending to head and the other to the tail.
    139     list1.addToHead(ListElement(1));
    140     list2.addToTail(ListElement(1));
    141     iter1.init(list1, Iter::kHead_IterStart);
    142     iter2.init(list1, Iter::kTail_IterStart);
    143     REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID);
    144     iter3.init(list2, Iter::kHead_IterStart);
    145     iter4.init(list2, Iter::kTail_IterStart);
    146     REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
    147     REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
    148     REPORTER_ASSERT(reporter, list1 == list2);
    149 
    150     list2.reset();
    151 
    152     // use both before/after in-place construction on an empty list
    153     list2.addBefore(list2.headIter(), 1);
    154     REPORTER_ASSERT(reporter, list2 == list1);
    155     list2.reset();
    156 
    157     list2.addAfter(list2.tailIter(), 1);
    158     REPORTER_ASSERT(reporter, list2 == list1);
    159 
    160     // add an element to the second list, check that iters are still valid
    161     iter3.init(list2, Iter::kHead_IterStart);
    162     iter4.init(list2, Iter::kTail_IterStart);
    163     list2.addToHead(ListElement(2));
    164 
    165     REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
    166     REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
    167     REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID);
    168     REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID);
    169     REPORTER_ASSERT(reporter, list1 != list2);
    170     list1.addToHead(ListElement(2));
    171     REPORTER_ASSERT(reporter, list1 == list2);
    172     REPORTER_ASSERT(reporter, !list1.isEmpty());
    173 
    174     list1.reset();
    175     list2.reset();
    176     REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty());
    177 
    178     // randomly perform insertions and deletions on a list and perform tests
    179     int count = 0;
    180     for (int j = 0; j < 100; ++j) {
    181         if (list1.isEmpty() || random.nextBiasedBool(3  * SK_Scalar1 / 4)) {
    182             int id = j;
    183             // Choose one of three ways to insert a new element: at the head, at the tail,
    184             // before a random element, after a random element
    185             int numValidMethods = 0 == count ? 2 : 4;
    186             int insertionMethod = random.nextULessThan(numValidMethods);
    187             switch (insertionMethod) {
    188                 case 0:
    189                     list1.addToHead(ListElement(id));
    190                     break;
    191                 case 1:
    192                     list1.addToTail(ListElement(id));
    193                     break;
    194                 case 2: // fallthru to share code that picks random element.
    195                 case 3: {
    196                     int n = random.nextULessThan(list1.count());
    197                     Iter iter = list1.headIter();
    198                     // remember the elements before/after the insertion point.
    199                     while (n--) {
    200                         iter.next();
    201                     }
    202                     Iter prev(iter);
    203                     Iter next(iter);
    204                     next.next();
    205                     prev.prev();
    206 
    207                     SkASSERT(iter.get());
    208                     // insert either before or after the iterator, then check that the
    209                     // surrounding sequence is correct.
    210                     if (2 == insertionMethod) {
    211                         list1.addBefore(iter, id);
    212                         Iter newItem(iter);
    213                         newItem.prev();
    214                         REPORTER_ASSERT(reporter, newItem.get()->fID == id);
    215 
    216                         if (next.get()) {
    217                             REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID);
    218                         }
    219                         if (prev.get()) {
    220                             REPORTER_ASSERT(reporter, prev.next()->fID == id);
    221                         }
    222                     } else {
    223                         list1.addAfter(iter, id);
    224                         Iter newItem(iter);
    225                         newItem.next();
    226                         REPORTER_ASSERT(reporter, newItem.get()->fID == id);
    227 
    228                         if (next.get()) {
    229                             REPORTER_ASSERT(reporter, next.prev()->fID == id);
    230                         }
    231                         if (prev.get()) {
    232                             REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID);
    233                         }
    234                     }
    235                 }
    236             }
    237             ++count;
    238         } else {
    239             // walk to a random place either forward or backwards and remove.
    240             int n = random.nextULessThan(list1.count());
    241             typename Iter::IterStart start;
    242             ListElement* (Iter::*incrFunc)();
    243 
    244             if (random.nextBool()) {
    245                 start = Iter::kHead_IterStart;
    246                 incrFunc = &Iter::next;
    247             } else {
    248                 start = Iter::kTail_IterStart;
    249                 incrFunc = &Iter::prev;
    250             }
    251 
    252             // find the element
    253             Iter iter(list1, start);
    254             while (n--) {
    255                 REPORTER_ASSERT(reporter, iter.get());
    256                 (iter.*incrFunc)();
    257             }
    258             REPORTER_ASSERT(reporter, iter.get());
    259 
    260             // remember the prev and next elements from the element to be removed
    261             Iter prev = iter;
    262             Iter next = iter;
    263             prev.prev();
    264             next.next();
    265             list1.remove(iter.get());
    266 
    267             // make sure the remembered next/prev iters still work
    268             Iter pn = prev; pn.next();
    269             Iter np = next; np.prev();
    270             // pn should match next unless the target node was the head, in which case prev
    271             // walked off the list.
    272             REPORTER_ASSERT(reporter, pn.get() == next.get() || nullptr == prev.get());
    273             // Similarly, np should match prev unless next originally walked off the tail.
    274             REPORTER_ASSERT(reporter, np.get() == prev.get() || nullptr == next.get());
    275             --count;
    276         }
    277         REPORTER_ASSERT(reporter, count == list1.count());
    278     }
    279 }
    280 
    281 DEF_TEST(LList, reporter) {
    282     test_tinternallist(reporter);
    283     test_tllist<1>(reporter);
    284     test_tllist<3>(reporter);
    285     test_tllist<8>(reporter);
    286     test_tllist<10>(reporter);
    287     test_tllist<16>(reporter);
    288 }
    289