1 // Copyright 2020 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "fxjs/gc/gced_tree_node_mixin.h"
6
7 #include <map>
8
9 #include "core/fxcrt/observed_ptr.h"
10 #include "fxjs/gc/heap.h"
11 #include "testing/fxgc_unittest.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "testing/v8_test_environment.h"
14 #include "v8/include/cppgc/allocation.h"
15 #include "v8/include/cppgc/persistent.h"
16
17 namespace {
18
19 class ObservableGCedTreeNodeMixinForTest
20 : public cppgc::GarbageCollected<ObservableGCedTreeNodeMixinForTest>,
21 public GCedTreeNodeMixin<ObservableGCedTreeNodeMixinForTest>,
22 public Observable {
23 public:
24 CONSTRUCT_VIA_MAKE_GARBAGE_COLLECTED;
25
26 // GCedTreeNodeMixin:
Trace(cppgc::Visitor * visitor) const27 void Trace(cppgc::Visitor* visitor) const override {
28 GCedTreeNodeMixin<ObservableGCedTreeNodeMixinForTest>::Trace(visitor);
29 }
30
31 private:
32 ObservableGCedTreeNodeMixinForTest() = default;
33 };
34
35 } // namespace
36
37 class GCedTreeNodeMixinUnitTest : public FXGCUnitTest {
38 public:
39 GCedTreeNodeMixinUnitTest() = default;
40 ~GCedTreeNodeMixinUnitTest() override = default;
41
42 // FXGCUnitTest:
TearDown()43 void TearDown() override {
44 root_ = nullptr; // Can't (yet) outlive |FXGCUnitTest::heap_|.
45 FXGCUnitTest::TearDown();
46 }
47
root() const48 ObservableGCedTreeNodeMixinForTest* root() const { return root_; }
CreateRoot()49 void CreateRoot() { root_ = CreateNode(); }
50
CreateNode()51 ObservableGCedTreeNodeMixinForTest* CreateNode() {
52 return cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
53 heap()->GetAllocationHandle());
54 }
55
AddClutterToFront(ObservableGCedTreeNodeMixinForTest * parent)56 void AddClutterToFront(ObservableGCedTreeNodeMixinForTest* parent) {
57 for (int i = 0; i < 4; ++i) {
58 parent->AppendFirstChild(
59 cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
60 heap()->GetAllocationHandle()));
61 }
62 }
63
AddClutterToBack(ObservableGCedTreeNodeMixinForTest * parent)64 void AddClutterToBack(ObservableGCedTreeNodeMixinForTest* parent) {
65 for (int i = 0; i < 4; ++i) {
66 parent->AppendLastChild(
67 cppgc::MakeGarbageCollected<ObservableGCedTreeNodeMixinForTest>(
68 heap()->GetAllocationHandle()));
69 }
70 }
71
72 private:
73 cppgc::Persistent<ObservableGCedTreeNodeMixinForTest> root_;
74 };
75
TEST_F(GCedTreeNodeMixinUnitTest,OneRefence)76 TEST_F(GCedTreeNodeMixinUnitTest, OneRefence) {
77 CreateRoot();
78 ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(root());
79 ForceGCAndPump();
80 EXPECT_TRUE(watcher);
81 }
82
TEST_F(GCedTreeNodeMixinUnitTest,NoReferences)83 TEST_F(GCedTreeNodeMixinUnitTest, NoReferences) {
84 ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
85 ForceGCAndPump();
86 EXPECT_FALSE(watcher);
87 }
88
TEST_F(GCedTreeNodeMixinUnitTest,FirstHasParent)89 TEST_F(GCedTreeNodeMixinUnitTest, FirstHasParent) {
90 CreateRoot();
91 ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
92 root()->AppendFirstChild(watcher.Get());
93 ForceGCAndPump();
94 ASSERT_TRUE(root());
95 EXPECT_TRUE(watcher);
96 root()->RemoveChild(watcher.Get());
97 ForceGCAndPump();
98 ASSERT_TRUE(root());
99 EXPECT_FALSE(watcher);
100
101 // Now add some clutter.
102 watcher.Reset(CreateNode());
103 root()->AppendFirstChild(watcher.Get());
104 AddClutterToFront(root());
105 AddClutterToBack(root());
106 ForceGCAndPump();
107 ASSERT_TRUE(root());
108 EXPECT_TRUE(watcher);
109 root()->RemoveChild(watcher.Get());
110 ForceGCAndPump();
111 EXPECT_TRUE(root());
112 EXPECT_FALSE(watcher);
113 }
114
TEST_F(GCedTreeNodeMixinUnitTest,RemoveSelf)115 TEST_F(GCedTreeNodeMixinUnitTest, RemoveSelf) {
116 CreateRoot();
117 ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
118 root()->AppendFirstChild(watcher.Get());
119 ForceGCAndPump();
120 EXPECT_TRUE(root());
121 ASSERT_TRUE(watcher);
122 watcher->RemoveSelfIfParented();
123 ForceGCAndPump();
124 EXPECT_TRUE(root());
125 EXPECT_FALSE(watcher);
126 }
127
TEST_F(GCedTreeNodeMixinUnitTest,InsertBeforeAfter)128 TEST_F(GCedTreeNodeMixinUnitTest, InsertBeforeAfter) {
129 CreateRoot();
130 AddClutterToFront(root());
131 ObservedPtr<ObservableGCedTreeNodeMixinForTest> watcher(CreateNode());
132 root()->AppendFirstChild(watcher.Get());
133 root()->InsertBefore(root()->GetFirstChild(), root()->GetLastChild());
134 root()->InsertAfter(root()->GetLastChild(), root()->GetFirstChild());
135 ForceGCAndPump();
136 ASSERT_TRUE(root());
137 EXPECT_TRUE(watcher);
138 root()->RemoveChild(watcher.Get());
139 ForceGCAndPump();
140 EXPECT_TRUE(root());
141 EXPECT_FALSE(watcher);
142 }
143
TEST_F(GCedTreeNodeMixinUnitTest,AsMapKey)144 TEST_F(GCedTreeNodeMixinUnitTest, AsMapKey) {
145 std::map<cppgc::Persistent<ObservableGCedTreeNodeMixinForTest>, int> score;
146 ObservableGCedTreeNodeMixinForTest* node = CreateNode();
147 score[node] = 100;
148 EXPECT_EQ(100, score[node]);
149 }
150