1 | //===- unittests/ADT/BumpPtrListTest.cpp - BumpPtrList unit tests ---------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/ADT/AllocatorList.h" |
10 | #include "llvm/ADT/STLExtras.h" |
11 | #include "gtest/gtest.h" |
12 | |
13 | using namespace llvm; |
14 | |
15 | namespace { |
16 | |
17 | struct CountsDestructors { |
18 | static unsigned NumCalls; |
19 | ~CountsDestructors() { ++NumCalls; } |
20 | }; |
21 | unsigned CountsDestructors::NumCalls = 0; |
22 | |
23 | struct MoveOnly { |
24 | int V; |
25 | explicit MoveOnly(int V) : V(V) {} |
26 | MoveOnly() = delete; |
27 | MoveOnly(MoveOnly &&X) { V = X.V; } |
28 | MoveOnly(const MoveOnly &X) = delete; |
29 | MoveOnly &operator=(MoveOnly &&X) = delete; |
30 | MoveOnly &operator=(const MoveOnly &X) = delete; |
31 | }; |
32 | |
33 | struct EmplaceOnly { |
34 | int V1, V2; |
35 | explicit EmplaceOnly(int V1, int V2) : V1(V1), V2(V2) {} |
36 | EmplaceOnly() = delete; |
37 | EmplaceOnly(EmplaceOnly &&X) = delete; |
38 | EmplaceOnly(const EmplaceOnly &X) = delete; |
39 | EmplaceOnly &operator=(EmplaceOnly &&X) = delete; |
40 | EmplaceOnly &operator=(const EmplaceOnly &X) = delete; |
41 | }; |
42 | |
43 | TEST(BumpPtrListTest, DefaultConstructor) { |
44 | BumpPtrList<int> L; |
45 | EXPECT_TRUE(L.empty()); |
46 | } |
47 | |
48 | TEST(BumpPtrListTest, pushPopBack) { |
49 | // Build a list with push_back. |
50 | BumpPtrList<int> L; |
51 | int Ns[] = {1, 3, 9, 5, 7}; |
52 | for (const int N : Ns) |
53 | L.push_back(V: N); |
54 | |
55 | // Use iterators to check contents. |
56 | auto I = L.begin(); |
57 | for (int N : Ns) |
58 | EXPECT_EQ(N, *I++); |
59 | EXPECT_EQ(I, L.end()); |
60 | |
61 | // Unbuild the list with pop_back. |
62 | for (int N : llvm::reverse(C&: Ns)) { |
63 | EXPECT_EQ(N, L.back()); |
64 | L.pop_back(); |
65 | } |
66 | EXPECT_TRUE(L.empty()); |
67 | } |
68 | |
69 | TEST(BumpPtrListTest, pushPopFront) { |
70 | // Build a list with push_front. |
71 | BumpPtrList<int> L; |
72 | int Ns[] = {1, 3, 9, 5, 7}; |
73 | for (const int N : Ns) |
74 | L.push_front(V: N); |
75 | |
76 | // Use reverse iterators to check contents. |
77 | auto I = L.rbegin(); |
78 | for (int N : Ns) |
79 | EXPECT_EQ(N, *I++); |
80 | EXPECT_EQ(I, L.rend()); |
81 | |
82 | // Unbuild the list with pop_front. |
83 | for (int N : llvm::reverse(C&: Ns)) { |
84 | EXPECT_EQ(N, L.front()); |
85 | L.pop_front(); |
86 | } |
87 | EXPECT_TRUE(L.empty()); |
88 | } |
89 | |
90 | TEST(BumpPtrListTest, pushBackMoveOnly) { |
91 | BumpPtrList<MoveOnly> L; |
92 | int Ns[] = {1, 3, 9, 5, 7}; |
93 | for (const int N : Ns) { |
94 | L.push_back(V: MoveOnly(N)); |
95 | EXPECT_EQ(N, L.back().V); |
96 | } |
97 | // Instantiate with MoveOnly. |
98 | while (!L.empty()) |
99 | L.pop_back(); |
100 | } |
101 | |
102 | TEST(BumpPtrListTest, pushFrontMoveOnly) { |
103 | BumpPtrList<MoveOnly> L; |
104 | int Ns[] = {1, 3, 9, 5, 7}; |
105 | for (const int N : Ns) { |
106 | L.push_front(V: MoveOnly(N)); |
107 | EXPECT_EQ(N, L.front().V); |
108 | } |
109 | // Instantiate with MoveOnly. |
110 | while (!L.empty()) |
111 | L.pop_front(); |
112 | } |
113 | |
114 | TEST(BumpPtrListTest, emplaceBack) { |
115 | BumpPtrList<EmplaceOnly> L; |
116 | int N1s[] = {1, 3, 9, 5, 7}; |
117 | int N2s[] = {7, 3, 1, 8, 2}; |
118 | for (int I = 0; I != 5; ++I) { |
119 | L.emplace_back(Vs&: N1s[I], Vs&: N2s[I]); |
120 | EXPECT_EQ(N1s[I], L.back().V1); |
121 | EXPECT_EQ(N2s[I], L.back().V2); |
122 | } |
123 | // Instantiate with EmplaceOnly. |
124 | while (!L.empty()) |
125 | L.pop_back(); |
126 | } |
127 | |
128 | TEST(BumpPtrListTest, emplaceFront) { |
129 | BumpPtrList<EmplaceOnly> L; |
130 | int N1s[] = {1, 3, 9, 5, 7}; |
131 | int N2s[] = {7, 3, 1, 8, 2}; |
132 | for (int I = 0; I != 5; ++I) { |
133 | L.emplace_front(Vs&: N1s[I], Vs&: N2s[I]); |
134 | EXPECT_EQ(N1s[I], L.front().V1); |
135 | EXPECT_EQ(N2s[I], L.front().V2); |
136 | } |
137 | // Instantiate with EmplaceOnly. |
138 | while (!L.empty()) |
139 | L.pop_front(); |
140 | } |
141 | |
142 | TEST(BumpPtrListTest, swap) { |
143 | // Build two lists with different lifetimes and swap them. |
144 | int N1s[] = {1, 3, 5, 7, 9}; |
145 | int N2s[] = {2, 4, 6, 8, 10}; |
146 | |
147 | BumpPtrList<int> L1; |
148 | L1.insert(I: L1.end(), First: std::begin(arr&: N1s), Last: std::end(arr&: N1s)); |
149 | { |
150 | BumpPtrList<int> L2; |
151 | L2.insert(I: L2.end(), First: std::begin(arr&: N2s), Last: std::end(arr&: N2s)); |
152 | |
153 | // Swap the lists. |
154 | L1.swap(RHS&: L2); |
155 | |
156 | // Check L2's contents before it goes out of scope. |
157 | auto I = L2.begin(); |
158 | for (int N : N1s) |
159 | EXPECT_EQ(N, *I++); |
160 | EXPECT_EQ(I, L2.end()); |
161 | } |
162 | |
163 | // Check L1's contents now that L2 is out of scope (with its allocation |
164 | // blocks). |
165 | auto I = L1.begin(); |
166 | for (int N : N2s) |
167 | EXPECT_EQ(N, *I++); |
168 | EXPECT_EQ(I, L1.end()); |
169 | } |
170 | |
171 | TEST(BumpPtrListTest, clear) { |
172 | CountsDestructors::NumCalls = 0; |
173 | CountsDestructors N; |
174 | BumpPtrList<CountsDestructors> L; |
175 | L.push_back(V: N); |
176 | L.push_back(V: N); |
177 | L.push_back(V: N); |
178 | EXPECT_EQ(3u, L.size()); |
179 | EXPECT_EQ(0u, CountsDestructors::NumCalls); |
180 | L.pop_back(); |
181 | EXPECT_EQ(1u, CountsDestructors::NumCalls); |
182 | L.clear(); |
183 | EXPECT_EQ(3u, CountsDestructors::NumCalls); |
184 | } |
185 | |
186 | TEST(BumpPtrListTest, move) { |
187 | BumpPtrList<int> L1, L2; |
188 | L1.push_back(V: 1); |
189 | L2.push_back(V: 2); |
190 | L1 = std::move(L2); |
191 | EXPECT_EQ(1u, L1.size()); |
192 | EXPECT_EQ(2, L1.front()); |
193 | EXPECT_EQ(0u, L2.size()); |
194 | } |
195 | |
196 | TEST(BumpPtrListTest, moveCallsDestructors) { |
197 | CountsDestructors::NumCalls = 0; |
198 | BumpPtrList<CountsDestructors> L1, L2; |
199 | L1.emplace_back(); |
200 | EXPECT_EQ(0u, CountsDestructors::NumCalls); |
201 | L1 = std::move(L2); |
202 | EXPECT_EQ(1u, CountsDestructors::NumCalls); |
203 | } |
204 | |
205 | TEST(BumpPtrListTest, copy) { |
206 | BumpPtrList<int> L1, L2; |
207 | L1.push_back(V: 1); |
208 | L2.push_back(V: 2); |
209 | L1 = L2; |
210 | EXPECT_EQ(1u, L1.size()); |
211 | EXPECT_EQ(2, L1.front()); |
212 | EXPECT_EQ(1u, L2.size()); |
213 | EXPECT_EQ(2, L2.front()); |
214 | } |
215 | |
216 | TEST(BumpPtrListTest, copyCallsDestructors) { |
217 | CountsDestructors::NumCalls = 0; |
218 | BumpPtrList<CountsDestructors> L1, L2; |
219 | L1.emplace_back(); |
220 | EXPECT_EQ(0u, CountsDestructors::NumCalls); |
221 | L1 = L2; |
222 | EXPECT_EQ(1u, CountsDestructors::NumCalls); |
223 | } |
224 | |
225 | TEST(BumpPtrListTest, resetAlloc) { |
226 | // Resetting an empty list should work. |
227 | BumpPtrList<int> L; |
228 | |
229 | // Resetting an empty list that has allocated should also work. |
230 | L.resetAlloc(); |
231 | L.push_back(V: 5); |
232 | L.erase(I: L.begin()); |
233 | L.resetAlloc(); |
234 | |
235 | // Resetting a non-empty list should crash. |
236 | L.push_back(V: 5); |
237 | #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) |
238 | EXPECT_DEATH(L.resetAlloc(), "Cannot reset allocator if not empty" ); |
239 | #endif |
240 | } |
241 | |
242 | } // end namespace |
243 | |