|  | //===- unittests/ADT/BumpPtrListTest.cpp - BumpPtrList unit tests ---------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/ADT/AllocatorList.h" | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | struct CountsDestructors { | 
|  | static unsigned NumCalls; | 
|  | ~CountsDestructors() { ++NumCalls; } | 
|  | }; | 
|  | unsigned CountsDestructors::NumCalls = 0; | 
|  |  | 
|  | struct MoveOnly { | 
|  | int V; | 
|  | explicit MoveOnly(int V) : V(V) {} | 
|  | MoveOnly() = delete; | 
|  | MoveOnly(MoveOnly &&X) { V = X.V; } | 
|  | MoveOnly(const MoveOnly &X) = delete; | 
|  | MoveOnly &operator=(MoveOnly &&X) = delete; | 
|  | MoveOnly &operator=(const MoveOnly &X) = delete; | 
|  | }; | 
|  |  | 
|  | struct EmplaceOnly { | 
|  | int V1, V2; | 
|  | explicit EmplaceOnly(int V1, int V2) : V1(V1), V2(V2) {} | 
|  | EmplaceOnly() = delete; | 
|  | EmplaceOnly(EmplaceOnly &&X) = delete; | 
|  | EmplaceOnly(const EmplaceOnly &X) = delete; | 
|  | EmplaceOnly &operator=(EmplaceOnly &&X) = delete; | 
|  | EmplaceOnly &operator=(const EmplaceOnly &X) = delete; | 
|  | }; | 
|  |  | 
|  | TEST(BumpPtrListTest, DefaultConstructor) { | 
|  | BumpPtrList<int> L; | 
|  | EXPECT_TRUE(L.empty()); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, pushPopBack) { | 
|  | // Build a list with push_back. | 
|  | BumpPtrList<int> L; | 
|  | int Ns[] = {1, 3, 9, 5, 7}; | 
|  | for (const int N : Ns) | 
|  | L.push_back(N); | 
|  |  | 
|  | // Use iterators to check contents. | 
|  | auto I = L.begin(); | 
|  | for (int N : Ns) | 
|  | EXPECT_EQ(N, *I++); | 
|  | EXPECT_EQ(I, L.end()); | 
|  |  | 
|  | // Unbuild the list with pop_back. | 
|  | for (int N : llvm::reverse(Ns)) { | 
|  | EXPECT_EQ(N, L.back()); | 
|  | L.pop_back(); | 
|  | } | 
|  | EXPECT_TRUE(L.empty()); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, pushPopFront) { | 
|  | // Build a list with push_front. | 
|  | BumpPtrList<int> L; | 
|  | int Ns[] = {1, 3, 9, 5, 7}; | 
|  | for (const int N : Ns) | 
|  | L.push_front(N); | 
|  |  | 
|  | // Use reverse iterators to check contents. | 
|  | auto I = L.rbegin(); | 
|  | for (int N : Ns) | 
|  | EXPECT_EQ(N, *I++); | 
|  | EXPECT_EQ(I, L.rend()); | 
|  |  | 
|  | // Unbuild the list with pop_front. | 
|  | for (int N : llvm::reverse(Ns)) { | 
|  | EXPECT_EQ(N, L.front()); | 
|  | L.pop_front(); | 
|  | } | 
|  | EXPECT_TRUE(L.empty()); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, pushBackMoveOnly) { | 
|  | BumpPtrList<MoveOnly> L; | 
|  | int Ns[] = {1, 3, 9, 5, 7}; | 
|  | for (const int N : Ns) { | 
|  | L.push_back(MoveOnly(N)); | 
|  | EXPECT_EQ(N, L.back().V); | 
|  | } | 
|  | // Instantiate with MoveOnly. | 
|  | while (!L.empty()) | 
|  | L.pop_back(); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, pushFrontMoveOnly) { | 
|  | BumpPtrList<MoveOnly> L; | 
|  | int Ns[] = {1, 3, 9, 5, 7}; | 
|  | for (const int N : Ns) { | 
|  | L.push_front(MoveOnly(N)); | 
|  | EXPECT_EQ(N, L.front().V); | 
|  | } | 
|  | // Instantiate with MoveOnly. | 
|  | while (!L.empty()) | 
|  | L.pop_front(); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, emplaceBack) { | 
|  | BumpPtrList<EmplaceOnly> L; | 
|  | int N1s[] = {1, 3, 9, 5, 7}; | 
|  | int N2s[] = {7, 3, 1, 8, 2}; | 
|  | for (int I = 0; I != 5; ++I) { | 
|  | L.emplace_back(N1s[I], N2s[I]); | 
|  | EXPECT_EQ(N1s[I], L.back().V1); | 
|  | EXPECT_EQ(N2s[I], L.back().V2); | 
|  | } | 
|  | // Instantiate with EmplaceOnly. | 
|  | while (!L.empty()) | 
|  | L.pop_back(); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, emplaceFront) { | 
|  | BumpPtrList<EmplaceOnly> L; | 
|  | int N1s[] = {1, 3, 9, 5, 7}; | 
|  | int N2s[] = {7, 3, 1, 8, 2}; | 
|  | for (int I = 0; I != 5; ++I) { | 
|  | L.emplace_front(N1s[I], N2s[I]); | 
|  | EXPECT_EQ(N1s[I], L.front().V1); | 
|  | EXPECT_EQ(N2s[I], L.front().V2); | 
|  | } | 
|  | // Instantiate with EmplaceOnly. | 
|  | while (!L.empty()) | 
|  | L.pop_front(); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, swap) { | 
|  | // Build two lists with different lifetimes and swap them. | 
|  | int N1s[] = {1, 3, 5, 7, 9}; | 
|  | int N2s[] = {2, 4, 6, 8, 10}; | 
|  |  | 
|  | BumpPtrList<int> L1; | 
|  | L1.insert(L1.end(), std::begin(N1s), std::end(N1s)); | 
|  | { | 
|  | BumpPtrList<int> L2; | 
|  | L2.insert(L2.end(), std::begin(N2s), std::end(N2s)); | 
|  |  | 
|  | // Swap the lists. | 
|  | L1.swap(L2); | 
|  |  | 
|  | // Check L2's contents before it goes out of scope. | 
|  | auto I = L2.begin(); | 
|  | for (int N : N1s) | 
|  | EXPECT_EQ(N, *I++); | 
|  | EXPECT_EQ(I, L2.end()); | 
|  | } | 
|  |  | 
|  | // Check L1's contents now that L2 is out of scope (with its allocation | 
|  | // blocks). | 
|  | auto I = L1.begin(); | 
|  | for (int N : N2s) | 
|  | EXPECT_EQ(N, *I++); | 
|  | EXPECT_EQ(I, L1.end()); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, clear) { | 
|  | CountsDestructors::NumCalls = 0; | 
|  | CountsDestructors N; | 
|  | BumpPtrList<CountsDestructors> L; | 
|  | L.push_back(N); | 
|  | L.push_back(N); | 
|  | L.push_back(N); | 
|  | EXPECT_EQ(3u, L.size()); | 
|  | EXPECT_EQ(0u, CountsDestructors::NumCalls); | 
|  | L.pop_back(); | 
|  | EXPECT_EQ(1u, CountsDestructors::NumCalls); | 
|  | L.clear(); | 
|  | EXPECT_EQ(3u, CountsDestructors::NumCalls); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, move) { | 
|  | BumpPtrList<int> L1, L2; | 
|  | L1.push_back(1); | 
|  | L2.push_back(2); | 
|  | L1 = std::move(L2); | 
|  | EXPECT_EQ(1u, L1.size()); | 
|  | EXPECT_EQ(2, L1.front()); | 
|  | EXPECT_EQ(0u, L2.size()); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, moveCallsDestructors) { | 
|  | CountsDestructors::NumCalls = 0; | 
|  | BumpPtrList<CountsDestructors> L1, L2; | 
|  | L1.emplace_back(); | 
|  | EXPECT_EQ(0u, CountsDestructors::NumCalls); | 
|  | L1 = std::move(L2); | 
|  | EXPECT_EQ(1u, CountsDestructors::NumCalls); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, copy) { | 
|  | BumpPtrList<int> L1, L2; | 
|  | L1.push_back(1); | 
|  | L2.push_back(2); | 
|  | L1 = L2; | 
|  | EXPECT_EQ(1u, L1.size()); | 
|  | EXPECT_EQ(2, L1.front()); | 
|  | EXPECT_EQ(1u, L2.size()); | 
|  | EXPECT_EQ(2, L2.front()); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, copyCallsDestructors) { | 
|  | CountsDestructors::NumCalls = 0; | 
|  | BumpPtrList<CountsDestructors> L1, L2; | 
|  | L1.emplace_back(); | 
|  | EXPECT_EQ(0u, CountsDestructors::NumCalls); | 
|  | L1 = L2; | 
|  | EXPECT_EQ(1u, CountsDestructors::NumCalls); | 
|  | } | 
|  |  | 
|  | TEST(BumpPtrListTest, resetAlloc) { | 
|  | // Resetting an empty list should work. | 
|  | BumpPtrList<int> L; | 
|  |  | 
|  | // Resetting an empty list that has allocated should also work. | 
|  | L.resetAlloc(); | 
|  | L.push_back(5); | 
|  | L.erase(L.begin()); | 
|  | L.resetAlloc(); | 
|  |  | 
|  | // Resetting a non-empty list should crash. | 
|  | L.push_back(5); | 
|  | #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) | 
|  | EXPECT_DEATH(L.resetAlloc(), "Cannot reset allocator if not empty"); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | } // end namespace |