blob: 0012014df8f9d1a3b32df59952ec543e3a655a29 [file] [log] [blame]
// Copyright 2015 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/base/sidecar_list_container.h"
#include <algorithm>
#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
const size_t kNumInitiallyReservedElements = 2;
const size_t kNumElementsForTest = 100;
class DestructionNotifier {
public:
DestructionNotifier() : flag_(nullptr) {}
explicit DestructionNotifier(bool* flag) { set_flag(flag); }
~DestructionNotifier() {
if (flag_)
*flag_ = true;
}
void set_flag(bool* flag) {
if (flag)
DCHECK(!*flag);
flag_ = flag;
}
private:
bool* flag_;
};
class TestElement {
public:
TestElement() {}
virtual ~TestElement() {}
void set_destruction_flag(bool* flag) { notifier_.set_flag(flag); }
private:
DestructionNotifier notifier_;
};
class DerivedTestElement : public TestElement {
public:
int additional_field = 0;
};
class TestSidecar {
public:
TestSidecar() {}
explicit TestSidecar(bool* destruction_flag) : notifier_(destruction_flag) {}
static void Destroy(void* sidecar) {
static_cast<TestSidecar*>(sidecar)->~TestSidecar();
}
protected:
virtual ~TestSidecar() {}
private:
DestructionNotifier notifier_;
};
class DerivedTestSidecar : public TestSidecar {
public:
DerivedTestSidecar() {}
explicit DerivedTestSidecar(bool* destruction_flag)
: TestSidecar(destruction_flag) {}
int additional_field = 0;
private:
~DerivedTestSidecar() override {}
};
class TestContainer : public SidecarListContainer<TestElement> {
public:
TestContainer()
: SidecarListContainer(
std::max(sizeof(TestElement), sizeof(DerivedTestElement)),
std::max(sizeof(TestSidecar), sizeof(DerivedTestSidecar)),
kNumInitiallyReservedElements,
&TestSidecar::Destroy) {}
};
TEST(SidecarListContainerTest, Destructor) {
bool element_destroyed = false;
bool sidecar_destroyed = false;
{
TestContainer container;
TestElement* element = container.AllocateAndConstruct<TestElement>();
TestSidecar* sidecar =
new (container.GetSidecar(element)) TestSidecar(&sidecar_destroyed);
element->set_destruction_flag(&element_destroyed);
// They shouldn't be destroyed yet. And they shouldn't overlap in memory.
ASSERT_FALSE(element_destroyed);
ASSERT_FALSE(sidecar_destroyed);
ASSERT_GE(reinterpret_cast<char*>(sidecar),
reinterpret_cast<char*>(element) + sizeof(TestElement));
}
// They should, however, be destroyed when going out of scope.
ASSERT_TRUE(element_destroyed);
ASSERT_TRUE(sidecar_destroyed);
}
TEST(SidecarListContainerTest, Clear) {
bool element_destroyed = false;
bool sidecar_destroyed = false;
TestContainer container;
TestElement* element = container.AllocateAndConstruct<TestElement>();
new (container.GetSidecar(element)) TestSidecar(&sidecar_destroyed);
element->set_destruction_flag(&element_destroyed);
// They shouldn't be destroyed yet.
ASSERT_FALSE(element_destroyed);
ASSERT_FALSE(sidecar_destroyed);
// They should, however, be destroyed after clearing.
container.clear();
EXPECT_TRUE(element_destroyed);
EXPECT_TRUE(sidecar_destroyed);
}
TEST(SidecarListContainerTest, DerivedTypes) {
bool element_destroyed = false;
bool sidecar_destroyed = false;
{
TestContainer container;
DerivedTestElement* element =
container.AllocateAndConstruct<DerivedTestElement>();
DerivedTestSidecar* sidecar = new (container.GetSidecar(element))
DerivedTestSidecar(&sidecar_destroyed);
element->set_destruction_flag(&element_destroyed);
element->additional_field = 12;
sidecar->additional_field = 13;
// They shouldn't be destroyed yet.
ASSERT_FALSE(element_destroyed);
ASSERT_FALSE(sidecar_destroyed);
}
// They should, however, be destroyed when going out of scope.
EXPECT_TRUE(element_destroyed);
EXPECT_TRUE(sidecar_destroyed);
}
TEST(SidecarListContainerTest, AddingAndRemovingElements) {
TestContainer container;
EXPECT_TRUE(container.empty());
EXPECT_EQ(0u, container.size());
EXPECT_EQ(container.end(), container.begin());
for (size_t i = 1; i <= kNumElementsForTest; i++) {
TestElement* element = container.AllocateAndConstruct<TestElement>();
new (container.GetSidecar(element)) TestSidecar();
ASSERT_FALSE(container.empty());
ASSERT_EQ(i, container.size());
ASSERT_NE(container.end(), container.begin());
}
size_t num_elements = 0;
for (const auto* element : container) {
(void)element;
num_elements++;
}
EXPECT_EQ(kNumElementsForTest, num_elements);
container.clear();
EXPECT_TRUE(container.empty());
EXPECT_EQ(0u, container.size());
EXPECT_EQ(container.end(), container.begin());
}
TEST(SidecarListContainerTest, RemoveLast) {
// We need only ensure that the sidecar is also destroyed on RemoveLast.
// The rest is logic already present in ListContainer.
bool sidecar_destroyed = false;
TestContainer container;
TestElement* element = container.AllocateAndConstruct<TestElement>();
new (container.GetSidecar(element)) TestSidecar(&sidecar_destroyed);
ASSERT_FALSE(sidecar_destroyed);
container.RemoveLast();
ASSERT_TRUE(sidecar_destroyed);
}
} // namespace
} // namespace cc