summaryrefslogtreecommitdiffstats
path: root/mfbt/tests/TestLinkedList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mfbt/tests/TestLinkedList.cpp')
-rw-r--r--mfbt/tests/TestLinkedList.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/mfbt/tests/TestLinkedList.cpp b/mfbt/tests/TestLinkedList.cpp
new file mode 100644
index 0000000000..bb1ffe08c0
--- /dev/null
+++ b/mfbt/tests/TestLinkedList.cpp
@@ -0,0 +1,399 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/LinkedList.h"
+
+using mozilla::AutoCleanLinkedList;
+using mozilla::LinkedList;
+using mozilla::LinkedListElement;
+
+struct SomeClass : public LinkedListElement<SomeClass> {
+ unsigned int mValue;
+ explicit SomeClass(int aValue = 0) : mValue(aValue) {}
+ SomeClass(SomeClass&&) = default;
+ SomeClass& operator=(SomeClass&&) = default;
+ void incr() { ++mValue; }
+};
+
+template <size_t N>
+static void CheckListValues(LinkedList<SomeClass>& list,
+ unsigned int (&values)[N]) {
+ size_t count = 0;
+ for (SomeClass* x : list) {
+ MOZ_RELEASE_ASSERT(x->mValue == values[count]);
+ ++count;
+ }
+ MOZ_RELEASE_ASSERT(count == N);
+}
+
+static void TestList() {
+ LinkedList<SomeClass> list;
+
+ SomeClass one(1), two(2), three(3);
+
+ MOZ_RELEASE_ASSERT(list.isEmpty());
+ MOZ_RELEASE_ASSERT(list.length() == 0);
+ MOZ_RELEASE_ASSERT(!list.getFirst());
+ MOZ_RELEASE_ASSERT(!list.getLast());
+ MOZ_RELEASE_ASSERT(!list.popFirst());
+ MOZ_RELEASE_ASSERT(!list.popLast());
+
+ for (SomeClass* x : list) {
+ MOZ_RELEASE_ASSERT(x);
+ MOZ_RELEASE_ASSERT(false);
+ }
+
+ list.insertFront(&one);
+ {
+ unsigned int check[]{1};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(one.isInList());
+ MOZ_RELEASE_ASSERT(!two.isInList());
+ MOZ_RELEASE_ASSERT(!three.isInList());
+
+ MOZ_RELEASE_ASSERT(list.contains(&one));
+ MOZ_RELEASE_ASSERT(!list.contains(&two));
+ MOZ_RELEASE_ASSERT(!list.contains(&three));
+
+ MOZ_RELEASE_ASSERT(!list.isEmpty());
+ MOZ_RELEASE_ASSERT(list.length() == 1);
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 1);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1);
+
+ list.insertFront(&two);
+ {
+ unsigned int check[]{2, 1};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(list.length() == 2);
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 1);
+
+ list.insertBack(&three);
+ {
+ unsigned int check[]{2, 1, 3};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(list.length() == 3);
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 2);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 3);
+
+ one.removeFrom(list);
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ three.setPrevious(&one);
+ {
+ unsigned int check[]{2, 1, 3};
+ CheckListValues(list, check);
+ }
+
+ three.removeFrom(list);
+ {
+ unsigned int check[]{2, 1};
+ CheckListValues(list, check);
+ }
+
+ two.setPrevious(&three);
+ {
+ unsigned int check[]{3, 2, 1};
+ CheckListValues(list, check);
+ }
+
+ three.removeFrom(list);
+ {
+ unsigned int check[]{2, 1};
+ CheckListValues(list, check);
+ }
+
+ two.setNext(&three);
+ {
+ unsigned int check[]{2, 3, 1};
+ CheckListValues(list, check);
+ }
+
+ one.remove();
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ two.remove();
+ {
+ unsigned int check[]{3};
+ CheckListValues(list, check);
+ }
+
+ three.setPrevious(&two);
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ three.remove();
+ {
+ unsigned int check[]{2};
+ CheckListValues(list, check);
+ }
+
+ two.remove();
+
+ list.insertBack(&three);
+ {
+ unsigned int check[]{3};
+ CheckListValues(list, check);
+ }
+
+ list.insertFront(&two);
+ {
+ unsigned int check[]{2, 3};
+ CheckListValues(list, check);
+ }
+
+ for (SomeClass* x : list) {
+ x->incr();
+ }
+
+ MOZ_RELEASE_ASSERT(list.length() == 2);
+ MOZ_RELEASE_ASSERT(list.getFirst() == &two);
+ MOZ_RELEASE_ASSERT(list.getLast() == &three);
+ MOZ_RELEASE_ASSERT(list.getFirst()->mValue == 3);
+ MOZ_RELEASE_ASSERT(list.getLast()->mValue == 4);
+
+ const LinkedList<SomeClass>& constList = list;
+ for (const SomeClass* x : constList) {
+ MOZ_RELEASE_ASSERT(x);
+ }
+}
+
+static void TestExtendLists() {
+ AutoCleanLinkedList<SomeClass> list1, list2;
+
+ constexpr unsigned int N = 5;
+ for (unsigned int i = 0; i < N; ++i) {
+ list1.insertBack(new SomeClass(static_cast<int>(i)));
+
+ AutoCleanLinkedList<SomeClass> singleItemList;
+ singleItemList.insertFront(new SomeClass(static_cast<int>(i + N)));
+ list2.extendBack(std::move(singleItemList));
+ }
+ // list1 = { 0, 1, 2, 3, 4 }
+ // list2 = { 5, 6, 7, 8, 9 }
+
+ list1.extendBack(AutoCleanLinkedList<SomeClass>());
+ list1.extendBack(std::move(list2));
+
+ // Make sure the line above has properly emptied |list2|.
+ MOZ_RELEASE_ASSERT(list2.isEmpty()); // NOLINT(bugprone-use-after-move)
+
+ size_t i = 0;
+ for (SomeClass* x : list1) {
+ MOZ_RELEASE_ASSERT(x->mValue == i++);
+ }
+ MOZ_RELEASE_ASSERT(i == N * 2);
+}
+
+void TestSplice() {
+ AutoCleanLinkedList<SomeClass> list1, list2;
+ for (unsigned int i = 1; i <= 5; ++i) {
+ list1.insertBack(new SomeClass(static_cast<int>(i)));
+
+ AutoCleanLinkedList<SomeClass> singleItemList;
+ singleItemList.insertFront(new SomeClass(static_cast<int>(i * 10)));
+ list2.extendBack(std::move(singleItemList));
+ }
+ // list1 = { 1, 2, 3, 4, 5 }
+ // list2 = { 10, 20, 30, 40, 50 }
+
+ list1.splice(2, list2, 0, 5);
+
+ MOZ_RELEASE_ASSERT(list2.isEmpty());
+ unsigned int kExpected1[]{1, 2, 10, 20, 30, 40, 50, 3, 4, 5};
+ CheckListValues(list1, kExpected1);
+
+ // Since aSourceLen=100 exceeds list1's end, the function transfers
+ // three items [3, 4, 5].
+ list2.splice(0, list1, 7, 100);
+
+ unsigned int kExpected2[]{1, 2, 10, 20, 30, 40, 50};
+ unsigned int kExpected3[]{3, 4, 5};
+ CheckListValues(list1, kExpected2);
+ CheckListValues(list2, kExpected3);
+
+ // Since aDestinationPos=100 exceeds list2's end, the function transfers
+ // items to list2's end.
+ list2.splice(100, list1, 1, 1);
+
+ unsigned int kExpected4[]{1, 10, 20, 30, 40, 50};
+ unsigned int kExpected5[]{3, 4, 5, 2};
+ CheckListValues(list1, kExpected4);
+ CheckListValues(list2, kExpected5);
+}
+
+static void TestMove() {
+ auto MakeSomeClass = [](unsigned int aValue) -> SomeClass {
+ return SomeClass(aValue);
+ };
+
+ LinkedList<SomeClass> list1;
+
+ // Test move constructor for LinkedListElement.
+ SomeClass c1(MakeSomeClass(1));
+ list1.insertBack(&c1);
+
+ // Test move assignment for LinkedListElement from an element not in a
+ // list.
+ SomeClass c2;
+ c2 = MakeSomeClass(2);
+ list1.insertBack(&c2);
+
+ // Test move assignment of LinkedListElement from an element already in a
+ // list.
+ SomeClass c3;
+ c3 = std::move(c2);
+ MOZ_RELEASE_ASSERT(!c2.isInList());
+ MOZ_RELEASE_ASSERT(c3.isInList());
+
+ // Test move constructor for LinkedList.
+ LinkedList<SomeClass> list2(std::move(list1));
+ {
+ unsigned int check[]{1, 2};
+ CheckListValues(list2, check);
+ }
+ MOZ_RELEASE_ASSERT(list1.isEmpty());
+
+ // Test move assignment for LinkedList.
+ LinkedList<SomeClass> list3;
+ list3 = std::move(list2);
+ {
+ unsigned int check[]{1, 2};
+ CheckListValues(list3, check);
+ }
+ MOZ_RELEASE_ASSERT(list2.isEmpty());
+
+ list3.clear();
+}
+
+static void TestRemoveAndGet() {
+ LinkedList<SomeClass> list;
+
+ SomeClass one(1), two(2), three(3);
+ list.insertBack(&one);
+ list.insertBack(&two);
+ list.insertBack(&three);
+ {
+ unsigned int check[]{1, 2, 3};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(two.removeAndGetNext() == &three);
+ {
+ unsigned int check[]{1, 3};
+ CheckListValues(list, check);
+ }
+
+ MOZ_RELEASE_ASSERT(three.removeAndGetPrevious() == &one);
+ {
+ unsigned int check[]{1};
+ CheckListValues(list, check);
+ }
+}
+
+struct PrivateClass : private LinkedListElement<PrivateClass> {
+ friend class mozilla::LinkedList<PrivateClass>;
+ friend class mozilla::LinkedListElement<PrivateClass>;
+};
+
+static void TestPrivate() {
+ LinkedList<PrivateClass> list;
+ PrivateClass one, two;
+ list.insertBack(&one);
+ list.insertBack(&two);
+
+ size_t count = 0;
+ for (PrivateClass* p : list) {
+ MOZ_RELEASE_ASSERT(p, "cannot have null elements in list");
+ count++;
+ }
+ MOZ_RELEASE_ASSERT(count == 2);
+}
+
+struct CountedClass : public LinkedListElement<RefPtr<CountedClass>> {
+ int mCount;
+ void AddRef() { mCount++; }
+ void Release() { mCount--; }
+
+ CountedClass() : mCount(0) {}
+ ~CountedClass() { MOZ_RELEASE_ASSERT(mCount == 0); }
+};
+
+static void TestRefPtrList() {
+ LinkedList<RefPtr<CountedClass>> list;
+ CountedClass* elt1 = new CountedClass;
+ CountedClass* elt2 = new CountedClass;
+
+ list.insertBack(elt1);
+ list.insertBack(elt2);
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ for (RefPtr<CountedClass> p : list) {
+ MOZ_RELEASE_ASSERT(p->mCount == 2);
+ }
+
+ RefPtr<CountedClass> ptr = list.getFirst();
+ while (ptr) {
+ MOZ_RELEASE_ASSERT(ptr->mCount == 2);
+ RefPtr<CountedClass> next = ptr->getNext();
+ ptr->remove();
+ ptr = std::move(next);
+ }
+ ptr = nullptr;
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 0);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 0);
+
+ list.insertBack(elt1);
+ elt1->setNext(elt2);
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ RefPtr<CountedClass> first = list.popFirst();
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ RefPtr<CountedClass> second = list.popFirst();
+
+ MOZ_RELEASE_ASSERT(elt1->mCount == 1);
+ MOZ_RELEASE_ASSERT(elt2->mCount == 1);
+
+ first = second = nullptr;
+
+ delete elt1;
+ delete elt2;
+}
+
+int main() {
+ TestList();
+ TestExtendLists();
+ TestSplice();
+ TestPrivate();
+ TestMove();
+ TestRemoveAndGet();
+ TestRefPtrList();
+ return 0;
+}