1 // Copyright (c) 2012 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.
5 #include "chrome/test/nacl/nacl_browsertest_util.h"
8 #include "base/command_line.h"
9 #include "base/json/json_reader.h"
10 #include "base/path_service.h"
11 #include "base/values.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/common/chrome_paths.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "chrome/test/base/ui_test_utils.h"
17 #include "components/nacl/common/nacl_switches.h"
18 #include "content/public/browser/plugin_service.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/webplugininfo.h"
21 #include "net/base/net_util.h"
23 typedef content::TestMessageHandler::MessageResponse MessageResponse;
25 MessageResponse StructuredMessageHandler::HandleMessage(
26 const std::string& json) {
27 scoped_ptr<base::Value> value;
28 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
29 // Automation messages are stringified before they are sent because the
30 // automation channel cannot handle arbitrary objects. This means we
31 // need to decode the json twice to get the original message.
32 value.reset(reader.ReadToValue(json));
34 return InternalError("Could parse automation JSON: " + json +
35 " because " + reader.GetErrorMessage());
38 if (!value->GetAsString(&temp))
39 return InternalError("Message was not a string: " + json);
41 value.reset(reader.ReadToValue(temp));
43 return InternalError("Could not parse message JSON: " + temp +
44 " because " + reader.GetErrorMessage());
46 base::DictionaryValue* msg;
47 if (!value->GetAsDictionary(&msg))
48 return InternalError("Message was not an object: " + temp);
51 if (!msg->GetString("type", &type))
52 return MissingField("unknown", "type");
54 return HandleStructuredMessage(type, msg);
57 MessageResponse StructuredMessageHandler::MissingField(
58 const std::string& type,
59 const std::string& field) {
60 return InternalError(type + " message did not have field: " + field);
63 MessageResponse StructuredMessageHandler::InternalError(
64 const std::string& reason) {
69 LoadTestMessageHandler::LoadTestMessageHandler()
70 : test_passed_(false) {
73 void LoadTestMessageHandler::Log(const std::string& type,
74 const std::string& message) {
75 // TODO(ncbray) better logging.
76 LOG(INFO) << type << " " << message;
79 MessageResponse LoadTestMessageHandler::HandleStructuredMessage(
80 const std::string& type,
81 base::DictionaryValue* msg) {
84 if (!msg->GetString("message", &message))
85 return MissingField(type, "message");
88 } else if (type == "Shutdown") {
90 if (!msg->GetString("message", &message))
91 return MissingField(type, "message");
92 if (!msg->GetBoolean("passed", &test_passed_))
93 return MissingField(type, "passed");
94 Log("SHUTDOWN", message);
97 return InternalError("Unknown message type: " + type);
101 // A message handler for nacl_integration tests ported to be browser_tests.
102 // nacl_integration tests report to their test jig using a series of RPC calls
103 // that are encoded as URL requests. When these tests run as browser_tests,
104 // they make the same RPC requests, but use the automation channel instead of
105 // URL requests. This message handler decodes and responds to these requests.
106 class NaClIntegrationMessageHandler : public StructuredMessageHandler {
108 NaClIntegrationMessageHandler();
110 void Log(const std::string& message);
112 virtual MessageResponse HandleStructuredMessage(
113 const std::string& type,
114 base::DictionaryValue* msg) OVERRIDE;
116 bool test_passed() const {
123 DISALLOW_COPY_AND_ASSIGN(NaClIntegrationMessageHandler);
126 NaClIntegrationMessageHandler::NaClIntegrationMessageHandler()
127 : test_passed_(false) {
130 void NaClIntegrationMessageHandler::Log(const std::string& message) {
131 // TODO(ncbray) better logging.
132 LOG(INFO) << "|||| " << message;
135 MessageResponse NaClIntegrationMessageHandler::HandleStructuredMessage(
136 const std::string& type,
137 base::DictionaryValue* msg) {
138 if (type == "TestLog") {
140 if (!msg->GetString("message", &message))
141 return MissingField(type, "message");
144 } else if (type == "Shutdown") {
146 if (!msg->GetString("message", &message))
147 return MissingField(type, "message");
148 if (!msg->GetBoolean("passed", &test_passed_))
149 return MissingField(type, "passed");
152 } else if (type == "Ping") {
154 } else if (type == "JavaScriptIsAlive") {
157 return InternalError("Unknown message type: " + type);
161 // NaCl browser tests serve files out of the build directory because nexes and
162 // pexes are artifacts of the build. To keep things tidy, all test data is kept
163 // in a subdirectory. Several variants of a test may be run, for example when
164 // linked against newlib and when linked against glibc. These variants are kept
165 // in different subdirectories. For example, the build directory will look
166 // something like this on Linux:
173 static bool GetNaClVariantRoot(const base::FilePath::StringType& variant,
174 base::FilePath* document_root) {
175 if (!ui_test_utils::GetRelativeBuildDirectory(document_root))
177 *document_root = document_root->Append(FILE_PATH_LITERAL("nacl_test_data"));
178 *document_root = document_root->Append(variant);
182 static void AddPnaclParm(const base::FilePath::StringType& url,
183 base::FilePath::StringType* url_with_parm) {
184 if (url.find(FILE_PATH_LITERAL("?")) == base::FilePath::StringType::npos) {
185 *url_with_parm = url + FILE_PATH_LITERAL("?pnacl=1");
187 *url_with_parm = url + FILE_PATH_LITERAL("&pnacl=1");
191 NaClBrowserTestBase::NaClBrowserTestBase() {
194 NaClBrowserTestBase::~NaClBrowserTestBase() {
197 void NaClBrowserTestBase::SetUpCommandLine(base::CommandLine* command_line) {
198 command_line->AppendSwitch(switches::kEnableNaCl);
201 void NaClBrowserTestBase::SetUpOnMainThread() {
202 ASSERT_TRUE(StartTestServer()) << "Cannot start test server.";
205 bool NaClBrowserTestBase::GetDocumentRoot(base::FilePath* document_root) {
206 return GetNaClVariantRoot(Variant(), document_root);
209 bool NaClBrowserTestBase::IsAPnaclTest() {
213 GURL NaClBrowserTestBase::TestURL(
214 const base::FilePath::StringType& url_fragment) {
215 base::FilePath expanded_url = base::FilePath(FILE_PATH_LITERAL("files"));
216 expanded_url = expanded_url.Append(url_fragment);
217 return test_server_->GetURL(expanded_url.MaybeAsASCII());
220 bool NaClBrowserTestBase::RunJavascriptTest(
222 content::TestMessageHandler* handler) {
223 content::JavascriptTestObserver observer(
224 browser()->tab_strip_model()->GetActiveWebContents(),
226 ui_test_utils::NavigateToURL(browser(), url);
227 return observer.Run();
230 void NaClBrowserTestBase::RunLoadTest(
231 const base::FilePath::StringType& test_file) {
232 LoadTestMessageHandler handler;
233 base::FilePath::StringType test_file_with_pnacl = test_file;
234 if (IsAPnaclTest()) {
235 AddPnaclParm(test_file, &test_file_with_pnacl);
237 base::FilePath::StringType test_file_with_both = test_file_with_pnacl;
238 bool ok = RunJavascriptTest(TestURL(test_file_with_both), &handler);
239 ASSERT_TRUE(ok) << handler.error_message();
240 ASSERT_TRUE(handler.test_passed()) << "Test failed.";
243 void NaClBrowserTestBase::RunNaClIntegrationTest(
244 const base::FilePath::StringType& url_fragment, bool full_url) {
245 NaClIntegrationMessageHandler handler;
246 base::FilePath::StringType url_fragment_with_pnacl = url_fragment;
247 if (IsAPnaclTest()) {
248 AddPnaclParm(url_fragment, &url_fragment_with_pnacl);
250 base::FilePath::StringType url_fragment_with_both = url_fragment_with_pnacl;
251 bool ok = RunJavascriptTest(full_url
252 ? GURL(url_fragment_with_both)
253 : TestURL(url_fragment_with_both),
255 ASSERT_TRUE(ok) << handler.error_message();
256 ASSERT_TRUE(handler.test_passed()) << "Test failed.";
259 bool NaClBrowserTestBase::StartTestServer() {
260 // Launch the web server.
261 base::FilePath document_root;
262 if (!GetDocumentRoot(&document_root))
264 test_server_.reset(new net::SpawnedTestServer(
265 net::SpawnedTestServer::TYPE_HTTP,
266 net::SpawnedTestServer::kLocalhost,
268 return test_server_->Start();
271 base::FilePath::StringType NaClBrowserTestNewlib::Variant() {
272 return FILE_PATH_LITERAL("newlib");
275 base::FilePath::StringType NaClBrowserTestGLibc::Variant() {
276 return FILE_PATH_LITERAL("glibc");
279 base::FilePath::StringType NaClBrowserTestPnacl::Variant() {
280 return FILE_PATH_LITERAL("pnacl");
283 bool NaClBrowserTestPnacl::IsAPnaclTest() {
287 base::FilePath::StringType NaClBrowserTestNonSfiMode::Variant() {
288 return FILE_PATH_LITERAL("libc-free");
291 void NaClBrowserTestNonSfiMode::SetUpCommandLine(
292 base::CommandLine* command_line) {
293 NaClBrowserTestBase::SetUpCommandLine(command_line);
294 command_line->AppendSwitch(switches::kEnableNaClNonSfiMode);
297 base::FilePath::StringType NaClBrowserTestStatic::Variant() {
298 return FILE_PATH_LITERAL("static");
301 bool NaClBrowserTestStatic::GetDocumentRoot(base::FilePath* document_root) {
302 *document_root = base::FilePath(FILE_PATH_LITERAL("chrome/test/data/nacl"));
306 base::FilePath::StringType NaClBrowserTestPnaclNonSfi::Variant() {
307 return FILE_PATH_LITERAL("nonsfi");
310 void NaClBrowserTestPnaclNonSfi::SetUpCommandLine(
311 base::CommandLine* command_line) {
312 NaClBrowserTestBase::SetUpCommandLine(command_line);
313 command_line->AppendSwitch(switches::kEnableNaClNonSfiMode);
316 void NaClBrowserTestNewlibExtension::SetUpCommandLine(
317 CommandLine* command_line) {
318 NaClBrowserTestBase::SetUpCommandLine(command_line);
319 base::FilePath src_root;
320 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_root));
322 // Extension-based tests should specialize the GetDocumentRoot() / Variant()
323 // to point at the isolated the test extension directory.
324 // Otherwise, multiple NaCl extensions tests will end up sharing the
325 // same directory when loading the extension files.
326 base::FilePath document_root;
327 ASSERT_TRUE(GetDocumentRoot(&document_root));
329 // Document root is relative to source root, and source root may not be CWD.
330 command_line->AppendSwitchPath(switches::kLoadExtension,
331 src_root.Append(document_root));
334 void NaClBrowserTestGLibcExtension::SetUpCommandLine(
335 CommandLine* command_line) {
336 NaClBrowserTestBase::SetUpCommandLine(command_line);
337 base::FilePath src_root;
338 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_root));
340 // Extension-based tests should specialize the GetDocumentRoot() / Variant()
341 // to point at the isolated the test extension directory.
342 // Otherwise, multiple NaCl extensions tests will end up sharing the
343 // same directory when loading the extension files.
344 base::FilePath document_root;
345 ASSERT_TRUE(GetDocumentRoot(&document_root));
347 // Document root is relative to source root, and source root may not be CWD.
348 command_line->AppendSwitchPath(switches::kLoadExtension,
349 src_root.Append(document_root));