* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/private/base/SkASAN.h"
#include "include/private/base/SkTArray.h"
#include "src/base/SkFixedArray.h"
#include "src/base/SkRandom.h"
#include "tests/Test.h"
#include <array>
#include <cstdint>
#include <initializer_list>
#include <utility>
using namespace skia_private;
namespace {
class TestClass {
public:
TestClass() = default;
TestClass(const TestClass&) = default;
TestClass& operator=(const TestClass&) = default;
TestClass(int v) : value(v) {}
virtual ~TestClass() {}
bool operator==(const TestClass& c) const { return value == c.value; }
bool operator!=(const TestClass& c) const { return value != c.value; }
int value = 0;
};
}
template <typename ArrayType>
static void TestTSet_basic(skiatest::Reporter* reporter) {
using T = typename ArrayType::value_type;
ArrayType a;
REPORTER_ASSERT(reporter, a.empty());
REPORTER_ASSERT(reporter, a.size() == 0);
a.push_back() = T{0};
REPORTER_ASSERT(reporter, !a.empty());
REPORTER_ASSERT(reporter, a.size() == 1);
a.removeShuffle(0);
REPORTER_ASSERT(reporter, a.empty());
REPORTER_ASSERT(reporter, a.size() == 0);
a.push_back() = T{0};
a.push_back() = T{1};
a.removeShuffle(0);
REPORTER_ASSERT(reporter, !a.empty());
REPORTER_ASSERT(reporter, a.size() == 1);
REPORTER_ASSERT(reporter, a[0] == T{1});
T b[5] = {T{0}, T{1}, T{2}, T{3}, T{4}};
a.reset(b, std::size(b));
REPORTER_ASSERT(reporter, a.size() == std::size(b));
REPORTER_ASSERT(reporter, a[2] == T{2});
REPORTER_ASSERT(reporter, a[4] == T{4});
a.removeShuffle(4);
REPORTER_ASSERT(reporter, a.size() == std::size(b) - 1);
REPORTER_ASSERT(reporter, a[3] == T{3});
a.removeShuffle(1);
REPORTER_ASSERT(reporter, a.size() == std::size(b) - 2);
REPORTER_ASSERT(reporter, a[0] == T{0});
REPORTER_ASSERT(reporter, a[1] == T{3});
REPORTER_ASSERT(reporter, a[2] == T{2});
a.pop_back();
REPORTER_ASSERT(reporter, a.size() == std::size(b) - 3);
a.push_back_n(3, 5);
REPORTER_ASSERT(reporter, a.size() == std::size(b));
REPORTER_ASSERT(reporter, a[2] == T{5});
REPORTER_ASSERT(reporter, a[3] == T{5});
REPORTER_ASSERT(reporter, a[4] == T{5});
a.pop_back_n(4);
REPORTER_ASSERT(reporter, a.size() == std::size(b) - 4);
REPORTER_ASSERT(reporter, a[0] == 0);
}
template <typename T>
static void test_construction(skiatest::Reporter* reporter, bool hasMoveSemantics = true) {
using ValueType = typename T::value_type;
T arrayNoArgs;
REPORTER_ASSERT(reporter, arrayNoArgs.empty());
T arrayReserve(15);
REPORTER_ASSERT(reporter, arrayReserve.empty());
REPORTER_ASSERT(reporter, arrayReserve.capacity() >= 15 && arrayReserve.capacity() < 50);
T arrayInitial;
arrayInitial.push_back(ValueType{1});
arrayInitial.push_back(ValueType{2});
arrayInitial.push_back(ValueType{3});
T arrayCopy(arrayInitial);
REPORTER_ASSERT(reporter, arrayInitial.size() == 3);
REPORTER_ASSERT(reporter, arrayInitial[0] == ValueType{1});
REPORTER_ASSERT(reporter, arrayInitial[1] == ValueType{2});
REPORTER_ASSERT(reporter, arrayInitial[2] == ValueType{3});
REPORTER_ASSERT(reporter, arrayCopy.size() == 3);
REPORTER_ASSERT(reporter, arrayCopy[0] == ValueType{1});
REPORTER_ASSERT(reporter, arrayCopy[1] == ValueType{2});
REPORTER_ASSERT(reporter, arrayCopy[2] == ValueType{3});
T arrayMove(std::move(arrayInitial));
if (hasMoveSemantics) {
REPORTER_ASSERT(reporter, arrayInitial.empty());
}
REPORTER_ASSERT(reporter, arrayMove.size() == 3);
REPORTER_ASSERT(reporter, arrayMove[0] == ValueType{1});
REPORTER_ASSERT(reporter, arrayMove[1] == ValueType{2});
REPORTER_ASSERT(reporter, arrayMove[2] == ValueType{3});
typename T::value_type data[3] = { 7, 8, 9 };
T arrayPtrCount(data, 3);
REPORTER_ASSERT(reporter, arrayPtrCount.size() == 3);
REPORTER_ASSERT(reporter, arrayPtrCount[0] == ValueType{7});
REPORTER_ASSERT(reporter, arrayPtrCount[1] == ValueType{8});
REPORTER_ASSERT(reporter, arrayPtrCount[2] == ValueType{9});
T arrayInitializer{8, 6, 7, 5, 3, 0, 9};
REPORTER_ASSERT(reporter, arrayInitializer.size() == 7);
REPORTER_ASSERT(reporter, arrayInitializer[0] == ValueType{8});
REPORTER_ASSERT(reporter, arrayInitializer[1] == ValueType{6});
REPORTER_ASSERT(reporter, arrayInitializer[2] == ValueType{7});
REPORTER_ASSERT(reporter, arrayInitializer[3] == ValueType{5});
REPORTER_ASSERT(reporter, arrayInitializer[4] == ValueType{3});
REPORTER_ASSERT(reporter, arrayInitializer[5] == ValueType{0});
REPORTER_ASSERT(reporter, arrayInitializer[6] == ValueType{9});
}
template <typename T, typename U>
static void test_starray_compatibility(skiatest::Reporter* reporter) {
T tArray;
tArray.push_back(1);
tArray.push_back(2);
tArray.push_back(3);
T tArray2 = tArray;
U arrayCopy(tArray);
REPORTER_ASSERT(reporter, tArray.size() == 3);
REPORTER_ASSERT(reporter, tArray[0] == 1);
REPORTER_ASSERT(reporter, tArray[1] == 2);
REPORTER_ASSERT(reporter, tArray[2] == 3);
REPORTER_ASSERT(reporter, arrayCopy.size() == 3);
REPORTER_ASSERT(reporter, arrayCopy[0] == 1);
REPORTER_ASSERT(reporter, arrayCopy[1] == 2);
REPORTER_ASSERT(reporter, arrayCopy[2] == 3);
U arrayAssignment;
arrayAssignment = tArray;
REPORTER_ASSERT(reporter, tArray.size() == 3);
REPORTER_ASSERT(reporter, tArray[0] == 1);
REPORTER_ASSERT(reporter, tArray[1] == 2);
REPORTER_ASSERT(reporter, tArray[2] == 3);
REPORTER_ASSERT(reporter, arrayAssignment.size() == 3);
REPORTER_ASSERT(reporter, arrayAssignment[0] == 1);
REPORTER_ASSERT(reporter, arrayAssignment[1] == 2);
REPORTER_ASSERT(reporter, arrayAssignment[2] == 3);
U arrayMove(std::move(tArray));
REPORTER_ASSERT(reporter, tArray.empty());
REPORTER_ASSERT(reporter, arrayMove.size() == 3);
REPORTER_ASSERT(reporter, arrayMove[0] == 1);
REPORTER_ASSERT(reporter, arrayMove[1] == 2);
REPORTER_ASSERT(reporter, arrayMove[2] == 3);
U arrayMoveAssign;
arrayMoveAssign = std::move(tArray2);
REPORTER_ASSERT(reporter, tArray2.empty());
REPORTER_ASSERT(reporter, arrayMoveAssign.size() == 3);
REPORTER_ASSERT(reporter, arrayMoveAssign[0] == 1);
REPORTER_ASSERT(reporter, arrayMoveAssign[1] == 2);
REPORTER_ASSERT(reporter, arrayMoveAssign[2] == 3);
}
namespace {
struct MoveOnlyInt {
MoveOnlyInt(int i) : fInt(i) {}
MoveOnlyInt(MoveOnlyInt&& that) : fInt(that.fInt) {}
bool operator==(int i) const { return fInt == i; }
int fInt;
};
}
template <typename T> static void test_swap(skiatest::Reporter* reporter,
SkSpan<TArray<T>*> arrays,
SkSpan<const int> sizes) {
for (auto a : arrays) {
for (auto b : arrays) {
if (a == b) {
continue;
}
for (auto sizeA : sizes) {
for (auto sizeB : sizes) {
a->clear();
b->clear();
int curr = 0;
for (int i = 0; i < sizeA; i++) { a->push_back(curr++); }
for (int i = 0; i < sizeB; i++) { b->push_back(curr++); }
a->swap(*b);
REPORTER_ASSERT(reporter, b->size() == sizeA);
REPORTER_ASSERT(reporter, a->size() == sizeB);
curr = 0;
for (auto&& x : *b) { REPORTER_ASSERT(reporter, x == curr++); }
for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
a->swap(*a);
curr = sizeA;
for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
}}
}}
}
DEF_TEST(TArray_Swap, reporter) {
static constexpr int kSizes[] = {0, 1, 5, 10, 15, 20, 25};
TArray<int> arr;
STArray< 5, int> arr5;
STArray<10, int> arr10;
STArray<20, int> arr20;
TArray<int>* arrays[] = { &arr, &arr5, &arr10, &arr20 };
test_swap<int>(reporter, arrays, kSizes);
TArray<MoveOnlyInt> moi;
STArray< 5, MoveOnlyInt> moi5;
STArray<10, MoveOnlyInt> moi10;
STArray<20, MoveOnlyInt> moi20;
TArray<MoveOnlyInt>* arraysMoi[] = { &moi, &moi5, &moi10, &moi20 };
test_swap<MoveOnlyInt>(reporter, arraysMoi, kSizes);
}
template <typename T> static void test_array_move(skiatest::Reporter* reporter,
SkSpan<TArray<T>*> arrays,
SkSpan<const int> sizes) {
for (auto a : arrays) {
for (auto sizeA : sizes) {
a->clear();
for (int i = 0; i < sizeA; i++) { a->push_back(i); }
a->move_back(*a);
REPORTER_ASSERT(reporter, a->size() == sizeA);
for (int i = 0; i < sizeA; i++) {
REPORTER_ASSERT(reporter, (*a)[i] == i);
}
}
}
for (auto a : arrays) {
for (auto b : arrays) {
if (a == b) {
continue;
}
for (auto sizeA : sizes) {
for (auto sizeB : sizes) {
a->clear();
b->clear();
int curr = 0;
for (int i = 0; i < sizeA; i++) { a->push_back(curr++); }
for (int i = 0; i < sizeB; i++) { b->push_back(curr++); }
a->move_back(*b);
REPORTER_ASSERT(reporter, b->size() == 0);
REPORTER_ASSERT(reporter, a->size() == sizeA + sizeB);
curr = 0;
for (auto&& x : *a) { REPORTER_ASSERT(reporter, x == curr++); }
}}
}}
}
DEF_TEST(TArray_Move, reporter) {
static constexpr int kSizes[] = {0, 1, 5, 10, 15, 20, 25};
TArray<int> arr;
STArray< 5, int> arr5;
STArray<10, int> arr10;
STArray<20, int> arr20;
TArray<int>* arrays[] = { &arr, &arr5, &arr10, &arr20 };
test_array_move<int>(reporter, arrays, kSizes);
TArray<MoveOnlyInt> moi;
STArray< 5, MoveOnlyInt> moi5;
STArray<10, MoveOnlyInt> moi10;
STArray<20, MoveOnlyInt> moi20;
TArray<MoveOnlyInt>* arraysMoi[] = { &moi, &moi5, &moi10, &moi20 };
test_array_move<MoveOnlyInt>(reporter, arraysMoi, kSizes);
}
DEF_TEST(TArray_NoUnnecessaryAllocs, reporter) {
{
TArray<int> a;
REPORTER_ASSERT(reporter, a.capacity() == 0);
}
{
STArray<10, int> a;
REPORTER_ASSERT(reporter, a.capacity() == 10);
}
{
TArray<int> a(1);
REPORTER_ASSERT(reporter, a.capacity() >= 1);
}
{
TArray<int> a, b;
b = a;
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
{
STArray<10, int> a;
TArray<int> b;
b = a;
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
{
TArray<int> a;
TArray<int> b(a);
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
{
STArray<10, int> a;
TArray<int> b(a);
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
{
TArray<int> a;
TArray<int> b(std::move(a));
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
{
STArray<10, int> a;
TArray<int> b(std::move(a));
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
{
TArray<int> a;
TArray<int> b;
b = std::move(a);
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
{
STArray<10, int> a;
TArray<int> b;
b = std::move(a);
REPORTER_ASSERT(reporter, b.capacity() == 0);
}
}
template <typename ArrayType>
static void test_self_assignment(skiatest::Reporter* reporter) {
ArrayType a;
a.push_back(1);
REPORTER_ASSERT(reporter, !a.empty());
REPORTER_ASSERT(reporter, a.size() == 1);
REPORTER_ASSERT(reporter, a[0] == 1);
a = static_cast<ArrayType&>(a);
REPORTER_ASSERT(reporter, !a.empty());
REPORTER_ASSERT(reporter, a.size() == 1);
REPORTER_ASSERT(reporter, a[0] == 1);
}
DEF_TEST(TArray_SelfAssignment, reporter) {
test_self_assignment<TArray<int>>(reporter);
test_self_assignment<STArray<3, unsigned short>>(reporter);
}
DEF_TEST(FixedArray_SelfAssignment, reporter) {
test_self_assignment<FixedArray<1, int>>(reporter);
test_self_assignment<FixedArray<4, unsigned short>>(reporter);
}
template <typename ArrayType>
static void test_comparison(skiatest::Reporter* reporter) {
using T = typename ArrayType::value_type;
ArrayType a, b;
REPORTER_ASSERT(reporter, a == b);
REPORTER_ASSERT(reporter, !(a != b));
for (int x = 0; x < 10; ++x) {
a.push_back(T(x));
b.push_back(T(x));
REPORTER_ASSERT(reporter, a == b);
REPORTER_ASSERT(reporter, !(a != b));
}
for (int x = 0; x < 10; ++x) {
a.pop_back();
REPORTER_ASSERT(reporter, a != b);
REPORTER_ASSERT(reporter, b != a);
REPORTER_ASSERT(reporter, !(a == b));
REPORTER_ASSERT(reporter, !(b == a));
}
a = b;
for (int x = 0; x < 10; ++x) {
a[x] = T(x + 100);
REPORTER_ASSERT(reporter, a != b);
REPORTER_ASSERT(reporter, b != a);
REPORTER_ASSERT(reporter, !(a == b));
REPORTER_ASSERT(reporter, !(b == a));
a[x] = T(x);
}
}
DEF_TEST(TArray_Comparison, reporter) {
test_comparison<TArray<int>>(reporter);
test_comparison<TArray<double>>(reporter);
test_comparison<TArray<TestClass>>(reporter);
test_comparison<STArray<1, int>>(reporter);
test_comparison<STArray<5, char>>(reporter);
test_comparison<STArray<7, TestClass>>(reporter);
test_comparison<STArray<10, float>>(reporter);
}
DEF_TEST(FixedArray_Comparison, reporter) {
test_comparison<FixedArray<15, int>>(reporter);
test_comparison<FixedArray<20, char>>(reporter);
test_comparison<FixedArray<25, float>>(reporter);
}
template <typename Array> static void test_array_reserve(skiatest::Reporter* reporter,
Array* array, int reserveCount) {
SkRandom random;
REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
array->push_back();
REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
array->pop_back();
REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
while (array->size() < reserveCount) {
if (random.nextULessThan(3) < 2) {
array->push_back();
} else if (array->size() > 0) {
array->pop_back();
}
REPORTER_ASSERT(reporter, array->capacity() >= reserveCount);
}
}
template<typename Array> static void test_reserve(skiatest::Reporter* reporter) {
for (int reserveCount : {1, 2, 10, 100}) {
Array array1(reserveCount);
test_array_reserve(reporter, &array1, reserveCount);
Array array2;
array2.reserve(reserveCount);
test_array_reserve(reporter, &array2, reserveCount);
Array array3(reserveCount/2);
array3.reserve(reserveCount);
test_array_reserve(reporter, &array3, reserveCount);
Array array4;
array4.push_back_n(reserveCount);
array4.reserve(2 * reserveCount);
array4.pop_back_n(reserveCount);
test_array_reserve(reporter, &array4, 2 * reserveCount);
}
}
template <typename T>
static void test_inner_push(skiatest::Reporter* reporter) {
T a;
a.push_back(12345);
for (int x=0; x<50; ++x) {
a.push_back(a.front());
}
for (int x=0; x<50; ++x) {
a.push_back(a.back());
}
REPORTER_ASSERT(reporter, a.size() == 101);
REPORTER_ASSERT(reporter, std::count(a.begin(), a.end(), 12345) == a.size());
}
struct EmplaceStruct {
EmplaceStruct(int v) : fValue(v) {}
int fValue;
};
template <typename T>
static void test_inner_emplace(skiatest::Reporter* reporter) {
T a;
a.emplace_back(12345);
for (int x=0; x<50; ++x) {
a.emplace_back(a.front().fValue);
}
for (int x=0; x<50; ++x) {
a.emplace_back(a.back().fValue);
}
REPORTER_ASSERT(reporter, a.size() == 101);
REPORTER_ASSERT(reporter, std::all_of(a.begin(), a.end(), [](const EmplaceStruct& s) {
return s.fValue == 12345;
}));
}
DEF_TEST(TArray_Basic, reporter) {
TestTSet_basic<TArray<int, true>>(reporter);
TestTSet_basic<TArray<int, false>>(reporter);
TestTSet_basic<TArray<TestClass, false>>(reporter);
}
DEF_TEST(FixedArray_Basic, reporter) {
TestTSet_basic<FixedArray<5, char>>(reporter);
TestTSet_basic<FixedArray<7, int>>(reporter);
TestTSet_basic<FixedArray<100, double>>(reporter);
}
DEF_TEST(TArray_Reserve, reporter) {
test_reserve<TArray<int>>(reporter);
test_reserve<STArray<1, int>>(reporter);
test_reserve<STArray<2, int>>(reporter);
test_reserve<STArray<16, int>>(reporter);
test_reserve<TArray<TestClass>>(reporter);
test_reserve<STArray<1, TestClass>>(reporter);
test_reserve<STArray<2, TestClass>>(reporter);
test_reserve<STArray<16, TestClass>>(reporter);
}
DEF_TEST(TArray_Construction, reporter) {
test_construction<TArray<int>>(reporter);
test_construction<TArray<double>>(reporter);
test_construction<TArray<TestClass>>(reporter);
test_construction<STArray<1, int>>(reporter);
test_construction<STArray<5, char>>(reporter);
test_construction<STArray<7, TestClass>>(reporter);
test_construction<STArray<10, float>>(reporter);
}
DEF_TEST(FixedArray_Construction, reporter) {
test_construction<FixedArray<15, int>>(reporter, false);
test_construction<FixedArray<20, char>>(reporter, false);
test_construction<FixedArray<25, float>>(reporter, false);
}
DEF_TEST(TArray_InnerPush, reporter) {
test_inner_push<TArray<int>>(reporter);
test_inner_push<STArray<1, int>>(reporter);
test_inner_push<STArray<99, int>>(reporter);
test_inner_push<STArray<200, int>>(reporter);
}
DEF_TEST(FixedArray_InnerPush, reporter) {
test_inner_push<FixedArray<101, int>>(reporter);
test_inner_push<FixedArray<150, short>>(reporter);
test_inner_push<FixedArray<250, double>>(reporter);
}
DEF_TEST(TArray_InnerEmplace, reporter) {
test_inner_emplace<TArray<EmplaceStruct>>(reporter);
test_inner_emplace<STArray<1, EmplaceStruct>>(reporter);
test_inner_emplace<STArray<99, EmplaceStruct>>(reporter);
test_inner_emplace<STArray<200, EmplaceStruct>>(reporter);
}
DEF_TEST(TArray_STArrayCompatibility, reporter) {
test_starray_compatibility<STArray<1, int>, TArray<int>>(reporter);
test_starray_compatibility<STArray<5, char>, TArray<char>>(reporter);
test_starray_compatibility<STArray<10, float>, TArray<float>>(reporter);
test_starray_compatibility<TArray<int>, STArray<1, int>>(reporter);
test_starray_compatibility<TArray<char>, STArray<5, char>>(reporter);
test_starray_compatibility<TArray<float>, STArray<10, float>>(reporter);
test_starray_compatibility<STArray<10, uint8_t>, STArray<1, uint8_t>>(reporter);
test_starray_compatibility<STArray<1, long>, STArray<10, long>>(reporter);
test_starray_compatibility<STArray<3, double>, STArray<4, double>>(reporter);
test_starray_compatibility<STArray<2, short>, STArray<1, short>>(reporter);
}
DEF_TEST(TArray_BoundsCheck, reporter) {
#if 0
TArray<int> v;
v[0];
#endif
}
#if defined(SK_SANITIZE_ADDRESS)
template <typename Array>
static void verify_poison(skiatest::Reporter* r, const Array& array) {
int allocated = array.size() * sizeof(typename Array::value_type);
int capacity = array.capacity() * sizeof(typename Array::value_type);
const std::byte* data = reinterpret_cast<const std::byte*>(array.data());
for (int index = 0; index < allocated; ++index) {
REPORTER_ASSERT(r, !sk_asan_address_is_poisoned(data + index));
}
REPORTER_ASSERT(r, capacity >= 8);
for (int index = allocated; index < capacity; ++index) {
REPORTER_ASSERT(r, sk_asan_address_is_poisoned(data + index));
}
}
template <typename Array>
static void test_poison(skiatest::Reporter* reporter) {
Array array;
for (int index = 0; index < 20; ++index) {
array.emplace_back();
verify_poison(reporter, array);
}
for (int index = 0; index < 20; ++index) {
array.pop_back();
verify_poison(reporter, array);
}
for (int index = 0; index < 20; ++index) {
array.reserve(array.capacity() + 3);
verify_poison(reporter, array);
}
array.clear();
verify_poison(reporter, array);
}
DEF_TEST(TArray_ASANPoisoning, reporter) {
test_poison<TArray<int>>(reporter);
test_poison<STArray<1, double>>(reporter);
test_poison<STArray<2, char>>(reporter);
test_poison<STArray<16, TestClass>>(reporter);
}
#endif