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 "chrome/browser/page_load_metrics/integration_tests/metric_integration_test.h"
6
7 #include "base/strings/stringprintf.h"
8 #include "base/test/trace_event_analyzer.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/common/chrome_switches.h"
11 #include "chrome/test/base/ui_test_utils.h"
12 #include "content/public/browser/tracing_controller.h"
13 #include "content/public/common/content_switches.h"
14 #include "net/dns/mock_host_resolver.h"
15 #include "net/test/embedded_test_server/http_request.h"
16 #include "net/test/embedded_test_server/http_response.h"
17 #include "services/metrics/public/cpp/ukm_builders.h"
18
19 using base::CommandLine;
20 using base::OnceClosure;
21 using base::RunLoop;
22 using base::StringPiece;
23 using base::TimeDelta;
24 using base::trace_event::TraceConfig;
25 using content::TracingController;
26 using content::WebContents;
27 using net::test_server::BasicHttpResponse;
28 using net::test_server::HttpRequest;
29 using net::test_server::HttpResponse;
30 using trace_analyzer::TraceAnalyzer;
31 using ukm::TestUkmRecorder;
32 using ukm::builders::PageLoad;
33 using ukm::mojom::UkmEntry;
34
35 MetricIntegrationTest::MetricIntegrationTest() = default;
36 MetricIntegrationTest::~MetricIntegrationTest() = default;
37
SetUpOnMainThread()38 void MetricIntegrationTest::SetUpOnMainThread() {
39 host_resolver()->AddRule("*", "127.0.0.1");
40 embedded_test_server()->ServeFilesFromSourceDirectory(
41 "chrome/browser/page_load_metrics/integration_tests/data");
42 embedded_test_server()->ServeFilesFromSourceDirectory(
43 "third_party/blink/web_tests/external/wpt");
44 content::SetupCrossSiteRedirector(embedded_test_server());
45
46 ukm_recorder_.emplace();
47 histogram_tester_.emplace();
48 }
49
ServeDelayed(const std::string & url,const std::string & content,TimeDelta delay)50 void MetricIntegrationTest::ServeDelayed(const std::string& url,
51 const std::string& content,
52 TimeDelta delay) {
53 embedded_test_server()->RegisterRequestHandler(
54 base::BindRepeating(&HandleRequest, url, content, delay));
55 }
56
Serve(const std::string & url,const std::string & content)57 void MetricIntegrationTest::Serve(const std::string& url,
58 const std::string& content) {
59 ServeDelayed(url, content, TimeDelta());
60 }
61
Start()62 void MetricIntegrationTest::Start() {
63 ASSERT_TRUE(embedded_test_server()->Start());
64 }
65
Load(const std::string & relative_url)66 void MetricIntegrationTest::Load(const std::string& relative_url) {
67 GURL url = embedded_test_server()->GetURL("example.com", relative_url);
68 ui_test_utils::NavigateToURL(browser(), url);
69 }
70
LoadHTML(const std::string & content)71 void MetricIntegrationTest::LoadHTML(const std::string& content) {
72 Serve("/test.html", content);
73 Start();
74 Load("/test.html");
75 }
76
StartTracing(const std::vector<std::string> & categories)77 void MetricIntegrationTest::StartTracing(
78 const std::vector<std::string>& categories) {
79 RunLoop wait_for_tracing;
80 TracingController::GetInstance()->StartTracing(
81 TraceConfig(
82 base::StringPrintf("{\"included_categories\": [\"%s\"]}",
83 base::JoinString(categories, "\", \"").c_str())),
84 wait_for_tracing.QuitClosure());
85 wait_for_tracing.Run();
86 }
87
StopTracing(std::string & trace_output)88 void MetricIntegrationTest::StopTracing(std::string& trace_output) {
89 RunLoop wait_for_tracing;
90 TracingController::GetInstance()->StopTracing(
91 TracingController::CreateStringEndpoint(base::BindOnce(
92 [](OnceClosure quit_closure, std::string* output,
93 std::unique_ptr<std::string> trace_str) {
94 *output = *trace_str;
95 std::move(quit_closure).Run();
96 },
97 wait_for_tracing.QuitClosure(), &trace_output)));
98 wait_for_tracing.Run();
99 }
100
StopTracingAndAnalyze()101 std::unique_ptr<TraceAnalyzer> MetricIntegrationTest::StopTracingAndAnalyze() {
102 std::string trace_str;
103 StopTracing(trace_str);
104 return std::unique_ptr<TraceAnalyzer>(TraceAnalyzer::Create(trace_str));
105 }
106
web_contents() const107 WebContents* MetricIntegrationTest::web_contents() const {
108 return browser()->tab_strip_model()->GetActiveWebContents();
109 }
110
SetUpCommandLine(CommandLine * command_line)111 void MetricIntegrationTest::SetUpCommandLine(CommandLine* command_line) {
112 // Set a default window size for consistency.
113 command_line->AppendSwitchASCII(switches::kWindowSize, "800,600");
114 command_line->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
115
116 content::IsolateAllSitesForTesting(command_line);
117 }
118
HandleRequest(const std::string & relative_url,const std::string & content,TimeDelta delay,const HttpRequest & request)119 std::unique_ptr<HttpResponse> MetricIntegrationTest::HandleRequest(
120 const std::string& relative_url,
121 const std::string& content,
122 TimeDelta delay,
123 const HttpRequest& request) {
124 if (request.relative_url != relative_url)
125 return nullptr;
126 if (!delay.is_zero())
127 base::PlatformThread::Sleep(delay);
128 auto response = std::make_unique<BasicHttpResponse>();
129 response->set_code(net::HTTP_OK);
130 response->set_content(content);
131 response->set_content_type("text/html; charset=utf-8");
132 return std::move(response);
133 }
134
ExpectUKMPageLoadMetric(StringPiece metric_name,int64_t expected_value)135 void MetricIntegrationTest::ExpectUKMPageLoadMetric(StringPiece metric_name,
136 int64_t expected_value) {
137 std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
138 ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
139 EXPECT_EQ(1ul, merged_entries.size());
140 const auto& kv = merged_entries.begin();
141 TestUkmRecorder::ExpectEntryMetric(kv->second.get(), metric_name,
142 expected_value);
143 }
144
ExpectUKMPageLoadMetricNear(StringPiece metric_name,double expected_value,double epsilon)145 void MetricIntegrationTest::ExpectUKMPageLoadMetricNear(StringPiece metric_name,
146 double expected_value,
147 double epsilon) {
148 std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
149 ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
150
151 EXPECT_EQ(1ul, merged_entries.size());
152 const auto& kv = merged_entries.begin();
153 const int64_t* recorded =
154 TestUkmRecorder::GetEntryMetric(kv->second.get(), metric_name);
155 EXPECT_NE(recorded, nullptr);
156 EXPECT_NEAR(*recorded, expected_value, epsilon);
157 }
158
ExpectUniqueUMAPageLoadMetricNear(StringPiece metric_name,double expected_value)159 void MetricIntegrationTest::ExpectUniqueUMAPageLoadMetricNear(
160 StringPiece metric_name,
161 double expected_value) {
162 EXPECT_EQ(histogram_tester_->GetAllSamples(metric_name).size(), 1u)
163 << "There should be one sample for " << metric_name.data();
164 // UMA uses integer buckets so check that the value is in the bucket of
165 // |expected_value| or in the bucket of |expected_value| +- 1.
166 EXPECT_TRUE(
167 histogram_tester_->GetBucketCount(metric_name, expected_value) == 1 ||
168 histogram_tester_->GetBucketCount(metric_name, expected_value + 1.0) ==
169 1 ||
170 histogram_tester_->GetBucketCount(metric_name, expected_value - 1.0) == 1)
171 << "The sample for " << metric_name.data()
172 << " is not near the expected value!";
173 }
174