xref: /aosp_15_r20/external/perfetto/src/base/small_vector_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/ext/base/small_vector.h"
18 
19 #include <tuple>
20 #include <utility>
21 
22 #include "test/gtest_and_gmock.h"
23 
24 namespace perfetto {
25 namespace base {
26 namespace {
27 
28 int g_instances = 0;
29 
30 struct Obj {
Objperfetto::base::__anoncb840ffe0111::Obj31   explicit Obj(size_t v = 0) : value(v) {
32     EXPECT_FALSE(constructed);
33     constructed = true;
34     g_instances++;
35   }
36 
~Objperfetto::base::__anoncb840ffe0111::Obj37   ~Obj() {
38     EXPECT_TRUE(constructed);
39     g_instances--;
40   }
41 
42   // Move operators.
Objperfetto::base::__anoncb840ffe0111::Obj43   Obj(Obj&& other) noexcept {
44     g_instances++;
45     constructed = true;
46     moved_into = true;
47     value = other.value;
48     other.moved_from = true;
49     other.value = 0xffffffff - value;
50   }
51 
operator =perfetto::base::__anoncb840ffe0111::Obj52   Obj& operator=(Obj&& other) noexcept {
53     this->~Obj();
54     new (this) Obj(std::move(other));
55     return *this;
56   }
57 
58   // Copy operators.
Objperfetto::base::__anoncb840ffe0111::Obj59   Obj(const Obj& other) {
60     other.copied_from = true;
61     g_instances++;
62     constructed = true;
63     copied_into = true;
64     value = other.value;
65   }
66 
operator =perfetto::base::__anoncb840ffe0111::Obj67   Obj& operator=(const Obj& other) {
68     this->~Obj();
69     new (this) Obj(other);
70     return *this;
71   }
72 
73   uintptr_t addr = reinterpret_cast<uintptr_t>(this);
74   bool constructed = false;
75   size_t value = 0;
76   bool moved_from = false;
77   mutable bool copied_from = false;
78   bool moved_into = false;
79   bool copied_into = false;
80 };
81 
TEST(SmallVectorTest,StaySmall)82 TEST(SmallVectorTest, StaySmall) {
83   SmallVector<Obj, 8> v;
84   EXPECT_EQ(g_instances, 0);
85   EXPECT_EQ(v.size(), 0u);
86   EXPECT_TRUE(v.empty());
87   EXPECT_EQ(v.begin(), v.end());
88 
89   for (size_t i = 1; i <= 8; i++) {
90     v.emplace_back(i);
91     EXPECT_EQ(g_instances, static_cast<int>(i));
92     EXPECT_FALSE(v.empty());
93     EXPECT_EQ(v.end(), v.begin() + i);
94     EXPECT_EQ(v.back().value, i);
95     EXPECT_EQ(v[static_cast<size_t>(i - 1)].value, i);
96     EXPECT_EQ(v[static_cast<size_t>(i - 1)].value, i);
97   }
98 
99   for (size_t i = 1; i <= 3; i++) {
100     v.pop_back();
101     EXPECT_EQ(g_instances, 8 - static_cast<int>(i));
102   }
103 
104   v.clear();
105   EXPECT_EQ(g_instances, 0);
106 }
107 
TEST(SmallVectorTest,GrowOnHeap)108 TEST(SmallVectorTest, GrowOnHeap) {
109   SmallVector<Obj, 4> v;
110   for (size_t i = 0; i < 10; i++) {
111     v.emplace_back(i);
112     EXPECT_EQ(g_instances, static_cast<int>(i + 1));
113     EXPECT_FALSE(v.empty());
114     EXPECT_EQ(v.end(), v.begin() + i + 1);
115     EXPECT_EQ(v[i].value, i);
116   }
117 
118   // Do a second pass and check that the initial elements aren't corrupt.
119   for (size_t i = 0; i < 10; i++) {
120     EXPECT_EQ(v[i].value, i);
121     EXPECT_TRUE(v[i].constructed);
122   }
123 
124   // The first 4 elements must have been moved into because of the heap growth.
125   for (size_t i = 0; i < 4; i++)
126     EXPECT_TRUE(v[i].moved_into);
127   EXPECT_FALSE(v.back().moved_into);
128 }
129 
130 class SmallVectorTestP : public testing::TestWithParam<size_t> {};
131 
TEST_P(SmallVectorTestP,MoveOperators)132 TEST_P(SmallVectorTestP, MoveOperators) {
133   size_t num_elements = GetParam();
134   static constexpr size_t kInlineCapacity = 4;
135   SmallVector<Obj, kInlineCapacity> v1;
136   for (size_t i = 0; i < num_elements; i++)
137     v1.emplace_back(i);
138 
139   SmallVector<Obj, kInlineCapacity> v2(std::move(v1));
140   EXPECT_TRUE(v1.empty());
141   EXPECT_EQ(v2.size(), num_elements);
142 
143   // Check that v2 (the moved into vector) is consistent.
144   for (size_t i = 0; i < num_elements; i++) {
145     EXPECT_EQ(v2[i].value, i);
146     EXPECT_TRUE(v2[i].constructed);
147     if (num_elements <= kInlineCapacity) {
148       EXPECT_TRUE(v2[i].moved_into);
149     }
150   }
151 
152   // Check that v1 (the moved-from object) is still usable.
153   EXPECT_EQ(v1.size(), 0u);
154 
155   for (size_t i = 0; i < num_elements; i++) {
156     v1.emplace_back(1000 + i);
157     EXPECT_EQ(v1.size(), i + 1);
158   }
159 
160   EXPECT_NE(v1.data(), v2.data());
161 
162   for (size_t i = 0; i < num_elements; i++) {
163     EXPECT_EQ(v1[i].value, 1000 + i);
164     EXPECT_EQ(v2[i].value, i);
165     EXPECT_TRUE(v1[i].constructed);
166     EXPECT_FALSE(v1[i].moved_from);
167   }
168 
169   // Now swap again using the move-assignment.
170 
171   v1 = std::move(v2);
172   EXPECT_EQ(v1.size(), num_elements);
173   EXPECT_TRUE(v2.empty());
174   for (size_t i = 0; i < num_elements; i++) {
175     EXPECT_EQ(v1[i].value, i);
176     EXPECT_TRUE(v1[i].constructed);
177   }
178 
179   { auto destroy = std::move(v1); }
180 
181   EXPECT_EQ(g_instances, 0);
182 }
183 
TEST_P(SmallVectorTestP,CopyOperators)184 TEST_P(SmallVectorTestP, CopyOperators) {
185   size_t num_elements = GetParam();
186   static constexpr size_t kInlineCapacity = 4;
187   SmallVector<Obj, kInlineCapacity> v1;
188   for (size_t i = 0; i < num_elements; i++)
189     v1.emplace_back(i);
190 
191   SmallVector<Obj, kInlineCapacity> v2(v1);
192   EXPECT_EQ(v1.size(), num_elements);
193   EXPECT_EQ(v2.size(), num_elements);
194   EXPECT_EQ(g_instances, static_cast<int>(num_elements * 2));
195 
196   for (size_t i = 0; i < num_elements; i++) {
197     EXPECT_EQ(v1[i].value, i);
198     EXPECT_TRUE(v1[i].copied_from);
199     EXPECT_EQ(v2[i].value, i);
200     EXPECT_TRUE(v2[i].copied_into);
201   }
202 
203   // Now edit v2.
204   for (size_t i = 0; i < num_elements; i++)
205     v2[i].value = i + 100;
206   EXPECT_EQ(g_instances, static_cast<int>(num_elements * 2));
207 
208   // Append some extra elements.
209   for (size_t i = 0; i < num_elements; i++)
210     v2.emplace_back(i + 200);
211   EXPECT_EQ(g_instances, static_cast<int>(num_elements * 3));
212 
213   for (size_t i = 0; i < num_elements * 2; i++) {
214     if (i < num_elements) {
215       EXPECT_EQ(v1[i].value, i);
216       EXPECT_EQ(v2[i].value, 100 + i);
217     } else {
218       EXPECT_EQ(v2[i].value, 200 + i - num_elements);
219     }
220   }
221 
222   v2.clear();
223   EXPECT_EQ(g_instances, static_cast<int>(num_elements));
224 }
225 
226 INSTANTIATE_TEST_SUITE_P(SmallVectorTest,
227                          SmallVectorTestP,
228                          testing::Values(2, 4, 7, 512));
229 
230 }  // namespace
231 }  // namespace base
232 }  // namespace perfetto
233