// Copyright 2020 The PDFium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "fxjs/gc/gced_tree_node_mixin.h" #include #include "core/fxcrt/observed_ptr.h" #include "fxjs/gc/heap.h" #include "testing/fxgc_unittest.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/v8_test_environment.h" #include "v8/include/cppgc/allocation.h" #include "v8/include/cppgc/persistent.h" namespace { class ObservableGCedTreeNodeMixinForTest : public cppgc::GarbageCollected, public GCedTreeNodeMixin, public Observable { public: CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED; // GCedTreeNodeMixin: void Trace(cppgc::Visitor* visitor) const override { GCedTreeNodeMixin::Trace(visitor); } private: ObservableGCedTreeNodeMixinForTest() = default; }; } // namespace class GCedTreeNodeMixinUnitTest : public FXGCUnitTest { public: GCedTreeNodeMixinUnitTest() = default; ~GCedTreeNodeMixinUnitTest() override = default; // FXGCUnitTest: void TearDown() override { root_ = nullptr; // Can't (yet) outlive |FXGCUnitTest::heap_|. FXGCUnitTest::TearDown(); } ObservableGCedTreeNodeMixinForTest* root() const { return root_; } void CreateRoot() { root_ = CreateNode(); } ObservableGCedTreeNodeMixinForTest* CreateNode() { return cppgc::MakeGarbageCollected( heap()->GetAllocationHandle()); } void AddClutterToFront(ObservableGCedTreeNodeMixinForTest* parent) { for (int i = 0; i < 4; ++i) { parent->AppendFirstChild( cppgc::MakeGarbageCollected( heap()->GetAllocationHandle())); } } void AddClutterToBack(ObservableGCedTreeNodeMixinForTest* parent) { for (int i = 0; i < 4; ++i) { parent->AppendLastChild( cppgc::MakeGarbageCollected( heap()->GetAllocationHandle())); } } private: cppgc::Persistent root_; }; TEST_F(GCedTreeNodeMixinUnitTest, OneRefence) { CreateRoot(); ObservedPtr watcher(root()); ForceGCAndPump(); EXPECT_TRUE(watcher); } TEST_F(GCedTreeNodeMixinUnitTest, NoReferences) { ObservedPtr watcher(CreateNode()); ForceGCAndPump(); EXPECT_FALSE(watcher); } TEST_F(GCedTreeNodeMixinUnitTest, FirstHasParent) { CreateRoot(); ObservedPtr watcher(CreateNode()); root()->AppendFirstChild(watcher.Get()); ForceGCAndPump(); ASSERT_TRUE(root()); EXPECT_TRUE(watcher); root()->RemoveChild(watcher.Get()); ForceGCAndPump(); ASSERT_TRUE(root()); EXPECT_FALSE(watcher); // Now add some clutter. watcher.Reset(CreateNode()); root()->AppendFirstChild(watcher.Get()); AddClutterToFront(root()); AddClutterToBack(root()); ForceGCAndPump(); ASSERT_TRUE(root()); EXPECT_TRUE(watcher); root()->RemoveChild(watcher.Get()); ForceGCAndPump(); EXPECT_TRUE(root()); EXPECT_FALSE(watcher); } TEST_F(GCedTreeNodeMixinUnitTest, RemoveSelf) { CreateRoot(); ObservedPtr watcher(CreateNode()); root()->AppendFirstChild(watcher.Get()); ForceGCAndPump(); EXPECT_TRUE(root()); ASSERT_TRUE(watcher); watcher->RemoveSelfIfParented(); ForceGCAndPump(); EXPECT_TRUE(root()); EXPECT_FALSE(watcher); } TEST_F(GCedTreeNodeMixinUnitTest, InsertBeforeAfter) { CreateRoot(); AddClutterToFront(root()); ObservedPtr watcher(CreateNode()); root()->AppendFirstChild(watcher.Get()); root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild()); root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild()); ForceGCAndPump(); ASSERT_TRUE(root()); EXPECT_TRUE(watcher); root()->RemoveChild(watcher.Get()); ForceGCAndPump(); EXPECT_TRUE(root()); EXPECT_FALSE(watcher); } TEST_F(GCedTreeNodeMixinUnitTest, AsMapKey) { std::map, int> score; ObservableGCedTreeNodeMixinForTest* node = CreateNode(); score[node] = 100; EXPECT_EQ(100, score[node]); }