1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 "third_party/blink/renderer/core/execution_context/agent_metrics_collector.h"
6 #include "base/test/simple_test_tick_clock.h"
7 #include "base/time/default_tick_clock.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
10 #include "third_party/blink/renderer/core/frame/local_frame.h"
11 #include "third_party/blink/renderer/core/frame/location.h"
12 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
13 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
14 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
15 #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
16 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
17
18 namespace blink {
19
20 class AgentMetricsCollectorUnitTest : public SimTest {
21 public:
22 AgentMetricsCollectorUnitTest() = default;
23
SetUp()24 void SetUp() override {
25 SimTest::SetUp();
26
27 tick_clock_.SetNowTicks(base::TimeTicks::Now());
28
29 // Tests turn this on but it would force all frames into a single agent.
30 // Turn it off so we get an agent per-origin.
31 WebView().GetPage()->GetSettings().SetAllowUniversalAccessFromFileURLs(
32 false);
33
34 WebView().GetPage()->GetAgentMetricsCollector()->SetTickClockForTesting(
35 &tick_clock_);
36 WebView().MainFrameWidget()->Resize(WebSize(800, 600));
37 }
38
TearDown()39 void TearDown() override {
40 if (!torn_down_) {
41 // Avoid UAF when tick_clock_ is destroyed.
42 auto* collector = WebView().GetPage()->GetAgentMetricsCollector();
43
44 SimTest::TearDown();
45 torn_down_ = true;
46
47 collector->SetTickClockForTesting(base::DefaultTickClock::GetInstance());
48 }
49 }
50
51 bool torn_down_ = false;
52
53 HistogramTester tester_;
54 const char* kHistogramName = "PerformanceManager.AgentsPerRendererByTime";
55
56 base::SimpleTestTickClock tick_clock_;
57 };
58
59 // Tests that we record samples across a navigation.
TEST_F(AgentMetricsCollectorUnitTest,AgentsPerRendererRecordOnDocumentChange)60 TEST_F(AgentMetricsCollectorUnitTest, AgentsPerRendererRecordOnDocumentChange) {
61 SimRequest request_a("https://example.com/a.html", "text/html");
62 SimRequest request_b("https://example.com/b.html", "text/html");
63
64 // Load the first page. The histogram won't yet have any data because it
65 // records samples by time and only when documents are created/destroyed.
66 // Immediately after we load a.html, no time has passed yet.
67 LoadURL("https://example.com/a.html");
68 request_a.Complete(R"HTML(
69 <!DOCTYPE html>
70 )HTML");
71 Compositor().BeginFrame();
72
73 tick_clock_.Advance(base::TimeDelta::FromSeconds(10));
74
75 // Load the second page. Since 10 seconds have now elapsed, as the documents
76 // are swapped we should see 10 samples recorded in the 1 agent bucket.
77 LoadURL("https://example.com/b.html");
78 request_b.Complete(R"HTML(
79 <!DOCTYPE html>
80 )HTML");
81 Compositor().BeginFrame();
82
83 tester_.ExpectUniqueSample(kHistogramName, 1, 10);
84 }
85
86 // Test that we correctly record the case where a second agent is added to the
87 // page.
TEST_F(AgentMetricsCollectorUnitTest,MultipleAgents)88 TEST_F(AgentMetricsCollectorUnitTest, MultipleAgents) {
89 SimRequest request_a("https://foo.com/a.html", "text/html");
90 SimRequest request_b("https://bar.com/b.html", "text/html");
91
92 // Load the first page. The histogram won't yet have any data because it
93 // records samples by time and only when documents are created/destroyed.
94 // Immediately after we load a.html, no time has passed yet.
95 LoadURL("https://foo.com/a.html");
96 request_a.Complete(R"HTML(
97 <!DOCTYPE html>
98 <iframe src="about:blank"></iframe>
99 )HTML");
100 Compositor().BeginFrame();
101
102 tick_clock_.Advance(base::TimeDelta::FromSeconds(10));
103
104 // Navigate to a cross-origin page, this should create a second agent.
105 frame_test_helpers::LoadFrameDontWait(
106 MainFrame().FirstChild()->ToWebLocalFrame(),
107 KURL("https://bar.com/b.html"));
108 request_b.Complete(R"HTML(
109 <!DOCTYPE html>
110 )HTML");
111 tester_.ExpectBucketCount(kHistogramName, 1, 10);
112
113 tick_clock_.Advance(base::TimeDelta::FromSeconds(20));
114
115 // Simulate closing the page. This should cause us to report the metrics.
116 TearDown();
117
118 // The final 20 seconds had 2 agents.
119 tester_.ExpectBucketCount(kHistogramName, 1, 10);
120 tester_.ExpectBucketCount(kHistogramName, 2, 20);
121 }
122
123 // Ensure that multiple Pages in the same Agent are reported as only one agent.
TEST_F(AgentMetricsCollectorUnitTest,WindowOpenSameAgents)124 TEST_F(AgentMetricsCollectorUnitTest, WindowOpenSameAgents) {
125 SimRequest request_a("https://example.com/a.html", "text/html");
126 SimRequest request_b("https://example.com/b.html", "text/html");
127
128 LoadURL("https://example.com/a.html");
129 request_a.Complete(R"HTML(
130 <!DOCTYPE html>
131 <script>
132 window.open('https://example.com/b.html');
133 </script>
134 )HTML");
135 request_b.Complete(R"HTML(
136 <!DOCTYPE html>
137 )HTML");
138 Compositor().BeginFrame();
139
140 ASSERT_EQ(2u, Page::OrdinaryPages().size());
141
142 tick_clock_.Advance(base::TimeDelta::FromSeconds(10));
143
144 // Simulate closing the page. This should cause us to report the metrics.
145 TearDown();
146
147 // Both documents should end up in the same Agent, despite having separate
148 // WebViews/Page.
149 tester_.ExpectUniqueSample(kHistogramName, 1, 10);
150 }
151
152 // Ensure that multiple Pages in different Agents are reported as multiple
153 // agents.
TEST_F(AgentMetricsCollectorUnitTest,WindowOpenDifferentAgents)154 TEST_F(AgentMetricsCollectorUnitTest, WindowOpenDifferentAgents) {
155 SimRequest request_a("https://example.com/a.html", "text/html");
156 SimRequest request_b("https://different.com/a.html", "text/html");
157
158 LoadURL("https://example.com/a.html");
159 request_a.Complete(R"HTML(
160 <!DOCTYPE html>
161 <script>
162 window.open('https://different.com/a.html');
163 </script>
164 )HTML");
165 request_b.Complete(R"HTML(
166 <!DOCTYPE html>
167 )HTML");
168 Compositor().BeginFrame();
169
170 ASSERT_EQ(2u, Page::OrdinaryPages().size());
171
172 tick_clock_.Advance(base::TimeDelta::FromSeconds(10));
173
174 // Simulate closing the page. This should cause us to report the metrics.
175 TearDown();
176
177 // Each document should have its own Agent.
178 tester_.ExpectUniqueSample(kHistogramName, 2, 10);
179 }
180
181 } // namespace blink
182