Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
TArrayTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/private/base/SkASAN.h" // IWYU pragma: keep
10#include "src/base/SkRandom.h"
11#include "tests/Test.h"
12
13#include <array>
14#include <cstdint>
15#include <initializer_list>
16#include <utility>
17
18using namespace skia_private;
19
20// This class is used to test SkTArray's behavior with classes containing a vtable.
21
22namespace {
23
24class TestClass {
25public:
26 TestClass() = default;
27 TestClass(const TestClass&) = default;
28 TestClass& operator=(const TestClass&) = default;
29 TestClass(int v) : value(v) {}
30 virtual ~TestClass() {}
31
32 bool operator==(const TestClass& c) const { return value == c.value; }
33
34 int value = 0;
35};
36
37} // namespace
38
39// Tests the TArray<T> class template.
40
41template <typename T, bool MEM_MOVE>
44
45 // Starts empty.
46 REPORTER_ASSERT(reporter, a.empty());
47 REPORTER_ASSERT(reporter, a.size() == 0);
48
49 // { }, add a default constructed element
50 a.push_back() = T{0};
51 REPORTER_ASSERT(reporter, !a.empty());
52 REPORTER_ASSERT(reporter, a.size() == 1);
53
54 // { 0 }, removeShuffle the only element.
55 a.removeShuffle(0);
56 REPORTER_ASSERT(reporter, a.empty());
57 REPORTER_ASSERT(reporter, a.size() == 0);
58
59 // { }, add a default, add a 1, remove first
60 a.push_back() = T{0};
61 a.push_back() = T{1};
62 a.removeShuffle(0);
63 REPORTER_ASSERT(reporter, !a.empty());
64 REPORTER_ASSERT(reporter, a.size() == 1);
65 REPORTER_ASSERT(reporter, a[0] == T{1});
66
67 // { 1 }, replace with new array
68 T b[5] = {T{0}, T{1}, T{2}, T{3}, T{4}};
69 a.reset(b, std::size(b));
70 REPORTER_ASSERT(reporter, a.size() == std::size(b));
71 REPORTER_ASSERT(reporter, a[2] == T{2});
72 REPORTER_ASSERT(reporter, a[4] == T{4});
73
74 // { 0, 1, 2, 3, 4 }, removeShuffle the last
75 a.removeShuffle(4);
76 REPORTER_ASSERT(reporter, a.size() == std::size(b) - 1);
77 REPORTER_ASSERT(reporter, a[3] == T{3});
78
79 // { 0, 1, 2, 3 }, remove a middle, note shuffle
80 a.removeShuffle(1);
81 REPORTER_ASSERT(reporter, a.size() == std::size(b) - 2);
82 REPORTER_ASSERT(reporter, a[0] == T{0});
83 REPORTER_ASSERT(reporter, a[1] == T{3});
84 REPORTER_ASSERT(reporter, a[2] == T{2});
85
86 // { 0, 3, 2 }
87}
88
89template <typename T> static void test_construction(skiatest::Reporter* reporter) {
90 using ValueType = typename T::value_type;
91
92 // No arguments: Creates an empty array with no initial storage.
93 T arrayNoArgs;
94 REPORTER_ASSERT(reporter, arrayNoArgs.empty());
95
96 // Single integer: Creates an empty array that will preallocate space for reserveCount elements.
97 T arrayReserve(15);
98 REPORTER_ASSERT(reporter, arrayReserve.empty());
99 // May get some extra elements for free because sk_allocate_* can round up.
100 REPORTER_ASSERT(reporter, arrayReserve.capacity() >= 15 && arrayReserve.capacity() < 50);
101
102 // Another array, const&: Copies one array to another.
103 T arrayInitial;
104 arrayInitial.push_back(ValueType{1});
105 arrayInitial.push_back(ValueType{2});
106 arrayInitial.push_back(ValueType{3});
107
108 T arrayCopy(arrayInitial);
109 REPORTER_ASSERT(reporter, arrayInitial.size() == 3);
110 REPORTER_ASSERT(reporter, arrayInitial[0] == ValueType{1});
111 REPORTER_ASSERT(reporter, arrayInitial[1] == ValueType{2});
112 REPORTER_ASSERT(reporter, arrayInitial[2] == ValueType{3});
113 REPORTER_ASSERT(reporter, arrayCopy.size() == 3);
114 REPORTER_ASSERT(reporter, arrayCopy[0] == ValueType{1});
115 REPORTER_ASSERT(reporter, arrayCopy[1] == ValueType{2});
116 REPORTER_ASSERT(reporter, arrayCopy[2] == ValueType{3});
117
118 // Another array, &&: Moves one array to another.
119 T arrayMove(std::move(arrayInitial));
120 REPORTER_ASSERT(reporter, arrayInitial.empty()); // NOLINT(bugprone-use-after-move)
121 REPORTER_ASSERT(reporter, arrayMove.size() == 3);
122 REPORTER_ASSERT(reporter, arrayMove[0] == ValueType{1});
123 REPORTER_ASSERT(reporter, arrayMove[1] == ValueType{2});
124 REPORTER_ASSERT(reporter, arrayMove[2] == ValueType{3});
125
126 // Pointer and count: Copies contents of a standard C array.
127 typename T::value_type data[3] = { 7, 8, 9 };
128 T arrayPtrCount(data, 3);
129 REPORTER_ASSERT(reporter, arrayPtrCount.size() == 3);
130 REPORTER_ASSERT(reporter, arrayPtrCount[0] == ValueType{7});
131 REPORTER_ASSERT(reporter, arrayPtrCount[1] == ValueType{8});
132 REPORTER_ASSERT(reporter, arrayPtrCount[2] == ValueType{9});
133
134 // Initializer list.
135 T arrayInitializer{8, 6, 7, 5, 3, 0, 9};
136 REPORTER_ASSERT(reporter, arrayInitializer.size() == 7);
137 REPORTER_ASSERT(reporter, arrayInitializer[0] == ValueType{8});
138 REPORTER_ASSERT(reporter, arrayInitializer[1] == ValueType{6});
139 REPORTER_ASSERT(reporter, arrayInitializer[2] == ValueType{7});
140 REPORTER_ASSERT(reporter, arrayInitializer[3] == ValueType{5});
141 REPORTER_ASSERT(reporter, arrayInitializer[4] == ValueType{3});
142 REPORTER_ASSERT(reporter, arrayInitializer[5] == ValueType{0});
143 REPORTER_ASSERT(reporter, arrayInitializer[6] == ValueType{9});
144}
145
146template <typename T, typename U>
148 // We expect SkTArrays of the same type to be copyable and movable, even when:
149 // - one side is an SkTArray, and the other side is an SkSTArray
150 // - both sides are SkSTArray, but each side has a different internal capacity
151 T tArray;
152 tArray.push_back(1);
153 tArray.push_back(2);
154 tArray.push_back(3);
155 T tArray2 = tArray;
156
157 // Copy construction from other-type array.
158 U arrayCopy(tArray);
159 REPORTER_ASSERT(reporter, tArray.size() == 3);
160 REPORTER_ASSERT(reporter, tArray[0] == 1);
161 REPORTER_ASSERT(reporter, tArray[1] == 2);
162 REPORTER_ASSERT(reporter, tArray[2] == 3);
163 REPORTER_ASSERT(reporter, arrayCopy.size() == 3);
164 REPORTER_ASSERT(reporter, arrayCopy[0] == 1);
165 REPORTER_ASSERT(reporter, arrayCopy[1] == 2);
166 REPORTER_ASSERT(reporter, arrayCopy[2] == 3);
167
168 // Assignment from other-type array.
169 U arrayAssignment;
170 arrayAssignment = tArray;
171 REPORTER_ASSERT(reporter, tArray.size() == 3);
172 REPORTER_ASSERT(reporter, tArray[0] == 1);
173 REPORTER_ASSERT(reporter, tArray[1] == 2);
174 REPORTER_ASSERT(reporter, tArray[2] == 3);
175 REPORTER_ASSERT(reporter, arrayAssignment.size() == 3);
176 REPORTER_ASSERT(reporter, arrayAssignment[0] == 1);
177 REPORTER_ASSERT(reporter, arrayAssignment[1] == 2);
178 REPORTER_ASSERT(reporter, arrayAssignment[2] == 3);
179
180 // Move construction from other-type array.
181 U arrayMove(std::move(tArray));
182 REPORTER_ASSERT(reporter, tArray.empty()); // NOLINT(bugprone-use-after-move)
183 REPORTER_ASSERT(reporter, arrayMove.size() == 3);
184 REPORTER_ASSERT(reporter, arrayMove[0] == 1);
185 REPORTER_ASSERT(reporter, arrayMove[1] == 2);
186 REPORTER_ASSERT(reporter, arrayMove[2] == 3);
187
188 // Move assignment from other-type array.
189 U arrayMoveAssign;
190 arrayMoveAssign = std::move(tArray2);
191 REPORTER_ASSERT(reporter, tArray2.empty()); // NOLINT(bugprone-use-after-move)
192 REPORTER_ASSERT(reporter, arrayMoveAssign.size() == 3);
193 REPORTER_ASSERT(reporter, arrayMoveAssign[0] == 1);
194 REPORTER_ASSERT(reporter, arrayMoveAssign[1] == 2);
195 REPORTER_ASSERT(reporter, arrayMoveAssign[2] == 3);
196}
197
198// Move-only type used for testing swap and move_back() of TArray&&'s.
199namespace {
200struct MoveOnlyInt {
201 MoveOnlyInt(int i) : fInt(i) {}
202 MoveOnlyInt(MoveOnlyInt&& that) : fInt(that.fInt) {}
203 bool operator==(int i) const { return fInt == i; }
204 int fInt;
205};
206} // anonymous
207
208template <typename T> static void test_swap(skiatest::Reporter* reporter,
209 SkSpan<TArray<T>*> arrays,
210 SkSpan<const int> sizes) {
211 for (auto a : arrays) {
212 for (auto b : arrays) {
213 if (a == b) {
214 continue;
215 }
216
217 for (auto sizeA : sizes) {
218 for (auto sizeB : sizes) {
219 a->clear();
220 b->clear();
221
222 int curr = 0;
223 for (int i = 0; i < sizeA; i++) { a->push_back(curr++); }
224 for (int i = 0; i < sizeB; i++) { b->push_back(curr++); }
225
226 a->swap(*b);
227 REPORTER_ASSERT(reporter, b->size() == sizeA);
228 REPORTER_ASSERT(reporter, a->size() == sizeB);
229
230 curr = 0;
231 for (auto&& x : *b) { REPORTER_ASSERT(reporter, x == curr++); }
232 for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
233
234 a->swap(*a);
235 curr = sizeA;
236 for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
237 }}
238 }}
239}
240
242 static constexpr int kSizes[] = {0, 1, 5, 10, 15, 20, 25};
243
244 TArray<int> arr;
245 STArray< 5, int> arr5;
246 STArray<10, int> arr10;
247 STArray<20, int> arr20;
248 TArray<int>* arrays[] = { &arr, &arr5, &arr10, &arr20 };
249 test_swap<int>(reporter, arrays, kSizes);
250
255 TArray<MoveOnlyInt>* arraysMoi[] = { &moi, &moi5, &moi10, &moi20 };
256 test_swap<MoveOnlyInt>(reporter, arraysMoi, kSizes);
257}
258
259template <typename T> static void test_array_move(skiatest::Reporter* reporter,
260 SkSpan<TArray<T>*> arrays,
261 SkSpan<const int> sizes) {
262 // self test is a no-op
263 for (auto a : arrays) {
264 for (auto sizeA : sizes) {
265 a->clear();
266 for (int i = 0; i < sizeA; i++) { a->push_back(i); }
267 a->move_back(*a);
268 REPORTER_ASSERT(reporter, a->size() == sizeA);
269 for (int i = 0; i < sizeA; i++) {
270 REPORTER_ASSERT(reporter, (*a)[i] == i);
271 }
272 }
273 }
274
275 for (auto a : arrays) {
276 for (auto b : arrays) {
277 if (a == b) {
278 continue;
279 }
280
281 for (auto sizeA : sizes) {
282 for (auto sizeB : sizes) {
283 a->clear();
284 b->clear();
285
286 int curr = 0;
287 for (int i = 0; i < sizeA; i++) { a->push_back(curr++); }
288 for (int i = 0; i < sizeB; i++) { b->push_back(curr++); }
289
290 a->move_back(*b);
291 REPORTER_ASSERT(reporter, b->size() == 0);
292 REPORTER_ASSERT(reporter, a->size() == sizeA + sizeB);
293
294 curr = 0;
295 for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
296 }}
297 }}
298}
299
301 static constexpr int kSizes[] = {0, 1, 5, 10, 15, 20, 25};
302
303 TArray<int> arr;
304 STArray< 5, int> arr5;
305 STArray<10, int> arr10;
306 STArray<20, int> arr20;
307 TArray<int>* arrays[] = { &arr, &arr5, &arr10, &arr20 };
308 test_array_move<int>(reporter, arrays, kSizes);
309
314 TArray<MoveOnlyInt>* arraysMoi[] = { &moi, &moi5, &moi10, &moi20 };
315 test_array_move<MoveOnlyInt>(reporter, arraysMoi, kSizes);
316}
317
319 {
321 REPORTER_ASSERT(reporter, a.capacity() == 0);
322 }
323 {
325 REPORTER_ASSERT(reporter, a.capacity() == 10);
326 }
327 {
328 TArray<int> a(1);
329 REPORTER_ASSERT(reporter, a.capacity() >= 1);
330 }
331 {
332 TArray<int> a, b;
333 b = a;
334 REPORTER_ASSERT(reporter, b.capacity() == 0);
335 }
336 {
339 b = a;
340 REPORTER_ASSERT(reporter, b.capacity() == 0);
341 }
342 {
344 TArray<int> b(a); // NOLINT(performance-unnecessary-copy-initialization)
345 REPORTER_ASSERT(reporter, b.capacity() == 0);
346 }
347 {
349 TArray<int> b(a); // NOLINT(performance-unnecessary-copy-initialization)
350 REPORTER_ASSERT(reporter, b.capacity() == 0);
351 }
352 {
354 TArray<int> b(std::move(a));
355 REPORTER_ASSERT(reporter, b.capacity() == 0);
356 }
357 {
359 TArray<int> b(std::move(a));
360 REPORTER_ASSERT(reporter, b.capacity() == 0);
361 }
362 {
365 b = std::move(a);
366 REPORTER_ASSERT(reporter, b.capacity() == 0);
367 }
368 {
371 b = std::move(a);
372 REPORTER_ASSERT(reporter, b.capacity() == 0);
373 }
374}
375
378 a.push_back(1);
379 REPORTER_ASSERT(reporter, !a.empty());
380 REPORTER_ASSERT(reporter, a.size() == 1);
381 REPORTER_ASSERT(reporter, a[0] == 1);
382
383 a = static_cast<decltype(a)&>(a);
384 REPORTER_ASSERT(reporter, !a.empty());
385 REPORTER_ASSERT(reporter, a.size() == 1);
386 REPORTER_ASSERT(reporter, a[0] == 1);
387}
388
389template <typename Array> static void test_array_reserve(skiatest::Reporter* reporter,
390 Array* array, int reserveCount) {
391 SkRandom random;
392 REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
393 array->push_back();
394 REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
395 array->pop_back();
396 REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
397 while (array->size() < reserveCount) {
398 // Two steps forward, one step back
399 if (random.nextULessThan(3) < 2) {
400 array->push_back();
401 } else if (array->size() > 0) {
402 array->pop_back();
403 }
404 REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
405 }
406}
407
408template<typename Array> static void test_reserve(skiatest::Reporter* reporter) {
409 // Test that our allocated space stays >= to the reserve count until the array is filled to
410 // the reserve count
411 for (int reserveCount : {1, 2, 10, 100}) {
412 // Test setting reserve in constructor.
413 Array array1(reserveCount);
414 test_array_reserve(reporter, &array1, reserveCount);
415
416 // Test setting reserve after constructor.
417 Array array2;
418 array2.reserve(reserveCount);
419 test_array_reserve(reporter, &array2, reserveCount);
420
421 // Test increasing reserve after constructor.
422 Array array3(reserveCount/2);
423 array3.reserve(reserveCount);
424 test_array_reserve(reporter, &array3, reserveCount);
425
426 // Test setting reserve on non-empty array.
427 Array array4;
428 array4.push_back_n(reserveCount);
429 array4.reserve(2 * reserveCount);
430 array4.pop_back_n(reserveCount);
431 test_array_reserve(reporter, &array4, 2 * reserveCount);
432 }
433}
434
435template <typename T>
437 T a;
438 a.push_back(12345);
439 for (int x=0; x<50; ++x) {
440 a.push_back(a.front());
441 }
442 for (int x=0; x<50; ++x) {
443 a.push_back(a.back());
444 }
445
446 REPORTER_ASSERT(reporter, a.size() == 101);
447 REPORTER_ASSERT(reporter, std::count(a.begin(), a.end(), 12345) == a.size());
448}
449
451 EmplaceStruct(int v) : fValue(v) {}
453};
454
455template <typename T>
457 T a;
458 a.emplace_back(12345);
459 for (int x=0; x<50; ++x) {
460 a.emplace_back(a.front().fValue);
461 }
462 for (int x=0; x<50; ++x) {
463 a.emplace_back(a.back().fValue);
464 }
465
466 REPORTER_ASSERT(reporter, a.size() == 101);
467 REPORTER_ASSERT(reporter, std::all_of(a.begin(), a.end(), [](const EmplaceStruct& s) {
468 return s.fValue == 12345;
469 }));
470}
471
473 // ints are POD types and can work with either MEM_MOVE=true or false.
474 TestTSet_basic<int, true>(reporter);
475 TestTSet_basic<int, false>(reporter);
476
477 // TestClass has a vtable and can only work with MEM_MOVE=false.
478 TestTSet_basic<TestClass, false>(reporter);
479
482
484
486
487 test_reserve<TArray<int>>(reporter);
488 test_reserve<STArray<1, int>>(reporter);
489 test_reserve<STArray<2, int>>(reporter);
490 test_reserve<STArray<16, int>>(reporter);
491
492 test_reserve<TArray<TestClass>>(reporter);
493 test_reserve<STArray<1, TestClass>>(reporter);
494 test_reserve<STArray<2, TestClass>>(reporter);
495 test_reserve<STArray<16, TestClass>>(reporter);
496
497 test_construction<TArray<int>>(reporter);
498 test_construction<TArray<double>>(reporter);
499 test_construction<TArray<TestClass>>(reporter);
500 test_construction<STArray<1, int>>(reporter);
501 test_construction<STArray<5, char>>(reporter);
502 test_construction<STArray<7, TestClass>>(reporter);
503 test_construction<STArray<10, float>>(reporter);
504
505 test_inner_push<TArray<int>>(reporter);
506 test_inner_push<STArray<1, int>>(reporter);
507 test_inner_push<STArray<99, int>>(reporter);
508 test_inner_push<STArray<200, int>>(reporter);
509
510 test_inner_emplace<TArray<EmplaceStruct>>(reporter);
511 test_inner_emplace<STArray<1, EmplaceStruct>>(reporter);
512 test_inner_emplace<STArray<99, EmplaceStruct>>(reporter);
513 test_inner_emplace<STArray<200, EmplaceStruct>>(reporter);
514
515 test_skstarray_compatibility<STArray<1, int>, TArray<int>>(reporter);
516 test_skstarray_compatibility<STArray<5, char>, TArray<char>>(reporter);
517 test_skstarray_compatibility<STArray<10, float>, TArray<float>>(reporter);
518 test_skstarray_compatibility<TArray<int>, STArray<1, int>>(reporter);
519 test_skstarray_compatibility<TArray<char>, STArray<5, char>>(reporter);
520 test_skstarray_compatibility<TArray<float>, STArray<10, float>>(reporter);
521 test_skstarray_compatibility<STArray<10, uint8_t>, STArray<1, uint8_t>>(reporter);
522 test_skstarray_compatibility<STArray<1, long>, STArray<10, long>>(reporter);
523 test_skstarray_compatibility<STArray<3, double>, STArray<4, double>>(reporter);
524 test_skstarray_compatibility<STArray<2, short>, STArray<1, short>>(reporter);
525}
526
527DEF_TEST(TArray_BoundsCheck, reporter) {
528#if 0 // The v[0] fails
529 TArray<int> v;
530 v[0];
531#endif
532}
533
534#if defined(SK_SANITIZE_ADDRESS)
535
536template <typename Array>
537static void verify_poison(skiatest::Reporter* r, const Array& array) {
538 int allocated = array.size() * sizeof(typename Array::value_type);
539 int capacity = array.capacity() * sizeof(typename Array::value_type);
540 const std::byte* data = reinterpret_cast<const std::byte*>(array.data());
541
542 for (int index = 0; index < allocated; ++index) {
544 }
545
546 // ASAN user poisoning is conservative for ranges that are smaller than eight bytes long.
547 // We guarantee this alignment via SkContainerAllocator (because kCapacityMultiple == 8).
548 // https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm#mapping
549 REPORTER_ASSERT(r, capacity >= 8);
550 for (int index = allocated; index < capacity; ++index) {
552 }
553}
554
555template <typename Array>
556static void test_poison(skiatest::Reporter* reporter) {
557 Array array;
558
559 for (int index = 0; index < 20; ++index) {
560 array.emplace_back();
561 verify_poison(reporter, array);
562 }
563
564 for (int index = 0; index < 20; ++index) {
565 array.pop_back();
566 verify_poison(reporter, array);
567 }
568
569 for (int index = 0; index < 20; ++index) {
570 array.reserve(array.capacity() + 3);
571 verify_poison(reporter, array);
572 }
573
574 array.clear();
575 verify_poison(reporter, array);
576}
577
578DEF_TEST(TArray_ASAN_poisoning, reporter) {
579 test_poison<TArray<int>>(reporter);
580 test_poison<STArray<1, double>>(reporter);
581 test_poison<STArray<2, char>>(reporter);
582 test_poison<STArray<16, TestClass>>(reporter);
583}
584
585#endif
reporter
static int sk_asan_address_is_poisoned(void const volatile *addr)
Definition SkASAN.h:48
void test_unnecessary_alloc(skiatest::Reporter *reporter)
static void test_reserve(skiatest::Reporter *reporter)
static void test_array_reserve(skiatest::Reporter *reporter, Array *array, int reserveCount)
static void TestTSet_basic(skiatest::Reporter *reporter)
static void test_skstarray_compatibility(skiatest::Reporter *reporter)
static void test_inner_emplace(skiatest::Reporter *reporter)
static void test_swap(skiatest::Reporter *reporter, SkSpan< TArray< T > * > arrays, SkSpan< const int > sizes)
static void test_inner_push(skiatest::Reporter *reporter)
static void test_array_move(skiatest::Reporter *reporter, SkSpan< TArray< T > * > arrays, SkSpan< const int > sizes)
static void test_construction(skiatest::Reporter *reporter)
static void test_self_assignment(skiatest::Reporter *reporter)
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
uint32_t nextULessThan(uint32_t count)
Definition SkRandom.h:93
int size() const
Definition SkTArray.h:416
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
static bool b
struct MyStruct s
struct MyStruct a[10]
uint8_t value
double x
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
#define T
EmplaceStruct(int v)