1 // Copyright 2018 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_protocol_browsertest.h"
6
7 #include "base/base64.h"
8 #include "base/base_paths.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/json/json_reader.h"
12 #include "base/path_service.h"
13 #include "build/build_config.h"
14 #include "headless/app/headless_shell_switches.h"
15 #include "net/test/embedded_test_server/embedded_test_server.h"
16 #include "services/network/public/cpp/network_switches.h"
17
18 namespace headless {
19
20 namespace {
21 static const char kResetResults[] = "reset-results";
22 static const char kDumpDevToolsProtocol[] = "dump-devtools-protocol";
23 } // namespace
24
HeadlessProtocolBrowserTest()25 HeadlessProtocolBrowserTest::HeadlessProtocolBrowserTest() {
26 embedded_test_server()->ServeFilesFromSourceDirectory(
27 "third_party/blink/web_tests/http/tests/inspector-protocol");
28 EXPECT_TRUE(embedded_test_server()->Start());
29 }
30
SetUpCommandLine(base::CommandLine * command_line)31 void HeadlessProtocolBrowserTest::SetUpCommandLine(
32 base::CommandLine* command_line) {
33 command_line->AppendSwitchASCII(::network::switches::kHostResolverRules,
34 "MAP *.test 127.0.0.1");
35 HeadlessAsyncDevTooledBrowserTest::SetUpCommandLine(command_line);
36
37 // Make sure the navigations spawn new processes. We run test harness
38 // in one process (harness.test) and tests in another.
39 command_line->AppendSwitch(::switches::kSitePerProcess);
40
41 // Make sure proxy related tests are not affected by a platform specific
42 // system proxy configuration service.
43 command_line->AppendSwitch(switches::kNoSystemProxyConfigService);
44 }
45
GetPageUrlExtraParams()46 std::vector<std::string> HeadlessProtocolBrowserTest::GetPageUrlExtraParams() {
47 return {};
48 }
49
RunDevTooledTest()50 void HeadlessProtocolBrowserTest::RunDevTooledTest() {
51 browser_devtools_client_->SetRawProtocolListener(this);
52 devtools_client_->GetRuntime()->GetExperimental()->AddObserver(this);
53 devtools_client_->GetRuntime()->Enable();
54 devtools_client_->GetRuntime()->GetExperimental()->AddBinding(
55 headless::runtime::AddBindingParams::Builder()
56 .SetName("sendProtocolMessage")
57 .Build(),
58 base::BindOnce(&HeadlessProtocolBrowserTest::BindingCreated,
59 base::Unretained(this)));
60 }
61
BindingCreated(std::unique_ptr<headless::runtime::AddBindingResult>)62 void HeadlessProtocolBrowserTest::BindingCreated(
63 std::unique_ptr<headless::runtime::AddBindingResult>) {
64 base::ScopedAllowBlockingForTesting allow_blocking;
65 base::FilePath src_dir;
66 CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
67 static const base::FilePath kTestsDirectory(
68 FILE_PATH_LITERAL("headless/test/data/protocol"));
69 base::FilePath test_path =
70 src_dir.Append(kTestsDirectory).AppendASCII(script_name_);
71 std::string script;
72 if (!base::ReadFileToString(test_path, &script)) {
73 ADD_FAILURE() << "Unable to read test at " << test_path;
74 FinishTest();
75 return;
76 }
77 GURL test_url = embedded_test_server()->GetURL("harness.test",
78 "/protocol/" + script_name_);
79 GURL target_url =
80 embedded_test_server()->GetURL("127.0.0.1", "/protocol/" + script_name_);
81
82 std::string extra_params;
83 for (const auto& param : GetPageUrlExtraParams()) {
84 extra_params += "&" + param;
85 }
86
87 GURL page_url = embedded_test_server()->GetURL(
88 "harness.test",
89 "/protocol/inspector-protocol-test.html?test=" + test_url.spec() +
90 "&target=" + target_url.spec() + extra_params);
91 devtools_client_->GetPage()->Navigate(page_url.spec());
92 }
93
OnBindingCalled(const runtime::BindingCalledParams & params)94 void HeadlessProtocolBrowserTest::OnBindingCalled(
95 const runtime::BindingCalledParams& params) {
96 std::string json_message = params.GetPayload();
97 std::unique_ptr<base::Value> message =
98 base::JSONReader::ReadDeprecated(json_message);
99 const base::DictionaryValue* message_dict;
100 const base::DictionaryValue* params_dict;
101 std::string method;
102 int id;
103 if (!message || !message->GetAsDictionary(&message_dict) ||
104 !message_dict->GetString("method", &method) ||
105 !message_dict->GetDictionary("params", ¶ms_dict) ||
106 !message_dict->GetInteger("id", &id)) {
107 LOG(ERROR) << "Poorly formed message " << json_message;
108 FinishTest();
109 return;
110 }
111
112 if (method != "DONE") {
113 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
114 kDumpDevToolsProtocol)) {
115 LOG(INFO) << "FromJS: " << json_message;
116 }
117 // Pass unhandled commands onto the inspector.
118 browser_devtools_client_->SendRawDevToolsMessage(json_message);
119 return;
120 }
121
122 std::string test_result;
123 message_dict->GetString("result", &test_result);
124 static const base::FilePath kTestsDirectory(
125 FILE_PATH_LITERAL("headless/test/data/protocol"));
126
127 base::ScopedAllowBlockingForTesting allow_blocking;
128
129 base::FilePath src_dir;
130 CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
131 base::FilePath expectation_path =
132 src_dir.Append(kTestsDirectory)
133 .AppendASCII(script_name_.substr(0, script_name_.length() - 3) +
134 "-expected.txt");
135
136 if (base::CommandLine::ForCurrentProcess()->HasSwitch(kResetResults)) {
137 LOG(INFO) << "Updating expectations at " << expectation_path;
138 int result = base::WriteFile(expectation_path, test_result.data(),
139 static_cast<int>(test_result.size()));
140 CHECK(test_result.size() == static_cast<size_t>(result));
141 }
142
143 std::string expectation;
144 if (!base::ReadFileToString(expectation_path, &expectation)) {
145 ADD_FAILURE() << "Unable to read expectations at " << expectation_path;
146 }
147 EXPECT_EQ(test_result, expectation);
148 FinishTest();
149 }
150
OnProtocolMessage(base::span<const uint8_t> json_message,const base::DictionaryValue & parsed_message)151 bool HeadlessProtocolBrowserTest::OnProtocolMessage(
152 base::span<const uint8_t> json_message,
153 const base::DictionaryValue& parsed_message) {
154 SendMessageToJS(base::StringPiece(
155 reinterpret_cast<const char*>(json_message.data()), json_message.size()));
156 return true;
157 }
158
SendMessageToJS(base::StringPiece message)159 void HeadlessProtocolBrowserTest::SendMessageToJS(base::StringPiece message) {
160 if (test_finished_)
161 return;
162
163 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
164 kDumpDevToolsProtocol)) {
165 LOG(INFO) << "ToJS: " << message;
166 }
167
168 std::string encoded;
169 base::Base64Encode(message, &encoded);
170 devtools_client_->GetRuntime()->Evaluate("onmessage(atob(\"" + encoded +
171 "\"))");
172 }
173
FinishTest()174 void HeadlessProtocolBrowserTest::FinishTest() {
175 test_finished_ = true;
176 FinishAsynchronousTest();
177 }
178
179 // TODO(crbug.com/1086872): The whole test suite is flaky on Mac ASAN.
180 #if (defined(OS_MAC) && defined(ADDRESS_SANITIZER))
181 #define HEADLESS_PROTOCOL_TEST(TEST_NAME, SCRIPT_NAME) \
182 IN_PROC_BROWSER_TEST_F(HeadlessProtocolBrowserTest, DISABLED_##TEST_NAME) { \
183 test_folder_ = "/protocol/"; \
184 script_name_ = SCRIPT_NAME; \
185 RunTest(); \
186 }
187 #else
188 #define HEADLESS_PROTOCOL_TEST(TEST_NAME, SCRIPT_NAME) \
189 IN_PROC_BROWSER_TEST_F(HeadlessProtocolBrowserTest, TEST_NAME) { \
190 test_folder_ = "/protocol/"; \
191 script_name_ = SCRIPT_NAME; \
192 RunTest(); \
193 }
194 #endif
195
196 // Headless-specific tests
197 HEADLESS_PROTOCOL_TEST(VirtualTimeBasics, "emulation/virtual-time-basics.js")
198 HEADLESS_PROTOCOL_TEST(VirtualTimeInterrupt,
199 "emulation/virtual-time-interrupt.js")
200
201 // Flaky on Linux, Mac & Win. TODO(crbug.com/930717): Re-enable.
202 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) || \
203 defined(OS_WIN) || defined(OS_FUCHSIA)
204 #define MAYBE_VirtualTimeCrossProcessNavigation \
205 DISABLED_VirtualTimeCrossProcessNavigation
206 #else
207 #define MAYBE_VirtualTimeCrossProcessNavigation \
208 VirtualTimeCrossProcessNavigation
209 #endif
210 HEADLESS_PROTOCOL_TEST(MAYBE_VirtualTimeCrossProcessNavigation,
211 "emulation/virtual-time-cross-process-navigation.js")
212 HEADLESS_PROTOCOL_TEST(VirtualTimeDetachFrame,
213 "emulation/virtual-time-detach-frame.js")
214 HEADLESS_PROTOCOL_TEST(VirtualTimeNoBlock404, "emulation/virtual-time-404.js")
215 HEADLESS_PROTOCOL_TEST(VirtualTimeLocalStorage,
216 "emulation/virtual-time-local-storage.js")
217 HEADLESS_PROTOCOL_TEST(VirtualTimePendingScript,
218 "emulation/virtual-time-pending-script.js")
219 HEADLESS_PROTOCOL_TEST(VirtualTimeRedirect,
220 "emulation/virtual-time-redirect.js")
221 HEADLESS_PROTOCOL_TEST(VirtualTimeSessionStorage,
222 "emulation/virtual-time-session-storage.js")
223 HEADLESS_PROTOCOL_TEST(VirtualTimeStarvation,
224 "emulation/virtual-time-starvation.js")
225 HEADLESS_PROTOCOL_TEST(VirtualTimeVideo, "emulation/virtual-time-video.js")
226 HEADLESS_PROTOCOL_TEST(VirtualTimeErrorLoop,
227 "emulation/virtual-time-error-loop.js")
228 HEADLESS_PROTOCOL_TEST(VirtualTimeFetchStream,
229 "emulation/virtual-time-fetch-stream.js")
230 HEADLESS_PROTOCOL_TEST(VirtualTimeDialogWhileLoading,
231 "emulation/virtual-time-dialog-while-loading.js")
232 HEADLESS_PROTOCOL_TEST(VirtualTimeHistoryNavigation,
233 "emulation/virtual-time-history-navigation.js")
234 HEADLESS_PROTOCOL_TEST(VirtualTimeHistoryNavigationSameDoc,
235 "emulation/virtual-time-history-navigation-same-doc.js")
236 HEADLESS_PROTOCOL_TEST(VirtualTimeFetchKeepalive,
237 "emulation/virtual-time-fetch-keepalive.js")
238 HEADLESS_PROTOCOL_TEST(VirtualTimeDisposeWhileRunning,
239 "emulation/virtual-time-dispose-while-running.js")
240 HEADLESS_PROTOCOL_TEST(VirtualTimePausesDocumentLoading,
241 "emulation/virtual-time-pauses-document-loading.js")
242
243 HEADLESS_PROTOCOL_TEST(PageBeforeUnload, "page/page-before-unload.js")
244
245 // http://crbug.com/633321
246 #if defined(OS_ANDROID)
247 #define MAYBE_VirtualTimeTimerOrder DISABLED_VirtualTimeTimerOrder
248 #define MAYBE_VirtualTimeTimerSuspend DISABLED_VirtualTimeTimerSuspend
249 #else
250 #define MAYBE_VirtualTimeTimerOrder VirtualTimeTimerOrder
251 #define MAYBE_VirtualTimeTimerSuspend VirtualTimeTimerSuspend
252 #endif
253 HEADLESS_PROTOCOL_TEST(MAYBE_VirtualTimeTimerOrder,
254 "emulation/virtual-time-timer-order.js")
255 HEADLESS_PROTOCOL_TEST(MAYBE_VirtualTimeTimerSuspend,
256 "emulation/virtual-time-timer-suspended.js")
257 #undef MAYBE_VirtualTimeTimerOrder
258 #undef MAYBE_VirtualTimeTimerSuspend
259
260 HEADLESS_PROTOCOL_TEST(HeadlessSessionBasicsTest,
261 "sessions/headless-session-basics.js")
262
263 HEADLESS_PROTOCOL_TEST(HeadlessSessionCreateContextDisposeOnDetach,
264 "sessions/headless-createContext-disposeOnDetach.js")
265
266 HEADLESS_PROTOCOL_TEST(BrowserSetInitialProxyConfig,
267 "sanity/browser-set-initial-proxy-config.js")
268
269 class HeadlessProtocolBrowserTestWithProxy
270 : public HeadlessProtocolBrowserTest {
271 public:
HeadlessProtocolBrowserTestWithProxy()272 HeadlessProtocolBrowserTestWithProxy()
273 : proxy_server_(net::EmbeddedTestServer::TYPE_HTTP) {
274 proxy_server_.AddDefaultHandlers(
275 base::FilePath(FILE_PATH_LITERAL("headless/test/data")));
276 }
277
SetUp()278 void SetUp() override {
279 ASSERT_TRUE(proxy_server_.Start());
280 HeadlessProtocolBrowserTest::SetUp();
281 }
282
TearDown()283 void TearDown() override {
284 EXPECT_TRUE(proxy_server_.ShutdownAndWaitUntilComplete());
285 HeadlessProtocolBrowserTest::TearDown();
286 }
287
proxy_server()288 net::EmbeddedTestServer* proxy_server() { return &proxy_server_; }
289
290 protected:
GetPageUrlExtraParams()291 std::vector<std::string> GetPageUrlExtraParams() override {
292 std::string proxy = proxy_server()->host_port_pair().ToString();
293 return {"&proxy=" + proxy};
294 }
295
296 private:
297 net::EmbeddedTestServer proxy_server_;
298 };
299
300 #define HEADLESS_PROTOCOL_TEST_WITH_PROXY(TEST_NAME, SCRIPT_NAME) \
301 IN_PROC_BROWSER_TEST_F(HeadlessProtocolBrowserTestWithProxy, TEST_NAME) { \
302 test_folder_ = "/protocol/"; \
303 script_name_ = SCRIPT_NAME; \
304 RunTest(); \
305 }
306
307 HEADLESS_PROTOCOL_TEST_WITH_PROXY(BrowserSetProxyConfig,
308 "sanity/browser-set-proxy-config.js")
309
310 } // namespace headless
311