/* * * Copyright 2017 gRPC authors. * * 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 "src/core/lib/gprpp/inlined_vector.h" #include #include #include "src/core/lib/gprpp/memory.h" #include "test/core/util/test_config.h" namespace grpc_core { namespace testing { namespace { template static void FillVector(Vector* v, int len, int start = 0) { for (int i = 0; i < len; i++) { v->push_back(i + start); EXPECT_EQ(i + 1UL, v->size()); } EXPECT_EQ(static_cast(len), v->size()); EXPECT_LE(static_cast(len), v->capacity()); } } // namespace TEST(InlinedVectorTest, CreateAndIterate) { const int kNumElements = 9; InlinedVector v; EXPECT_TRUE(v.empty()); FillVector(&v, kNumElements); EXPECT_EQ(static_cast(kNumElements), v.size()); EXPECT_FALSE(v.empty()); for (int i = 0; i < kNumElements; ++i) { EXPECT_EQ(i, v[i]); EXPECT_EQ(i, &v[i] - &v[0]); // Ensure contiguous allocation. } } TEST(InlinedVectorTest, ValuesAreInlined) { const int kNumElements = 5; InlinedVector v; FillVector(&v, kNumElements); EXPECT_EQ(static_cast(kNumElements), v.size()); for (int i = 0; i < kNumElements; ++i) { EXPECT_EQ(i, v[i]); } } TEST(InlinedVectorTest, PushBackWithMove) { InlinedVector, 1> v; UniquePtr i = MakeUnique(3); v.push_back(std::move(i)); EXPECT_EQ(nullptr, i.get()); EXPECT_EQ(1UL, v.size()); EXPECT_EQ(3, *v[0]); } TEST(InlinedVectorTest, EmplaceBack) { InlinedVector, 1> v; v.emplace_back(New(3)); EXPECT_EQ(1UL, v.size()); EXPECT_EQ(3, *v[0]); } TEST(InlinedVectorTest, ClearAndRepopulate) { const int kNumElements = 10; InlinedVector v; EXPECT_EQ(0UL, v.size()); FillVector(&v, kNumElements); for (int i = 0; i < kNumElements; ++i) { EXPECT_EQ(i, v[i]); } v.clear(); EXPECT_EQ(0UL, v.size()); FillVector(&v, kNumElements, kNumElements); for (int i = 0; i < kNumElements; ++i) { EXPECT_EQ(kNumElements + i, v[i]); } } TEST(InlinedVectorTest, ConstIndexOperator) { constexpr int kNumElements = 10; InlinedVector v; EXPECT_EQ(0UL, v.size()); FillVector(&v, kNumElements); // The following lambda function is exceptionally allowed to use an anonymous // capture due to the erroneous behavior of the MSVC compiler, that refuses to // capture the kNumElements constexpr, something allowed by the standard. auto const_func = [&](const InlinedVector& v) { for (int i = 0; i < kNumElements; ++i) { EXPECT_EQ(i, v[i]); } }; const_func(v); } // the following constants and typedefs are used for copy/move // construction/assignment const size_t kInlinedLength = 8; typedef InlinedVector IntVec8; const size_t kInlinedFillSize = kInlinedLength - 1; const size_t kAllocatedFillSize = kInlinedLength + 1; TEST(InlinedVectorTest, CopyConstructorInlined) { IntVec8 original; FillVector(&original, kInlinedFillSize); IntVec8 copy_constructed(original); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], copy_constructed[i]); } } TEST(InlinedVectorTest, CopyConstructorAllocated) { IntVec8 original; FillVector(&original, kAllocatedFillSize); IntVec8 copy_constructed(original); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], copy_constructed[i]); } } TEST(InlinedVectorTest, CopyAssignementInlinedInlined) { IntVec8 original; FillVector(&original, kInlinedFillSize); IntVec8 copy_assigned; FillVector(©_assigned, kInlinedFillSize, 99); copy_assigned = original; for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], copy_assigned[i]); } } TEST(InlinedVectorTest, CopyAssignementInlinedAllocated) { IntVec8 original; FillVector(&original, kInlinedFillSize); IntVec8 copy_assigned; FillVector(©_assigned, kAllocatedFillSize, 99); copy_assigned = original; for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], copy_assigned[i]); } } TEST(InlinedVectorTest, CopyAssignementAllocatedInlined) { IntVec8 original; FillVector(&original, kAllocatedFillSize); IntVec8 copy_assigned; FillVector(©_assigned, kInlinedFillSize, 99); copy_assigned = original; for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], copy_assigned[i]); } } TEST(InlinedVectorTest, CopyAssignementAllocatedAllocated) { IntVec8 original; FillVector(&original, kAllocatedFillSize); IntVec8 copy_assigned; FillVector(©_assigned, kAllocatedFillSize, 99); copy_assigned = original; for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], copy_assigned[i]); } } TEST(InlinedVectorTest, MoveConstructorInlined) { IntVec8 original; FillVector(&original, kInlinedFillSize); IntVec8 tmp(original); auto* old_data = tmp.data(); IntVec8 move_constructed(std::move(tmp)); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], move_constructed[i]); } // original data was inlined so it should have been copied, not moved. EXPECT_NE(move_constructed.data(), old_data); } TEST(InlinedVectorTest, MoveConstructorAllocated) { IntVec8 original; FillVector(&original, kAllocatedFillSize); IntVec8 tmp(original); auto* old_data = tmp.data(); IntVec8 move_constructed(std::move(tmp)); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], move_constructed[i]); } // original data was allocated, so it should been moved, not copied EXPECT_EQ(move_constructed.data(), old_data); } TEST(InlinedVectorTest, MoveAssignmentInlinedInlined) { IntVec8 original; FillVector(&original, kInlinedFillSize); IntVec8 move_assigned; FillVector(&move_assigned, kInlinedFillSize, 99); // Add dummy elements IntVec8 tmp(original); auto* old_data = tmp.data(); move_assigned = std::move(tmp); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], move_assigned[i]); } // original data was inlined so it should have been copied, not moved. EXPECT_NE(move_assigned.data(), old_data); } TEST(InlinedVectorTest, MoveAssignmentInlinedAllocated) { IntVec8 original; FillVector(&original, kInlinedFillSize); IntVec8 move_assigned; FillVector(&move_assigned, kAllocatedFillSize, 99); // Add dummy elements IntVec8 tmp(original); auto* old_data = tmp.data(); move_assigned = std::move(tmp); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], move_assigned[i]); } // original data was inlined so it should have been copied, not moved. EXPECT_NE(move_assigned.data(), old_data); } TEST(InlinedVectorTest, MoveAssignmentAllocatedInlined) { IntVec8 original; FillVector(&original, kAllocatedFillSize); IntVec8 move_assigned; FillVector(&move_assigned, kInlinedFillSize, 99); // Add dummy elements IntVec8 tmp(original); auto* old_data = tmp.data(); move_assigned = std::move(tmp); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], move_assigned[i]); } // original data was allocated so it should have been moved, not copied. EXPECT_EQ(move_assigned.data(), old_data); } TEST(InlinedVectorTest, MoveAssignmentAllocatedAllocated) { IntVec8 original; FillVector(&original, kAllocatedFillSize); IntVec8 move_assigned; FillVector(&move_assigned, kAllocatedFillSize, 99); // Add dummy elements IntVec8 tmp(original); auto* old_data = tmp.data(); move_assigned = std::move(tmp); for (size_t i = 0; i < original.size(); ++i) { EXPECT_EQ(original[i], move_assigned[i]); } // original data was allocated so it should have been moved, not copied. EXPECT_EQ(move_assigned.data(), old_data); } // A copyable and movable value class, used to test that elements' copy // and move methods are called correctly. class Value { public: explicit Value(int v) : value_(MakeUnique(v)) {} // copyable Value(const Value& v) { value_ = MakeUnique(*v.value_); copied_ = true; } Value& operator=(const Value& v) { value_ = MakeUnique(*v.value_); copied_ = true; return *this; } // movable Value(Value&& v) { value_ = std::move(v.value_); moved_ = true; } Value& operator=(Value&& v) { value_ = std::move(v.value_); moved_ = true; return *this; } const UniquePtr& value() const { return value_; } bool copied() const { return copied_; } bool moved() const { return moved_; } private: UniquePtr value_; bool copied_ = false; bool moved_ = false; }; TEST(InlinedVectorTest, CopyConstructorCopiesElementsInlined) { InlinedVector v1; v1.emplace_back(3); InlinedVector v2(v1); EXPECT_EQ(v2.size(), 1UL); EXPECT_EQ(*v2[0].value(), 3); // Addresses should differ. EXPECT_NE(v1[0].value().get(), v2[0].value().get()); EXPECT_TRUE(v2[0].copied()); } TEST(InlinedVectorTest, CopyConstructorCopiesElementsAllocated) { InlinedVector v1; v1.reserve(2); v1.emplace_back(3); v1.emplace_back(5); InlinedVector v2(v1); EXPECT_EQ(v2.size(), 2UL); EXPECT_EQ(*v2[0].value(), 3); EXPECT_EQ(*v2[1].value(), 5); // Addresses should differ. EXPECT_NE(v1[0].value().get(), v2[0].value().get()); EXPECT_NE(v1[1].value().get(), v2[1].value().get()); EXPECT_TRUE(v2[0].copied()); EXPECT_TRUE(v2[1].copied()); } TEST(InlinedVectorTest, CopyAssignmentCopiesElementsInlined) { InlinedVector v1; v1.emplace_back(3); InlinedVector v2; EXPECT_EQ(v2.size(), 0UL); v2 = v1; EXPECT_EQ(v2.size(), 1UL); EXPECT_EQ(*v2[0].value(), 3); // Addresses should differ. EXPECT_NE(v1[0].value().get(), v2[0].value().get()); EXPECT_TRUE(v2[0].copied()); } TEST(InlinedVectorTest, CopyAssignmentCopiesElementsAllocated) { InlinedVector v1; v1.reserve(2); v1.emplace_back(3); v1.emplace_back(5); InlinedVector v2; EXPECT_EQ(v2.size(), 0UL); v2 = v1; EXPECT_EQ(v2.size(), 2UL); EXPECT_EQ(*v2[0].value(), 3); EXPECT_EQ(*v2[1].value(), 5); // Addresses should differ. EXPECT_NE(v1[0].value().get(), v2[0].value().get()); EXPECT_NE(v1[1].value().get(), v2[1].value().get()); EXPECT_TRUE(v2[0].copied()); EXPECT_TRUE(v2[1].copied()); } TEST(InlinedVectorTest, MoveConstructorMovesElementsInlined) { InlinedVector v1; v1.emplace_back(3); int* addr = v1[0].value().get(); InlinedVector v2(std::move(v1)); EXPECT_EQ(v2.size(), 1UL); EXPECT_EQ(*v2[0].value(), 3); EXPECT_EQ(addr, v2[0].value().get()); EXPECT_TRUE(v2[0].moved()); } TEST(InlinedVectorTest, MoveConstructorMovesElementsAllocated) { InlinedVector v1; v1.reserve(2); v1.emplace_back(3); v1.emplace_back(5); int* addr1 = v1[0].value().get(); int* addr2 = v1[1].value().get(); Value* data1 = v1.data(); InlinedVector v2(std::move(v1)); EXPECT_EQ(v2.size(), 2UL); EXPECT_EQ(*v2[0].value(), 3); EXPECT_EQ(*v2[1].value(), 5); EXPECT_EQ(addr1, v2[0].value().get()); EXPECT_EQ(addr2, v2[1].value().get()); // In this case, elements won't be moved, because we have just stolen // the underlying storage. EXPECT_EQ(data1, v2.data()); } TEST(InlinedVectorTest, MoveAssignmentMovesElementsInlined) { InlinedVector v1; v1.emplace_back(3); int* addr = v1[0].value().get(); InlinedVector v2; EXPECT_EQ(v2.size(), 0UL); v2 = std::move(v1); EXPECT_EQ(v2.size(), 1UL); EXPECT_EQ(*v2[0].value(), 3); EXPECT_EQ(addr, v2[0].value().get()); EXPECT_TRUE(v2[0].moved()); } TEST(InlinedVectorTest, MoveAssignmentMovesElementsAllocated) { InlinedVector v1; v1.reserve(2); v1.emplace_back(3); v1.emplace_back(5); int* addr1 = v1[0].value().get(); int* addr2 = v1[1].value().get(); Value* data1 = v1.data(); InlinedVector v2; EXPECT_EQ(v2.size(), 0UL); v2 = std::move(v1); EXPECT_EQ(v2.size(), 2UL); EXPECT_EQ(*v2[0].value(), 3); EXPECT_EQ(*v2[1].value(), 5); EXPECT_EQ(addr1, v2[0].value().get()); EXPECT_EQ(addr2, v2[1].value().get()); // In this case, elements won't be moved, because we have just stolen // the underlying storage. EXPECT_EQ(data1, v2.data()); } TEST(InlinedVectorTest, PopBackInlined) { InlinedVector, 2> v; // Add two elements, pop one out v.push_back(MakeUnique(3)); EXPECT_EQ(1UL, v.size()); EXPECT_EQ(3, *v[0]); v.push_back(MakeUnique(5)); EXPECT_EQ(2UL, v.size()); EXPECT_EQ(5, *v[1]); v.pop_back(); EXPECT_EQ(1UL, v.size()); } TEST(InlinedVectorTest, PopBackAllocated) { const int kInlinedSize = 2; InlinedVector, kInlinedSize> v; // Add elements to ensure allocated backing. for (size_t i = 0; i < kInlinedSize + 1; ++i) { v.push_back(MakeUnique(3)); EXPECT_EQ(i + 1, v.size()); } size_t sz = v.size(); v.pop_back(); EXPECT_EQ(sz - 1, v.size()); } } // namespace testing } // namespace grpc_core int main(int argc, char** argv) { grpc::testing::TestEnvironment env(argc, argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }