1 // Copyright 2016 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 "headless/test/headless_browser_test.h"
6
7 #include <memory>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/macros.h"
13 #include "base/path_service.h"
14 #include "base/run_loop.h"
15 #include "base/task/current_thread.h"
16 #include "build/build_config.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/common/url_constants.h"
20 #include "content/public/test/browser_test_utils.h"
21 #include "content/public/test/test_navigation_observer.h"
22 #include "headless/lib/browser/headless_browser_impl.h"
23 #include "headless/lib/browser/headless_web_contents_impl.h"
24 #include "headless/lib/headless_content_main_delegate.h"
25 #include "headless/public/devtools/domains/emulation.h"
26 #include "headless/public/devtools/domains/runtime.h"
27 #include "headless/public/headless_devtools_client.h"
28 #include "headless/public/headless_devtools_target.h"
29 #include "headless/public/headless_web_contents.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "ui/gfx/geometry/size.h"
32 #include "ui/gl/gl_switches.h"
33 #include "url/gurl.h"
34
35 namespace headless {
36 namespace {
37
38 class SynchronousLoadObserver {
39 public:
SynchronousLoadObserver(HeadlessBrowserTest * browser_test,HeadlessWebContents * web_contents)40 SynchronousLoadObserver(HeadlessBrowserTest* browser_test,
41 HeadlessWebContents* web_contents)
42 : web_contents_(web_contents),
43 devtools_client_(HeadlessDevToolsClient::Create()) {
44 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
45 load_observer_.reset(new LoadObserver(
46 devtools_client_.get(),
47 base::BindOnce(&HeadlessBrowserTest::FinishAsynchronousTest,
48 base::Unretained(browser_test))));
49 }
50
~SynchronousLoadObserver()51 ~SynchronousLoadObserver() {
52 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
53 }
54
navigation_succeeded() const55 bool navigation_succeeded() const {
56 return load_observer_->navigation_succeeded();
57 }
58
59 private:
60 HeadlessWebContents* web_contents_; // Not owned.
61 std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
62 std::unique_ptr<LoadObserver> load_observer_;
63 };
64
65 class EvaluateHelper {
66 public:
EvaluateHelper(HeadlessBrowserTest * browser_test,HeadlessWebContents * web_contents,const std::string & script_to_eval)67 EvaluateHelper(HeadlessBrowserTest* browser_test,
68 HeadlessWebContents* web_contents,
69 const std::string& script_to_eval)
70 : browser_test_(browser_test),
71 web_contents_(web_contents),
72 devtools_client_(HeadlessDevToolsClient::Create()) {
73 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
74 devtools_client_->GetRuntime()->Evaluate(
75 script_to_eval, base::BindOnce(&EvaluateHelper::OnEvaluateResult,
76 base::Unretained(this)));
77 }
78
~EvaluateHelper()79 ~EvaluateHelper() {
80 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
81 }
82
OnEvaluateResult(std::unique_ptr<runtime::EvaluateResult> result)83 void OnEvaluateResult(std::unique_ptr<runtime::EvaluateResult> result) {
84 result_ = std::move(result);
85 browser_test_->FinishAsynchronousTest();
86 }
87
TakeResult()88 std::unique_ptr<runtime::EvaluateResult> TakeResult() {
89 return std::move(result_);
90 }
91
92 private:
93 HeadlessBrowserTest* browser_test_; // Not owned.
94 HeadlessWebContents* web_contents_; // Not owned.
95 std::unique_ptr<HeadlessDevToolsClient> devtools_client_;
96
97 std::unique_ptr<runtime::EvaluateResult> result_;
98
99 DISALLOW_COPY_AND_ASSIGN(EvaluateHelper);
100 };
101
102 } // namespace
103
LoadObserver(HeadlessDevToolsClient * devtools_client,base::OnceClosure callback)104 LoadObserver::LoadObserver(HeadlessDevToolsClient* devtools_client,
105 base::OnceClosure callback)
106 : callback_(std::move(callback)),
107 devtools_client_(devtools_client),
108 navigation_succeeded_(true) {
109 devtools_client_->GetNetwork()->AddObserver(this);
110 devtools_client_->GetNetwork()->Enable();
111 devtools_client_->GetPage()->AddObserver(this);
112 devtools_client_->GetPage()->Enable();
113 }
114
~LoadObserver()115 LoadObserver::~LoadObserver() {
116 devtools_client_->GetNetwork()->RemoveObserver(this);
117 devtools_client_->GetPage()->RemoveObserver(this);
118 }
119
OnLoadEventFired(const page::LoadEventFiredParams & params)120 void LoadObserver::OnLoadEventFired(const page::LoadEventFiredParams& params) {
121 std::move(callback_).Run();
122 }
123
OnResponseReceived(const network::ResponseReceivedParams & params)124 void LoadObserver::OnResponseReceived(
125 const network::ResponseReceivedParams& params) {
126 if (params.GetResponse()->GetStatus() != 200 ||
127 params.GetResponse()->GetUrl() == content::kUnreachableWebDataURL) {
128 navigation_succeeded_ = false;
129 }
130 }
131
HeadlessBrowserTest()132 HeadlessBrowserTest::HeadlessBrowserTest() {
133 #if defined(OS_MAC)
134 // On Mac the source root is not set properly. We override it by assuming
135 // that is two directories up from the execution test file.
136 base::FilePath dir_exe_path;
137 CHECK(base::PathService::Get(base::DIR_EXE, &dir_exe_path));
138 dir_exe_path = dir_exe_path.Append("../../");
139 CHECK(base::PathService::Override(base::DIR_SOURCE_ROOT, dir_exe_path));
140 #endif // defined(OS_MAC)
141 base::FilePath headless_test_data(FILE_PATH_LITERAL("headless/test/data"));
142 CreateTestServer(headless_test_data);
143 }
144
SetUp()145 void HeadlessBrowserTest::SetUp() {
146 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
147 // Enable GPU usage (i.e., SwiftShader, hardware GL on macOS) in all tests
148 // since that's the default configuration of --headless.
149 command_line->AppendSwitch(switches::kUseGpuInTests);
150 SetUpCommandLine(command_line);
151 BrowserTestBase::SetUp();
152 }
153
SetUpWithoutGPU()154 void HeadlessBrowserTest::SetUpWithoutGPU() {
155 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
156 SetUpCommandLine(command_line);
157 BrowserTestBase::SetUp();
158 }
159
160 HeadlessBrowserTest::~HeadlessBrowserTest() = default;
161
PreRunTestOnMainThread()162 void HeadlessBrowserTest::PreRunTestOnMainThread() {
163 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
164 // Pump startup related events.
165 base::RunLoop().RunUntilIdle();
166 }
167
PostRunTestOnMainThread()168 void HeadlessBrowserTest::PostRunTestOnMainThread() {
169 browser()->Shutdown();
170 for (content::RenderProcessHost::iterator i(
171 content::RenderProcessHost::AllHostsIterator());
172 !i.IsAtEnd(); i.Advance()) {
173 i.GetCurrentValue()->FastShutdownIfPossible();
174 }
175 // Pump tasks produced during shutdown.
176 base::RunLoop().RunUntilIdle();
177 }
178
browser() const179 HeadlessBrowser* HeadlessBrowserTest::browser() const {
180 return HeadlessContentMainDelegate::GetInstance()->browser();
181 }
182
options() const183 HeadlessBrowser::Options* HeadlessBrowserTest::options() const {
184 return HeadlessContentMainDelegate::GetInstance()->browser()->options();
185 }
186
WaitForLoad(HeadlessWebContents * web_contents)187 bool HeadlessBrowserTest::WaitForLoad(HeadlessWebContents* web_contents) {
188 HeadlessWebContentsImpl* web_contents_impl =
189 HeadlessWebContentsImpl::From(web_contents);
190 content::TestNavigationObserver observer(web_contents_impl->web_contents(),
191 1);
192 observer.Wait();
193 return observer.last_navigation_succeeded();
194 }
195
WaitForLoadAndGainFocus(HeadlessWebContents * web_contents)196 void HeadlessBrowserTest::WaitForLoadAndGainFocus(
197 HeadlessWebContents* web_contents) {
198 content::WebContents* content =
199 HeadlessWebContentsImpl::From(web_contents)->web_contents();
200
201 // To finish loading and to gain focus are two independent events. Which one
202 // is issued first is undefined. The following code is waiting on both, in any
203 // order.
204 content::TestNavigationObserver load_observer(content, 1);
205 content::FrameFocusedObserver focus_observer(content->GetMainFrame());
206 load_observer.Wait();
207 focus_observer.Wait();
208 }
209
EvaluateScript(HeadlessWebContents * web_contents,const std::string & script)210 std::unique_ptr<runtime::EvaluateResult> HeadlessBrowserTest::EvaluateScript(
211 HeadlessWebContents* web_contents,
212 const std::string& script) {
213 EvaluateHelper helper(this, web_contents, script);
214 RunAsynchronousTest();
215 return helper.TakeResult();
216 }
217
RunAsynchronousTest()218 void HeadlessBrowserTest::RunAsynchronousTest() {
219 EXPECT_FALSE(run_loop_);
220 run_loop_ = std::make_unique<base::RunLoop>(
221 base::RunLoop::Type::kNestableTasksAllowed);
222 run_loop_->Run();
223 run_loop_ = nullptr;
224 }
225
FinishAsynchronousTest()226 void HeadlessBrowserTest::FinishAsynchronousTest() {
227 run_loop_->Quit();
228 }
229
HeadlessAsyncDevTooledBrowserTest()230 HeadlessAsyncDevTooledBrowserTest::HeadlessAsyncDevTooledBrowserTest()
231 : browser_context_(nullptr),
232 web_contents_(nullptr),
233 render_process_exited_(false) {}
234
235 HeadlessAsyncDevTooledBrowserTest::~HeadlessAsyncDevTooledBrowserTest() =
236 default;
237
DevToolsTargetReady()238 void HeadlessAsyncDevTooledBrowserTest::DevToolsTargetReady() {
239 EXPECT_TRUE(web_contents_->GetDevToolsTarget());
240 web_contents_->GetDevToolsTarget()->AttachClient(devtools_client_.get());
241 #if defined(OS_MAC)
242 devtools_client_->GetEmulation()->SetDeviceMetricsOverride(
243 emulation::SetDeviceMetricsOverrideParams::Builder()
244 .SetWidth(0)
245 .SetHeight(0)
246 .SetDeviceScaleFactor(1)
247 .SetMobile(false)
248 .Build(),
249 base::BindOnce(
250 [](HeadlessAsyncDevTooledBrowserTest* self) {
251 self->RunDevTooledTest();
252 },
253 base::Unretained(this)));
254 #else
255 RunDevTooledTest();
256 #endif
257 }
258
RenderProcessExited(base::TerminationStatus status,int exit_code)259 void HeadlessAsyncDevTooledBrowserTest::RenderProcessExited(
260 base::TerminationStatus status,
261 int exit_code) {
262 if (status == base::TERMINATION_STATUS_NORMAL_TERMINATION)
263 return;
264
265 FinishAsynchronousTest();
266 render_process_exited_ = true;
267 FAIL() << "Abnormal renderer termination "
268 << "(status=" << status << ", exit_code=" << exit_code << ")";
269 }
270
RunTest()271 void HeadlessAsyncDevTooledBrowserTest::RunTest() {
272 devtools_client_ = HeadlessDevToolsClient::Create();
273 browser_devtools_client_ = HeadlessDevToolsClient::Create();
274 interceptor_ = std::make_unique<TestNetworkInterceptor>();
275 HeadlessBrowserContext::Builder builder =
276 browser()->CreateBrowserContextBuilder();
277 CustomizeHeadlessBrowserContext(builder);
278 browser_context_ = builder.Build();
279
280 browser()->SetDefaultBrowserContext(browser_context_);
281 browser()->GetDevToolsTarget()->AttachClient(browser_devtools_client_.get());
282
283 HeadlessWebContents::Builder web_contents_builder =
284 browser_context_->CreateWebContentsBuilder();
285 web_contents_builder.SetEnableBeginFrameControl(GetEnableBeginFrameControl());
286 CustomizeHeadlessWebContents(web_contents_builder);
287 web_contents_ = web_contents_builder.Build();
288
289 web_contents_->AddObserver(this);
290
291 RunAsynchronousTest();
292 interceptor_.reset();
293 if (!render_process_exited_)
294 web_contents_->GetDevToolsTarget()->DetachClient(devtools_client_.get());
295 web_contents_->RemoveObserver(this);
296 web_contents_->Close();
297 web_contents_ = nullptr;
298 browser()->GetDevToolsTarget()->DetachClient(browser_devtools_client_.get());
299 browser_context_->Close();
300 browser_context_ = nullptr;
301 // Let the tasks that might have beein scheduled during web contents
302 // being closed run (see https://crbug.com/1036627 for details).
303 base::RunLoop().RunUntilIdle();
304 }
305
GetEnableBeginFrameControl()306 bool HeadlessAsyncDevTooledBrowserTest::GetEnableBeginFrameControl() {
307 return false;
308 }
309
CustomizeHeadlessBrowserContext(HeadlessBrowserContext::Builder & builder)310 void HeadlessAsyncDevTooledBrowserTest::CustomizeHeadlessBrowserContext(
311 HeadlessBrowserContext::Builder& builder) {}
312
CustomizeHeadlessWebContents(HeadlessWebContents::Builder & builder)313 void HeadlessAsyncDevTooledBrowserTest::CustomizeHeadlessWebContents(
314 HeadlessWebContents::Builder& builder) {}
315
316 } // namespace headless
317