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 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:
172 static bool GetNaClVariantRoot(const base::FilePath::StringType
& variant
,
173 base::FilePath
* document_root
) {
174 if (!ui_test_utils::GetRelativeBuildDirectory(document_root
))
176 *document_root
= document_root
->Append(FILE_PATH_LITERAL("nacl_test_data"));
177 *document_root
= document_root
->Append(variant
);
181 static void AddPnaclParm(const base::FilePath::StringType
& url
,
182 base::FilePath::StringType
* url_with_parm
) {
183 if (url
.find(FILE_PATH_LITERAL("?")) == base::FilePath::StringType::npos
) {
184 *url_with_parm
= url
+ FILE_PATH_LITERAL("?pnacl=1");
186 *url_with_parm
= url
+ FILE_PATH_LITERAL("&pnacl=1");
190 static void AddPnaclDisabledParm(const base::FilePath::StringType
& url
,
191 base::FilePath::StringType
* url_with_parm
) {
192 if (url
.find(FILE_PATH_LITERAL("?")) == base::FilePath::StringType::npos
) {
193 *url_with_parm
= url
+ FILE_PATH_LITERAL("?pnacl_disabled=1");
195 *url_with_parm
= url
+ FILE_PATH_LITERAL("&pnacl_disabled=1");
199 NaClBrowserTestBase::NaClBrowserTestBase() {
202 NaClBrowserTestBase::~NaClBrowserTestBase() {
205 void NaClBrowserTestBase::SetUpCommandLine(base::CommandLine
* command_line
) {
206 command_line
->AppendSwitch(switches::kEnableNaCl
);
209 void NaClBrowserTestBase::SetUpOnMainThread() {
211 base::FilePath plugin_lib
;
212 ASSERT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN
, &plugin_lib
));
213 ASSERT_TRUE(base::PathExists(plugin_lib
)) << plugin_lib
.value();
215 ASSERT_TRUE(StartTestServer()) << "Cannot start test server.";
218 bool NaClBrowserTestBase::GetDocumentRoot(base::FilePath
* document_root
) {
219 return GetNaClVariantRoot(Variant(), document_root
);
222 bool NaClBrowserTestBase::IsAPnaclTest() {
226 bool NaClBrowserTestBase::IsPnaclDisabled() {
230 GURL
NaClBrowserTestBase::TestURL(
231 const base::FilePath::StringType
& url_fragment
) {
232 base::FilePath expanded_url
= base::FilePath(FILE_PATH_LITERAL("files"));
233 expanded_url
= expanded_url
.Append(url_fragment
);
234 return test_server_
->GetURL(expanded_url
.MaybeAsASCII());
237 bool NaClBrowserTestBase::RunJavascriptTest(const GURL
& url
,
238 TestMessageHandler
* handler
) {
239 JavascriptTestObserver
observer(
240 browser()->tab_strip_model()->GetActiveWebContents(),
242 ui_test_utils::NavigateToURL(browser(), url
);
243 return observer
.Run();
246 void NaClBrowserTestBase::RunLoadTest(
247 const base::FilePath::StringType
& test_file
) {
248 LoadTestMessageHandler handler
;
249 base::FilePath::StringType test_file_with_pnacl
= test_file
;
250 if (IsAPnaclTest()) {
251 AddPnaclParm(test_file
, &test_file_with_pnacl
);
253 base::FilePath::StringType test_file_with_both
= test_file_with_pnacl
;
254 if (IsPnaclDisabled()) {
255 AddPnaclDisabledParm(test_file_with_pnacl
, &test_file_with_both
);
257 bool ok
= RunJavascriptTest(TestURL(test_file_with_both
), &handler
);
258 ASSERT_TRUE(ok
) << handler
.error_message();
259 ASSERT_TRUE(handler
.test_passed()) << "Test failed.";
262 void NaClBrowserTestBase::RunNaClIntegrationTest(
263 const base::FilePath::StringType
& url_fragment
) {
264 NaClIntegrationMessageHandler handler
;
265 base::FilePath::StringType url_fragment_with_pnacl
= url_fragment
;
266 if (IsAPnaclTest()) {
267 AddPnaclParm(url_fragment
, &url_fragment_with_pnacl
);
269 base::FilePath::StringType url_fragment_with_both
= url_fragment_with_pnacl
;
270 if (IsPnaclDisabled()) {
271 AddPnaclDisabledParm(url_fragment_with_pnacl
, &url_fragment_with_both
);
273 bool ok
= RunJavascriptTest(TestURL(url_fragment_with_both
), &handler
);
274 ASSERT_TRUE(ok
) << handler
.error_message();
275 ASSERT_TRUE(handler
.test_passed()) << "Test failed.";
278 bool NaClBrowserTestBase::StartTestServer() {
279 // Launch the web server.
280 base::FilePath document_root
;
281 if (!GetDocumentRoot(&document_root
))
283 test_server_
.reset(new net::SpawnedTestServer(
284 net::SpawnedTestServer::TYPE_HTTP
,
285 net::SpawnedTestServer::kLocalhost
,
287 return test_server_
->Start();
290 base::FilePath::StringType
NaClBrowserTestNewlib::Variant() {
291 return FILE_PATH_LITERAL("newlib");
294 base::FilePath::StringType
NaClBrowserTestGLibc::Variant() {
295 return FILE_PATH_LITERAL("glibc");
298 base::FilePath::StringType
NaClBrowserTestPnacl::Variant() {
299 return FILE_PATH_LITERAL("pnacl");
302 bool NaClBrowserTestPnacl::IsAPnaclTest() {
306 base::FilePath::StringType
NaClBrowserTestPnaclDisabled::Variant() {
307 return FILE_PATH_LITERAL("pnacl");
310 bool NaClBrowserTestPnaclDisabled::IsAPnaclTest() {
314 bool NaClBrowserTestPnaclDisabled::IsPnaclDisabled() {
317 void NaClBrowserTestPnaclDisabled::SetUpCommandLine(
318 base::CommandLine
* command_line
) {
319 NaClBrowserTestBase::SetUpCommandLine(command_line
);
320 command_line
->AppendSwitch(switches::kDisablePnacl
);
323 base::FilePath::StringType
NaClBrowserTestNonSfiMode::Variant() {
324 return FILE_PATH_LITERAL("libc-free");
327 void NaClBrowserTestNonSfiMode::SetUpCommandLine(
328 base::CommandLine
* command_line
) {
329 NaClBrowserTestBase::SetUpCommandLine(command_line
);
330 command_line
->AppendSwitch(switches::kEnableNaClNonSfiMode
);
333 base::FilePath::StringType
NaClBrowserTestStatic::Variant() {
334 return FILE_PATH_LITERAL("static");
337 bool NaClBrowserTestStatic::GetDocumentRoot(base::FilePath
* document_root
) {
338 *document_root
= base::FilePath(FILE_PATH_LITERAL("chrome/test/data/nacl"));
342 base::FilePath::StringType
NaClBrowserTestPnaclNonSfi::Variant() {
343 return FILE_PATH_LITERAL("nonsfi");
346 void NaClBrowserTestPnaclNonSfi::SetUpCommandLine(
347 base::CommandLine
* command_line
) {
348 NaClBrowserTestBase::SetUpCommandLine(command_line
);
349 command_line
->AppendSwitch(switches::kEnableNaClNonSfiMode
);