• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 <ftl/static_vector.h>
18 #include <gtest/gtest.h>
19 
20 #include <algorithm>
21 #include <iterator>
22 #include <string>
23 #include <utility>
24 
25 using namespace std::string_literals;
26 
27 namespace android::test {
28 
29 using ftl::StaticVector;
30 
31 // Keep in sync with example usage in header file.
TEST(StaticVector,Example)32 TEST(StaticVector, Example) {
33   ftl::StaticVector<char, 3> vector;
34   EXPECT_TRUE(vector.empty());
35 
36   vector = {'a', 'b'};
37   EXPECT_EQ(vector.size(), 2u);
38 
39   vector.push_back('c');
40   EXPECT_TRUE(vector.full());
41 
42   EXPECT_FALSE(vector.push_back('d'));
43   EXPECT_EQ(vector.size(), 3u);
44 
45   vector.unstable_erase(vector.begin());
46   EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'}));
47 
48   vector.pop_back();
49   EXPECT_EQ(vector.back(), 'c');
50 
51   const char array[] = "hi";
52   vector = ftl::StaticVector(array);
53   EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
54 
55   ftl::StaticVector strings = ftl::init::list<std::string>("abc")("123456", 3u)(3u, '?');
56   ASSERT_EQ(strings.size(), 3u);
57 
58   EXPECT_EQ(strings[0], "abc");
59   EXPECT_EQ(strings[1], "123");
60   EXPECT_EQ(strings[2], "???");
61 }
62 
TEST(StaticVector,Construct)63 TEST(StaticVector, Construct) {
64   {
65     // Default constructor.
66     StaticVector<std::string, 2> vector;
67     EXPECT_TRUE(vector.empty());
68   }
69   {
70     // Array constructor.
71     const float floats[] = {.1f, .2f, .3f};
72     StaticVector vector(floats);
73     EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f}));
74   }
75   {
76     // Iterator constructor.
77     const char chars[] = "abcdef";
78     std::string string(chars);
79     StaticVector<char, sizeof(chars)> vector(string.begin(), string.end());
80 
81     EXPECT_STREQ(vector.begin(), chars);
82   }
83   {
84     // Variadic constructor with same types.
85     StaticVector vector = {1, 2, 3};
86 
87     static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>);
88     EXPECT_EQ(vector, (StaticVector{1, 2, 3}));
89   }
90   {
91     // Variadic constructor with different types.
92     const auto copy = "quince"s;
93     auto move = "tart"s;
94     StaticVector vector = {copy, std::move(move)};
95 
96     static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>);
97     EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s}));
98   }
99   {
100     // In-place constructor with same types.
101     StaticVector vector =
102         ftl::init::list<std::string>("redolent", 3u)("velveteen", 6u)("cakewalk", 4u);
103 
104     static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
105     EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
106   }
107   {
108     // In-place constructor with different types.
109     const auto copy = "red"s;
110     auto move = "velvet"s;
111     std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
112     StaticVector vector = ftl::init::list<std::string>(copy.c_str())(std::move(move))(list);
113 
114     static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
115     EXPECT_TRUE(move.empty());
116     EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
117   }
118   {
119     struct String {
120       explicit String(const char* str) : str(str) {}
121       explicit String(const char** ptr) : str(*ptr) {}
122       const char* str;
123     };
124 
125     const char* strings[] = {"a", "b", "c", "d"};
126 
127     {
128       // Two iterator-like elements.
129       StaticVector<String, 3> vector(strings, strings + 3);
130       ASSERT_EQ(vector.size(), 2u);
131 
132       EXPECT_STREQ(vector[0].str, "a");
133       EXPECT_STREQ(vector[1].str, "d");
134     }
135     {
136       // Disambiguating iterator constructor.
137       StaticVector<String, 3> vector(ftl::kIteratorRange, strings, strings + 3);
138       ASSERT_EQ(vector.size(), 3u);
139 
140       EXPECT_STREQ(vector[0].str, "a");
141       EXPECT_STREQ(vector[1].str, "b");
142       EXPECT_STREQ(vector[2].str, "c");
143     }
144   }
145 }
146 
TEST(StaticVector,Copy)147 TEST(StaticVector, Copy) {
148   {
149     // Same capacity.
150     const StaticVector vector = {"snow"s, "cone"s};
151 
152     StaticVector<const std::string, 2> copy(vector);
153     EXPECT_EQ(copy, vector);
154 
155     // The vector is assignable even if T is const.
156     const StaticVector<std::string, 2> other = {"tiramisu"s};
157     copy = other;
158     EXPECT_EQ(copy, other);
159   }
160   {
161     // From smaller capacity.
162     const StaticVector vector = {"snow"s, "cone"s};
163 
164     StaticVector<const std::string, 3> copy(vector);
165     EXPECT_EQ(copy, vector);
166 
167     // The vector is assignable even if T is const.
168     const StaticVector other = {"tiramisu"s};
169     copy = other;
170     EXPECT_EQ(copy, other);
171   }
172 }
173 
TEST(StaticVector,Move)174 TEST(StaticVector, Move) {
175   {
176     // Same capacity.
177     StaticVector vector = {"snow"s, "cone"s};
178 
179     StaticVector<const std::string, 2> move(std::move(vector));
180     EXPECT_TRUE(vector.empty());
181     EXPECT_EQ(move, (StaticVector{"snow"s, "cone"s}));
182 
183     // The vector is assignable even if T is const.
184     StaticVector<std::string, 2> other = {"tiramisu"s};
185     move = std::move(other);
186     EXPECT_TRUE(other.empty());
187     EXPECT_EQ(move, (StaticVector{"tiramisu"s}));
188   }
189   {
190     // From smaller capacity.
191     StaticVector vector = {"snow"s, "cone"s};
192 
193     StaticVector<const std::string, 3> move(std::move(vector));
194     EXPECT_TRUE(vector.empty());
195     EXPECT_EQ(move, (StaticVector{"snow"s, "cone"s}));
196 
197     // The vector is assignable even if T is const.
198     StaticVector other = {"tiramisu"s};
199     move = std::move(other);
200     EXPECT_TRUE(other.empty());
201     EXPECT_EQ(move, (StaticVector{"tiramisu"s}));
202   }
203 }
204 
TEST(StaticVector,String)205 TEST(StaticVector, String) {
206   StaticVector<char, 10> chars;
207   char c = 'a';
208   std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
209   chars.back() = '\0';
210 
211   EXPECT_STREQ(chars.begin(), "abcdefghi");
212 
213   // Constructor takes iterator range.
214   const char numbers[] = "123456";
215   StaticVector<char, 10> string(std::begin(numbers), std::end(numbers));
216 
217   EXPECT_STREQ(string.begin(), "123456");
218   EXPECT_EQ(string.size(), 7u);
219 
220   // Similar to emplace, but replaces rather than inserts.
221   string.replace(string.begin() + 5, '\0');
222   EXPECT_STREQ(string.begin(), "12345");
223 
224   swap(chars, string);
225 
226   EXPECT_STREQ(chars.begin(), "12345");
227   EXPECT_STREQ(string.begin(), "abcdefghi");
228 }
229 
TEST(StaticVector,CopyableElement)230 TEST(StaticVector, CopyableElement) {
231   struct Pair {
232     const int a, b;
233     bool operator==(Pair p) const { return p.a == a && p.b == b; }
234   };
235 
236   StaticVector<Pair, 5> pairs;
237 
238   EXPECT_TRUE(pairs.empty());
239   EXPECT_EQ(pairs.max_size(), 5u);
240 
241   for (size_t i = 0; i < pairs.max_size(); ++i) {
242     EXPECT_EQ(pairs.size(), i);
243 
244     const int a = static_cast<int>(i) * 2;
245     const auto it = pairs.emplace_back(a, a + 1);
246     ASSERT_NE(it, pairs.end());
247     EXPECT_EQ(*it, (Pair{a, a + 1}));
248   }
249 
250   EXPECT_TRUE(pairs.full());
251   EXPECT_EQ(pairs.size(), 5u);
252 
253   // Insertion fails if the vector is full.
254   const auto it = pairs.emplace_back(10, 11);
255   EXPECT_EQ(it, pairs.end());
256 
257   EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}}));
258 
259   // Constructor takes at most N elements.
260   StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1};
261   EXPECT_TRUE(sums.full());
262 
263   // Random-access iterators comply with standard.
264   std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
265   EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1}));
266 
267   sums.pop_back();
268   std::reverse(sums.begin(), sums.end());
269 
270   EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1}));
271 }
272 
TEST(StaticVector,MovableElement)273 TEST(StaticVector, MovableElement) {
274   // Construct std::string elements in place from per-element arguments.
275   StaticVector strings = ftl::init::list<std::string>()()()("cake")("velvet")("red")();
276   strings.pop_back();
277 
278   EXPECT_EQ(strings.max_size(), 7u);
279   EXPECT_EQ(strings.size(), 6u);
280 
281   // Erase "cake" and append a substring copy.
282   {
283     auto it =
284         std::find_if(strings.begin(), strings.end(), [](const auto& s) { return !s.empty(); });
285     ASSERT_FALSE(it == strings.end());
286     EXPECT_EQ(*it, "cake");
287 
288     strings.unstable_erase(it);
289 
290     // Construct std::string from first 4 characters of string literal.
291     it = strings.emplace_back("cakewalk", 4u);
292     ASSERT_NE(it, strings.end());
293     EXPECT_EQ(*it, "cake"s);
294   }
295 
296   strings[1] = "quince"s;
297 
298   // Replace last empty string with "tart".
299   {
300     const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
301     ASSERT_FALSE(rit == strings.rend());
302 
303     std::initializer_list<char> list = {'t', 'a', 'r', 't'};
304     strings.replace(rit.base() - 1, list);
305   }
306 
307   strings.front().assign("pie");
308 
309   EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
310 }
311 
TEST(StaticVector,Replace)312 TEST(StaticVector, Replace) {
313   // Replacing does not require a copy/move assignment operator.
314   struct Word {
315     explicit Word(std::string str) : str(std::move(str)) {}
316     const std::string str;
317   };
318 
319   StaticVector words = ftl::init::list<Word>("red")("velour")("cake");
320 
321   // The replaced element can be referenced by the replacement.
322   const auto it = words.begin() + 1;
323   const Word& word = words.replace(it, it->str.substr(0, 3) + "vet");
324   EXPECT_EQ(word.str, "velvet");
325 }
326 
TEST(StaticVector,ReverseTruncate)327 TEST(StaticVector, ReverseTruncate) {
328   StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake");
329   EXPECT_FALSE(strings.full());
330 
331   for (auto it = strings.begin(); it != strings.end(); ++it) {
332     strings.replace(it, strings.back());
333     strings.pop_back();
334   }
335 
336   EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s}));
337 }
338 
TEST(StaticVector,Sort)339 TEST(StaticVector, Sort) {
340   StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake");
341   EXPECT_FALSE(strings.full());
342 
343   auto sorted = std::move(strings);
344   EXPECT_TRUE(strings.empty());
345 
346   std::sort(sorted.begin(), sorted.end());
347   EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
348 
349   // Constructor takes array reference.
350   {
351     const char* array[] = {"cake", "lie"};
352     strings = StaticVector(array);
353   }
354 
355   EXPECT_GT(sorted, strings);
356   swap(sorted, strings);
357   EXPECT_LT(sorted, strings);
358 
359   // Append remaining elements, such that "pie" is the only difference.
360   for (const char* str : {"quince", "red", "tart", "velvet"}) {
361     sorted.emplace_back(str);
362   }
363 
364   EXPECT_NE(sorted, strings);
365 
366   // Replace second element with "pie".
367   const auto it = sorted.begin() + 1;
368   EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
369 
370   EXPECT_EQ(sorted, strings);
371 }
372 
373 namespace {
374 
375 struct DestroyCounts {
DestroyCountsandroid::test::__anonecdb20820411::DestroyCounts376   DestroyCounts(int& live, int& dead) : counts{live, dead} {}
DestroyCountsandroid::test::__anonecdb20820411::DestroyCounts377   DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
DestroyCountsandroid::test::__anonecdb20820411::DestroyCounts378   DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
~DestroyCountsandroid::test::__anonecdb20820411::DestroyCounts379   ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
380 
381   struct {
382     int& live;
383     int& dead;
384   } counts;
385 
386   bool alive = true;
387 };
388 
389 }  // namespace
390 
TEST(StaticVector,Destroy)391 TEST(StaticVector, Destroy) {
392   int live = 0;
393   int dead = 0;
394   {
395     // Empty.
396     StaticVector<DestroyCounts, 5> counts;
397   }
398   EXPECT_EQ(0, live);
399   EXPECT_EQ(0, dead);
400 
401   {
402     // Non-empty.
403     StaticVector<DestroyCounts, 5> counts;
404     counts.emplace_back(live, dead);
405     counts.emplace_back(live, dead);
406     counts.emplace_back(live, dead);
407   }
408   EXPECT_EQ(3, live);
409   EXPECT_EQ(0, dead);
410 
411   live = 0;
412   {
413     // Copy.
414     StaticVector<DestroyCounts, 5> counts;
415     counts.emplace_back(live, dead);
416     counts.emplace_back(live, dead);
417     counts.emplace_back(live, dead);
418 
419     const auto copy = counts;
420   }
421   EXPECT_EQ(6, live);
422   EXPECT_EQ(0, dead);
423 
424   live = 0;
425   {
426     // Move.
427     StaticVector<DestroyCounts, 5> counts;
428     counts.emplace_back(live, dead);
429     counts.emplace_back(live, dead);
430     counts.emplace_back(live, dead);
431 
432     const auto move = std::move(counts);
433   }
434   EXPECT_EQ(3, live);
435   EXPECT_EQ(3, dead);
436 
437   live = dead = 0;
438   {
439     // Swap.
440     StaticVector<DestroyCounts, 5> counts1;
441     counts1.emplace_back(live, dead);
442     counts1.emplace_back(live, dead);
443     counts1.emplace_back(live, dead);
444 
445     StaticVector<DestroyCounts, 5> counts2;
446     counts2.emplace_back(live, dead);
447     counts2.emplace_back(live, dead);
448 
449     swap(counts1, counts2);
450 
451     EXPECT_EQ(2u, counts1.size());
452     EXPECT_EQ(3u, counts2.size());
453 
454     EXPECT_EQ(0, live);
455     EXPECT_EQ(7, dead);  // 3 moves per swap, plus 1 move.
456 
457     dead = 0;
458   }
459   EXPECT_EQ(5, live);
460   EXPECT_EQ(0, dead);
461 }
462 
TEST(StaticVector,Clear)463 TEST(StaticVector, Clear) {
464   int live = 0;
465   int dead = 0;
466   {
467     StaticVector<DestroyCounts, 5> counts;
468     counts.emplace_back(live, dead);
469     counts.emplace_back(live, dead);
470 
471     counts.clear();
472 
473     EXPECT_TRUE(counts.empty());
474   }
475   EXPECT_EQ(2, live);
476   EXPECT_EQ(0, dead);
477 }
478 
479 }  // namespace android::test
480