/* -*- 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 { unsigned int mValue; explicit SomeClass(int aValue = 0) : mValue(aValue) {} SomeClass(SomeClass&&) = default; SomeClass& operator=(SomeClass&&) = default; void incr() { ++mValue; } }; template static void CheckListValues(LinkedList& 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 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& constList = list; for (const SomeClass* x : constList) { MOZ_RELEASE_ASSERT(x); } } static void TestExtendLists() { AutoCleanLinkedList list1, list2; constexpr unsigned int N = 5; for (unsigned int i = 0; i < N; ++i) { list1.insertBack(new SomeClass(static_cast(i))); AutoCleanLinkedList singleItemList; singleItemList.insertFront(new SomeClass(static_cast(i + N))); list2.extendBack(std::move(singleItemList)); } // list1 = { 0, 1, 2, 3, 4 } // list2 = { 5, 6, 7, 8, 9 } list1.extendBack(AutoCleanLinkedList()); 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 list1, list2; for (unsigned int i = 1; i <= 5; ++i) { list1.insertBack(new SomeClass(static_cast(i))); AutoCleanLinkedList singleItemList; singleItemList.insertFront(new SomeClass(static_cast(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 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 list2(std::move(list1)); { unsigned int check[]{1, 2}; CheckListValues(list2, check); } MOZ_RELEASE_ASSERT(list1.isEmpty()); // Test move assignment for LinkedList. LinkedList list3; list3 = std::move(list2); { unsigned int check[]{1, 2}; CheckListValues(list3, check); } MOZ_RELEASE_ASSERT(list2.isEmpty()); list3.clear(); } static void TestRemoveAndGet() { LinkedList 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 { friend class mozilla::LinkedList; friend class mozilla::LinkedListElement; }; static void TestPrivate() { LinkedList 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> { int mCount; void AddRef() { mCount++; } void Release() { mCount--; } CountedClass() : mCount(0) {} ~CountedClass() { MOZ_RELEASE_ASSERT(mCount == 0); } }; static void TestRefPtrList() { LinkedList> 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 p : list) { MOZ_RELEASE_ASSERT(p->mCount == 2); } RefPtr ptr = list.getFirst(); while (ptr) { MOZ_RELEASE_ASSERT(ptr->mCount == 2); RefPtr 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 first = list.popFirst(); MOZ_RELEASE_ASSERT(elt1->mCount == 1); MOZ_RELEASE_ASSERT(elt2->mCount == 1); RefPtr 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; }