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 while (!list.isEmpty()) { 116 list.remove(list.tail()); 117 } 118 119 // test concat. 120 SkTInternalLList<ListElement> listA, listB; 121 listA.concat(std::move(listB)); 122 check_list(listA, reporter, true, 0, false, false, false, false, elements); 123 // NOLINTNEXTLINE(bugprone-use-after-move) 124 check_list(listB, reporter, true, 0, false, false, false, false, elements); 125 126 listB.addToTail(&elements[0]); 127 listA.concat(std::move(listB)); 128 check_list(listA, reporter, false, 1, true, false, false, false, elements); 129 // NOLINTNEXTLINE(bugprone-use-after-move) 130 check_list(listB, reporter, true, 0, false, false, false, false, elements); 131 132 listB.addToTail(&elements[1]); 133 listA.concat(std::move(listB)); 134 check_list(listA, reporter, false, 2, true, true, false, false, elements); 135 // NOLINTNEXTLINE(bugprone-use-after-move) 136 check_list(listB, reporter, true, 0, false, false, false, false, elements); 137 138 listA.concat(std::move(listB)); 139 check_list(listA, reporter, false, 2, true, true, false, false, elements); 140 // NOLINTNEXTLINE(bugprone-use-after-move) 141 check_list(listB, reporter, true, 0, false, false, false, false, elements); 142 143 listB.addToTail(&elements[2]); 144 listB.addToTail(&elements[3]); 145 listA.concat(std::move(listB)); 146 check_list(listA, reporter, false, 4, true, true, true, true, elements); 147 // NOLINTNEXTLINE(bugprone-use-after-move) 148 check_list(listB, reporter, true, 0, false, false, false, false, elements); 149 150 cur = iter.init(listA, Iter::kHead_IterStart); 151 for (int i = 0; cur; ++i, cur = iter.next()) { 152 REPORTER_ASSERT(reporter, cur->fID == i); 153 } 154 } 155 156 template <unsigned int N> static void test_tllist(skiatest::Reporter* reporter) { 157 typedef SkTLList<ListElement, N> ElList; 158 typedef typename ElList::Iter Iter; 159 SkRandom random; 160 161 ElList list1; 162 ElList list2; 163 Iter iter1; 164 Iter iter2; 165 Iter iter3; 166 Iter iter4; 167 168 REPORTER_ASSERT(reporter, list1.isEmpty()); 169 REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kHead_IterStart)); 170 REPORTER_ASSERT(reporter, nullptr == iter1.init(list1, Iter::kTail_IterStart)); 171 // Try popping an empty list 172 list1.popHead(); 173 list1.popTail(); 174 REPORTER_ASSERT(reporter, list1.isEmpty()); 175 REPORTER_ASSERT(reporter, list1 == list2); 176 177 // Create two identical lists, one by appending to head and the other to the tail. 178 list1.addToHead(ListElement(1)); 179 list2.addToTail(ListElement(1)); 180 iter1.init(list1, Iter::kHead_IterStart); 181 iter2.init(list1, Iter::kTail_IterStart); 182 REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID); 183 iter3.init(list2, Iter::kHead_IterStart); 184 iter4.init(list2, Iter::kTail_IterStart); 185 REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); 186 REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); 187 REPORTER_ASSERT(reporter, list1 == list2); 188 189 list2.reset(); 190 191 // use both before/after in-place construction on an empty list 192 list2.addBefore(list2.headIter(), 1); 193 REPORTER_ASSERT(reporter, list2 == list1); 194 list2.reset(); 195 196 list2.addAfter(list2.tailIter(), 1); 197 REPORTER_ASSERT(reporter, list2 == list1); 198 199 // add an element to the second list, check that iters are still valid 200 iter3.init(list2, Iter::kHead_IterStart); 201 iter4.init(list2, Iter::kTail_IterStart); 202 list2.addToHead(ListElement(2)); 203 204 REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID); 205 REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID); 206 REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID); 207 REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID); 208 REPORTER_ASSERT(reporter, list1 != list2); 209 list1.addToHead(ListElement(2)); 210 REPORTER_ASSERT(reporter, list1 == list2); 211 REPORTER_ASSERT(reporter, !list1.isEmpty()); 212 213 list1.reset(); 214 list2.reset(); 215 REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty()); 216 217 // randomly perform insertions and deletions on a list and perform tests 218 int count = 0; 219 for (int j = 0; j < 100; ++j) { 220 if (list1.isEmpty() || random.nextBiasedBool(3 * SK_Scalar1 / 4)) { 221 int id = j; 222 // Choose one of three ways to insert a new element: at the head, at the tail, 223 // before a random element, after a random element 224 int numValidMethods = 0 == count ? 2 : 4; 225 int insertionMethod = random.nextULessThan(numValidMethods); 226 switch (insertionMethod) { 227 case 0: 228 list1.addToHead(ListElement(id)); 229 break; 230 case 1: 231 list1.addToTail(ListElement(id)); 232 break; 233 case 2: // fallthru to share code that picks random element. 234 case 3: { 235 int n = random.nextULessThan(list1.count()); 236 Iter iter = list1.headIter(); 237 // remember the elements before/after the insertion point. 238 while (n--) { 239 iter.next(); 240 } 241 Iter prev(iter); 242 Iter next(iter); 243 next.next(); 244 prev.prev(); 245 246 SkASSERT(iter.get()); 247 // insert either before or after the iterator, then check that the 248 // surrounding sequence is correct. 249 if (2 == insertionMethod) { 250 list1.addBefore(iter, id); 251 Iter newItem(iter); 252 newItem.prev(); 253 REPORTER_ASSERT(reporter, newItem.get()->fID == id); 254 255 if (next.get()) { 256 REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID); 257 } 258 if (prev.get()) { 259 REPORTER_ASSERT(reporter, prev.next()->fID == id); 260 } 261 } else { 262 list1.addAfter(iter, id); 263 Iter newItem(iter); 264 newItem.next(); 265 REPORTER_ASSERT(reporter, newItem.get()->fID == id); 266 267 if (next.get()) { 268 REPORTER_ASSERT(reporter, next.prev()->fID == id); 269 } 270 if (prev.get()) { 271 REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID); 272 } 273 } 274 } 275 } 276 ++count; 277 } else { 278 // walk to a random place either forward or backwards and remove. 279 int n = random.nextULessThan(list1.count()); 280 typename Iter::IterStart start; 281 ListElement* (Iter::*incrFunc)(); 282 283 if (random.nextBool()) { 284 start = Iter::kHead_IterStart; 285 incrFunc = &Iter::next; 286 } else { 287 start = Iter::kTail_IterStart; 288 incrFunc = &Iter::prev; 289 } 290 291 // find the element 292 Iter iter(list1, start); 293 while (n--) { 294 REPORTER_ASSERT(reporter, iter.get()); 295 (iter.*incrFunc)(); 296 } 297 REPORTER_ASSERT(reporter, iter.get()); 298 299 // remember the prev and next elements from the element to be removed 300 Iter prev = iter; 301 Iter next = iter; 302 prev.prev(); 303 next.next(); 304 list1.remove(iter.get()); 305 306 // make sure the remembered next/prev iters still work 307 Iter pn = prev; pn.next(); 308 Iter np = next; np.prev(); 309 // pn should match next unless the target node was the head, in which case prev 310 // walked off the list. 311 REPORTER_ASSERT(reporter, pn.get() == next.get() || nullptr == prev.get()); 312 // Similarly, np should match prev unless next originally walked off the tail. 313 REPORTER_ASSERT(reporter, np.get() == prev.get() || nullptr == next.get()); 314 --count; 315 } 316 REPORTER_ASSERT(reporter, count == list1.count()); 317 } 318 } 319 320 DEF_TEST(LList, reporter) { 321 test_tinternallist(reporter); 322 test_tllist<1>(reporter); 323 test_tllist<3>(reporter); 324 test_tllist<8>(reporter); 325 test_tllist<10>(reporter); 326 test_tllist<16>(reporter); 327 } 328