// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cc/quads/list_container.h" #include #include "cc/quads/draw_quad.h" #include "cc/quads/largest_draw_quad.h" #include "cc/quads/render_pass_draw_quad.h" #include "cc/quads/shared_quad_state.h" #include "cc/quads/stream_video_draw_quad.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace cc { namespace { int kMagicNumberToUseForDrawQuadOne = 42; int kMagicNumberToUseForDrawQuadTwo = 314; bool isConstSharedQuadStatePointer(const SharedQuadState* ptr) { return true; } bool isConstSharedQuadStatePointer(SharedQuadState* ptr) { return false; } class SimpleDrawQuad : public DrawQuad { public: ~SimpleDrawQuad() override {} void IterateResources(const ResourceIteratorCallback& callback) override {} void set_value(int val) { value = val; } int get_value() { return value; } void ExtendValue(base::trace_event::TracedValue* value) const override {} private: int value; }; class SimpleDrawQuadConstructMagicNumberOne : public SimpleDrawQuad { public: SimpleDrawQuadConstructMagicNumberOne() : SimpleDrawQuad() { set_value(kMagicNumberToUseForDrawQuadOne); } }; class SimpleDrawQuadConstructMagicNumberTwo : public SimpleDrawQuad { public: SimpleDrawQuadConstructMagicNumberTwo() : SimpleDrawQuad() { set_value(kMagicNumberToUseForDrawQuadTwo); } }; class MockDrawQuad : public SimpleDrawQuadConstructMagicNumberOne { public: ~MockDrawQuad() override { Destruct(); } MOCK_METHOD0(Destruct, void()); }; class MockDrawQuadSubclass : public MockDrawQuad { public: MockDrawQuadSubclass() { set_value(kMagicNumberToUseForDrawQuadTwo); } }; const size_t kLargestQuadSize = std::max(LargestDrawQuadSize(), sizeof(MockDrawQuadSubclass)); TEST(ListContainerTest, ConstructorCalledInAllocateAndConstruct) { ListContainer list(kLargestQuadSize); size_t size = 2; SimpleDrawQuadConstructMagicNumberOne* dq_1 = list.AllocateAndConstruct(); SimpleDrawQuadConstructMagicNumberTwo* dq_2 = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(dq_1, list.front()); EXPECT_EQ(dq_2, list.back()); EXPECT_EQ(kMagicNumberToUseForDrawQuadOne, dq_1->get_value()); EXPECT_EQ(kMagicNumberToUseForDrawQuadTwo, dq_2->get_value()); } TEST(ListContainerTest, DestructorCalled) { ListContainer list(kLargestQuadSize); size_t size = 1; MockDrawQuad* dq_1 = list.AllocateAndConstruct(); EXPECT_CALL(*dq_1, Destruct()); EXPECT_EQ(size, list.size()); EXPECT_EQ(dq_1, list.front()); } TEST(ListContainerTest, DestructorCalledOnceWhenClear) { ListContainer list(kLargestQuadSize); size_t size = 1; MockDrawQuad* dq_1 = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(dq_1, list.front()); // Make sure destructor is called once during clear, and won't be called // again. testing::MockFunction separator; { testing::InSequence s; EXPECT_CALL(*dq_1, Destruct()); EXPECT_CALL(separator, Call()); EXPECT_CALL(*dq_1, Destruct()).Times(0); } list.clear(); separator.Call(); } TEST(ListContainerTest, ReplaceExistingElement) { ListContainer list(kLargestQuadSize); size_t size = 1; MockDrawQuad* dq_1 = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(dq_1, list.front()); // Make sure destructor is called once during clear, and won't be called // again. testing::MockFunction separator; { testing::InSequence s; EXPECT_CALL(*dq_1, Destruct()); EXPECT_CALL(separator, Call()); EXPECT_CALL(*dq_1, Destruct()).Times(0); } list.ReplaceExistingElement(list.begin()); EXPECT_EQ(kMagicNumberToUseForDrawQuadTwo, dq_1->get_value()); separator.Call(); EXPECT_CALL(*dq_1, Destruct()); list.clear(); } TEST(ListContainerTest, DestructorCalledOnceWhenErase) { ListContainer list(kLargestQuadSize); size_t size = 1; MockDrawQuad* dq_1 = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(dq_1, list.front()); // Make sure destructor is called once during clear, and won't be called // again. testing::MockFunction separator; { testing::InSequence s; EXPECT_CALL(*dq_1, Destruct()); EXPECT_CALL(separator, Call()); EXPECT_CALL(*dq_1, Destruct()).Times(0); } list.EraseAndInvalidateAllPointers(list.begin()); separator.Call(); } TEST(ListContainerTest, SimpleIndexAccessSharedQuadState) { ListContainer list; size_t size = 3; SharedQuadState* sqs_1 = list.AllocateAndConstruct(); SharedQuadState* sqs_2 = list.AllocateAndConstruct(); SharedQuadState* sqs_3 = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(sqs_1, list.front()); EXPECT_EQ(sqs_3, list.back()); EXPECT_EQ(list.front(), list.ElementAt(0)); EXPECT_EQ(sqs_2, list.ElementAt(1)); EXPECT_EQ(list.back(), list.ElementAt(2)); } TEST(ListContainerTest, SimpleInsertionSharedQuadState) { ListContainer list; size_t size = 3; SharedQuadState* sqs_1 = list.AllocateAndConstruct(); list.AllocateAndConstruct(); SharedQuadState* sqs_3 = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(sqs_1, list.front()); EXPECT_EQ(sqs_3, list.back()); } TEST(ListContainerTest, SimpleInsertionAndClearSharedQuadState) { ListContainer list; EXPECT_TRUE(list.empty()); EXPECT_EQ(0u, list.size()); size_t size = 3; SharedQuadState* sqs_1 = list.AllocateAndConstruct(); list.AllocateAndConstruct(); SharedQuadState* sqs_3 = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(sqs_1, list.front()); EXPECT_EQ(sqs_3, list.back()); EXPECT_FALSE(list.empty()); list.clear(); EXPECT_TRUE(list.empty()); EXPECT_EQ(0u, list.size()); } TEST(ListContainerTest, SimpleInsertionClearAndInsertAgainSharedQuadState) { ListContainer list; EXPECT_TRUE(list.empty()); EXPECT_EQ(0u, list.size()); size_t size = 2; SharedQuadState* sqs_front = list.AllocateAndConstruct(); SharedQuadState* sqs_back = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(sqs_front, list.front()); EXPECT_EQ(sqs_back, list.back()); EXPECT_FALSE(list.empty()); list.clear(); EXPECT_TRUE(list.empty()); EXPECT_EQ(0u, list.size()); size = 3; sqs_front = list.AllocateAndConstruct(); list.AllocateAndConstruct(); sqs_back = list.AllocateAndConstruct(); EXPECT_EQ(size, list.size()); EXPECT_EQ(sqs_front, list.front()); EXPECT_EQ(sqs_back, list.back()); EXPECT_FALSE(list.empty()); } // This test is used to test when there is more than one allocation needed // for, ListContainer can still perform like normal vector. TEST(ListContainerTest, SimpleInsertionTriggerMoreThanOneAllocationSharedQuadState) { ListContainer list(sizeof(SharedQuadState), 2); std::vector sqs_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { sqs_list.push_back(list.AllocateAndConstruct()); } EXPECT_EQ(size, list.size()); ListContainer::Iterator iter = list.begin(); for (std::vector::const_iterator sqs_iter = sqs_list.begin(); sqs_iter != sqs_list.end(); ++sqs_iter) { EXPECT_EQ(*sqs_iter, *iter); ++iter; } } TEST(ListContainerTest, CorrectAllocationSizeForMoreThanOneAllocationSharedQuadState) { // Constructor sets the allocation size to 2. Every time ListContainer needs // to allocate again, it doubles allocation size. In this test, 10 elements is // needed, thus ListContainerShould allocate spaces 2, 4 and 8 elements. ListContainer list(sizeof(SharedQuadState), 2); std::vector sqs_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { // Before asking for a new element, space available without another // allocation follows. switch (i) { case 2: case 6: EXPECT_EQ(0u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 1: case 5: EXPECT_EQ(1u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 0: case 4: EXPECT_EQ(2u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 3: EXPECT_EQ(3u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 9: EXPECT_EQ(5u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 8: EXPECT_EQ(6u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 7: EXPECT_EQ(7u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; default: break; } sqs_list.push_back(list.AllocateAndConstruct()); // After asking for a new element, space available without another // allocation follows. switch (i) { case 1: case 5: EXPECT_EQ(0u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 0: case 4: EXPECT_EQ(1u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 3: EXPECT_EQ(2u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 2: EXPECT_EQ(3u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 9: EXPECT_EQ(4u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 8: EXPECT_EQ(5u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 7: EXPECT_EQ(6u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; case 6: EXPECT_EQ(7u, list.AvailableSizeWithoutAnotherAllocationForTesting()); break; default: break; } } EXPECT_EQ(size, list.size()); ListContainer::Iterator iter = list.begin(); for (std::vector::const_iterator sqs_iter = sqs_list.begin(); sqs_iter != sqs_list.end(); ++sqs_iter) { EXPECT_EQ(*sqs_iter, *iter); ++iter; } } TEST(ListContainerTest, SimpleIterationSharedQuadState) { ListContainer list; std::vector sqs_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { sqs_list.push_back(list.AllocateAndConstruct()); } EXPECT_EQ(size, list.size()); size_t num_iters_in_list = 0; { std::vector::const_iterator sqs_iter = sqs_list.begin(); for (ListContainer::Iterator iter = list.begin(); iter != list.end(); ++iter) { EXPECT_EQ(*sqs_iter, *iter); ++num_iters_in_list; ++sqs_iter; } } size_t num_iters_in_vector = 0; { ListContainer::Iterator iter = list.begin(); for (std::vector::const_iterator sqs_iter = sqs_list.begin(); sqs_iter != sqs_list.end(); ++sqs_iter) { EXPECT_EQ(*sqs_iter, *iter); ++num_iters_in_vector; ++iter; } } EXPECT_EQ(num_iters_in_vector, num_iters_in_list); } TEST(ListContainerTest, SimpleConstIteratorIterationSharedQuadState) { ListContainer list; std::vector sqs_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { sqs_list.push_back(list.AllocateAndConstruct()); } EXPECT_EQ(size, list.size()); { std::vector::const_iterator sqs_iter = sqs_list.begin(); for (ListContainer::ConstIterator iter = list.begin(); iter != list.end(); ++iter) { EXPECT_TRUE(isConstSharedQuadStatePointer(*iter)); EXPECT_EQ(*sqs_iter, *iter); ++sqs_iter; } } { std::vector::const_iterator sqs_iter = sqs_list.begin(); for (ListContainer::Iterator iter = list.begin(); iter != list.end(); ++iter) { EXPECT_FALSE(isConstSharedQuadStatePointer(*iter)); EXPECT_EQ(*sqs_iter, *iter); ++sqs_iter; } } { ListContainer::ConstIterator iter = list.begin(); for (std::vector::const_iterator sqs_iter = sqs_list.begin(); sqs_iter != sqs_list.end(); ++sqs_iter) { EXPECT_EQ(*sqs_iter, *iter); ++iter; } } } TEST(ListContainerTest, SimpleReverseInsertionSharedQuadState) { ListContainer list; std::vector sqs_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { sqs_list.push_back(list.AllocateAndConstruct()); } EXPECT_EQ(size, list.size()); { std::vector::const_reverse_iterator sqs_iter = sqs_list.rbegin(); for (ListContainer::ReverseIterator iter = list.rbegin(); iter != list.rend(); ++iter) { EXPECT_EQ(*sqs_iter, *iter); ++sqs_iter; } } { ListContainer::ReverseIterator iter = list.rbegin(); for (std::vector::reverse_iterator sqs_iter = sqs_list.rbegin(); sqs_iter != sqs_list.rend(); ++sqs_iter) { EXPECT_EQ(*sqs_iter, *iter); ++iter; } } } TEST(ListContainerTest, SimpleDeletion) { ListContainer list(kLargestQuadSize); std::vector sdq_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { sdq_list.push_back(list.AllocateAndConstruct()); sdq_list.back()->set_value(i); } EXPECT_EQ(size, list.size()); list.EraseAndInvalidateAllPointers(list.begin()); --size; EXPECT_EQ(size, list.size()); int i = 1; for (ListContainer::Iterator iter = list.begin(); iter != list.end(); ++iter) { EXPECT_EQ(i, static_cast(*iter)->get_value()); ++i; } } TEST(ListContainerTest, DeletionAllInAllocation) { const size_t kReserve = 10; ListContainer list(kLargestQuadSize, kReserve); std::vector sdq_list; // Add enough quads to cause another allocation. for (size_t i = 0; i < kReserve + 1; ++i) { sdq_list.push_back(list.AllocateAndConstruct()); sdq_list.back()->set_value(static_cast(i)); } EXPECT_EQ(kReserve + 1, list.size()); // Remove everything in the first allocation. for (size_t i = 0; i < kReserve; ++i) list.EraseAndInvalidateAllPointers(list.begin()); EXPECT_EQ(1u, list.size()); // The last quad is left. SimpleDrawQuad* quad = static_cast(*list.begin()); EXPECT_EQ(static_cast(kReserve), quad->get_value()); // Remove the quad from the 2nd allocation. list.EraseAndInvalidateAllPointers(list.begin()); EXPECT_EQ(0u, list.size()); } TEST(ListContainerTest, DeletionAllInAllocationReversed) { const size_t kReserve = 10; ListContainer list(kLargestQuadSize, kReserve); std::vector sdq_list; // Add enough quads to cause another allocation. for (size_t i = 0; i < kReserve + 1; ++i) { sdq_list.push_back(list.AllocateAndConstruct()); sdq_list.back()->set_value(static_cast(i)); } EXPECT_EQ(kReserve + 1, list.size()); // Remove everything in the 2nd allocation. auto it = list.begin(); for (size_t i = 0; i < kReserve; ++i) ++it; list.EraseAndInvalidateAllPointers(it); // The 2nd-last quad is next, and the rest of the quads exist. size_t i = kReserve - 1; for (auto it = list.rbegin(); it != list.rend(); ++it) { SimpleDrawQuad* quad = static_cast(*it); EXPECT_EQ(static_cast(i), quad->get_value()); --i; } // Can forward iterate too. i = 0; for (auto it = list.begin(); it != list.end(); ++it) { SimpleDrawQuad* quad = static_cast(*it); EXPECT_EQ(static_cast(i), quad->get_value()); ++i; } // Remove the last thing from the 1st allocation. it = list.begin(); for (size_t i = 0; i < kReserve - 1; ++i) ++it; list.EraseAndInvalidateAllPointers(it); // The 2nd-last quad is next, and the rest of the quads exist. i = kReserve - 2; for (auto it = list.rbegin(); it != list.rend(); ++it) { SimpleDrawQuad* quad = static_cast(*it); EXPECT_EQ(static_cast(i), quad->get_value()); --i; } // Can forward iterate too. i = 0; for (auto it = list.begin(); it != list.end(); ++it) { SimpleDrawQuad* quad = static_cast(*it); EXPECT_EQ(static_cast(i), quad->get_value()); ++i; } } TEST(ListContainerTest, SimpleIterationAndManipulation) { ListContainer list(kLargestQuadSize); std::vector sdq_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { SimpleDrawQuad* simple_dq = list.AllocateAndConstruct(); sdq_list.push_back(simple_dq); } EXPECT_EQ(size, list.size()); ListContainer::Iterator iter = list.begin(); for (int i = 0; i < 10; ++i) { static_cast(*iter)->set_value(i); ++iter; } int i = 0; for (std::vector::const_iterator sdq_iter = sdq_list.begin(); sdq_iter < sdq_list.end(); ++sdq_iter) { EXPECT_EQ(i, (*sdq_iter)->get_value()); ++i; } } TEST(ListContainerTest, SimpleManipulationWithIndexSimpleDrawQuad) { ListContainer list(kLargestQuadSize); std::vector dq_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { dq_list.push_back(list.AllocateAndConstruct()); } EXPECT_EQ(size, list.size()); for (size_t i = 0; i < size; ++i) { static_cast(list.ElementAt(i))->set_value(i); } int i = 0; for (std::vector::const_iterator dq_iter = dq_list.begin(); dq_iter != dq_list.end(); ++dq_iter, ++i) { EXPECT_EQ(i, (*dq_iter)->get_value()); } } TEST(ListContainerTest, SimpleManipulationWithIndexMoreThanOneAllocationSimpleDrawQuad) { ListContainer list(LargestDrawQuadSize(), 2); std::vector dq_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { dq_list.push_back(list.AllocateAndConstruct()); } EXPECT_EQ(size, list.size()); for (size_t i = 0; i < size; ++i) { static_cast(list.ElementAt(i))->set_value(i); } int i = 0; for (std::vector::const_iterator dq_iter = dq_list.begin(); dq_iter != dq_list.end(); ++dq_iter, ++i) { EXPECT_EQ(i, (*dq_iter)->get_value()); } } TEST(ListContainerTest, SimpleIterationAndReverseIterationWithIndexSharedQuadState) { ListContainer list; std::vector sqs_list; size_t size = 10; for (size_t i = 0; i < size; ++i) { sqs_list.push_back(list.AllocateAndConstruct()); } EXPECT_EQ(size, list.size()); size_t i = 0; for (ListContainer::Iterator iter = list.begin(); iter != list.end(); ++iter) { EXPECT_EQ(i, iter.index()); ++i; } i = 0; for (ListContainer::ReverseIterator iter = list.rbegin(); iter != list.rend(); ++iter) { EXPECT_EQ(i, iter.index()); ++i; } } } // namespace } // namespace cc