diff options
Diffstat (limited to 'src/base/small_vector_unittest.cc')
-rw-r--r-- | src/base/small_vector_unittest.cc | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/src/base/small_vector_unittest.cc b/src/base/small_vector_unittest.cc new file mode 100644 index 000000000..e99d84ae3 --- /dev/null +++ b/src/base/small_vector_unittest.cc @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "perfetto/ext/base/small_vector.h" + +#include <tuple> +#include <utility> + +#include "test/gtest_and_gmock.h" + +namespace perfetto { +namespace base { +namespace { + +int g_instances = 0; + +struct Obj { + explicit Obj(size_t v = 0) : value(v) { + EXPECT_FALSE(constructed); + constructed = true; + g_instances++; + } + + ~Obj() { + EXPECT_TRUE(constructed); + g_instances--; + } + + // Move operators. + Obj(Obj&& other) noexcept { + g_instances++; + constructed = true; + moved_into = true; + value = other.value; + other.moved_from = true; + other.value = 0xffffffff - value; + } + + Obj& operator=(Obj&& other) noexcept { + this->~Obj(); + new (this) Obj(std::move(other)); + return *this; + } + + // Copy operators. + Obj(const Obj& other) { + other.copied_from = true; + g_instances++; + constructed = true; + copied_into = true; + value = other.value; + } + + Obj& operator=(const Obj& other) { + this->~Obj(); + new (this) Obj(other); + return *this; + } + + uintptr_t addr = reinterpret_cast<uintptr_t>(this); + bool constructed = false; + size_t value = 0; + bool moved_from = false; + mutable bool copied_from = false; + bool moved_into = false; + bool copied_into = false; +}; + +TEST(SmallVectorTest, StaySmall) { + SmallVector<Obj, 8> v; + EXPECT_EQ(g_instances, 0); + EXPECT_EQ(v.size(), 0u); + EXPECT_TRUE(v.empty()); + EXPECT_EQ(v.begin(), v.end()); + + for (size_t i = 1; i <= 8; i++) { + v.emplace_back(i); + EXPECT_EQ(g_instances, static_cast<int>(i)); + EXPECT_FALSE(v.empty()); + EXPECT_EQ(v.end(), v.begin() + i); + EXPECT_EQ(v.back().value, i); + EXPECT_EQ(v[static_cast<size_t>(i - 1)].value, i); + EXPECT_EQ(v[static_cast<size_t>(i - 1)].value, i); + } + + for (size_t i = 1; i <= 3; i++) { + v.pop_back(); + EXPECT_EQ(g_instances, 8 - static_cast<int>(i)); + } + + v.clear(); + EXPECT_EQ(g_instances, 0); +} + +TEST(SmallVectorTest, GrowOnHeap) { + SmallVector<Obj, 4> v; + for (size_t i = 0; i < 10; i++) { + v.emplace_back(i); + EXPECT_EQ(g_instances, static_cast<int>(i + 1)); + EXPECT_FALSE(v.empty()); + EXPECT_EQ(v.end(), v.begin() + i + 1); + EXPECT_EQ(v[i].value, i); + } + + // Do a second pass and check that the initial elements aren't corrupt. + for (size_t i = 0; i < 10; i++) { + EXPECT_EQ(v[i].value, i); + EXPECT_TRUE(v[i].constructed); + } + + // The first 4 elements must have been moved into because of the heap growth. + for (size_t i = 0; i < 4; i++) + EXPECT_TRUE(v[i].moved_into); + EXPECT_FALSE(v.back().moved_into); +} + +class SmallVectorTestP : public testing::TestWithParam<size_t> {}; + +TEST_P(SmallVectorTestP, MoveOperators) { + size_t num_elements = GetParam(); + static constexpr size_t kInlineCapacity = 4; + SmallVector<Obj, kInlineCapacity> v1; + for (size_t i = 0; i < num_elements; i++) + v1.emplace_back(i); + + SmallVector<Obj, kInlineCapacity> v2(std::move(v1)); + EXPECT_TRUE(v1.empty()); + EXPECT_EQ(v2.size(), num_elements); + + // Check that v2 (the moved into vector) is consistent. + for (size_t i = 0; i < num_elements; i++) { + EXPECT_EQ(v2[i].value, i); + EXPECT_TRUE(v2[i].constructed); + if (num_elements <= kInlineCapacity) { + EXPECT_TRUE(v2[i].moved_into); + } + } + + // Check that v1 (the moved-from object) is still usable. + EXPECT_EQ(v1.size(), 0u); + + for (size_t i = 0; i < num_elements; i++) { + v1.emplace_back(1000 + i); + EXPECT_EQ(v1.size(), i + 1); + } + + EXPECT_NE(v1.data(), v2.data()); + + for (size_t i = 0; i < num_elements; i++) { + EXPECT_EQ(v1[i].value, 1000 + i); + EXPECT_EQ(v2[i].value, i); + EXPECT_TRUE(v1[i].constructed); + EXPECT_FALSE(v1[i].moved_from); + } + + // Now swap again using the move-assignment. + + v1 = std::move(v2); + EXPECT_EQ(v1.size(), num_elements); + EXPECT_TRUE(v2.empty()); + for (size_t i = 0; i < num_elements; i++) { + EXPECT_EQ(v1[i].value, i); + EXPECT_TRUE(v1[i].constructed); + } + + { auto destroy = std::move(v1); } + + EXPECT_EQ(g_instances, 0); +} + +TEST_P(SmallVectorTestP, CopyOperators) { + size_t num_elements = GetParam(); + static constexpr size_t kInlineCapacity = 4; + SmallVector<Obj, kInlineCapacity> v1; + for (size_t i = 0; i < num_elements; i++) + v1.emplace_back(i); + + SmallVector<Obj, kInlineCapacity> v2(v1); + EXPECT_EQ(v1.size(), num_elements); + EXPECT_EQ(v2.size(), num_elements); + EXPECT_EQ(g_instances, static_cast<int>(num_elements * 2)); + + for (size_t i = 0; i < num_elements; i++) { + EXPECT_EQ(v1[i].value, i); + EXPECT_TRUE(v1[i].copied_from); + EXPECT_EQ(v2[i].value, i); + EXPECT_TRUE(v2[i].copied_into); + } + + // Now edit v2. + for (size_t i = 0; i < num_elements; i++) + v2[i].value = i + 100; + EXPECT_EQ(g_instances, static_cast<int>(num_elements * 2)); + + // Append some extra elements. + for (size_t i = 0; i < num_elements; i++) + v2.emplace_back(i + 200); + EXPECT_EQ(g_instances, static_cast<int>(num_elements * 3)); + + for (size_t i = 0; i < num_elements * 2; i++) { + if (i < num_elements) { + EXPECT_EQ(v1[i].value, i); + EXPECT_EQ(v2[i].value, 100 + i); + } else { + EXPECT_EQ(v2[i].value, 200 + i - num_elements); + } + } + + v2.clear(); + EXPECT_EQ(g_instances, static_cast<int>(num_elements)); +} + +INSTANTIATE_TEST_SUITE_P(SmallVectorTest, + SmallVectorTestP, + testing::Values(2, 4, 7, 512)); + +} // namespace +} // namespace base +} // namespace perfetto |