• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/test/nacl/nacl_browsertest_util.h"
6 
7 #include <stdlib.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"
22 
23 typedef content::TestMessageHandler::MessageResponse MessageResponse;
24 
HandleMessage(const std::string & json)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));
33   if (!value.get())
34     return InternalError("Could parse automation JSON: " + json +
35                          " because " + reader.GetErrorMessage());
36 
37   std::string temp;
38   if (!value->GetAsString(&temp))
39     return InternalError("Message was not a string: " + json);
40 
41   value.reset(reader.ReadToValue(temp));
42   if (!value.get())
43     return InternalError("Could not parse message JSON: " + temp +
44                          " because " + reader.GetErrorMessage());
45 
46   base::DictionaryValue* msg;
47   if (!value->GetAsDictionary(&msg))
48     return InternalError("Message was not an object: " + temp);
49 
50   std::string type;
51   if (!msg->GetString("type", &type))
52     return MissingField("unknown", "type");
53 
54   return HandleStructuredMessage(type, msg);
55 }
56 
MissingField(const std::string & type,const std::string & field)57 MessageResponse StructuredMessageHandler::MissingField(
58     const std::string& type,
59     const std::string& field) {
60   return InternalError(type + " message did not have field: " + field);
61 }
62 
InternalError(const std::string & reason)63 MessageResponse StructuredMessageHandler::InternalError(
64     const std::string& reason) {
65   SetError(reason);
66   return DONE;
67 }
68 
LoadTestMessageHandler()69 LoadTestMessageHandler::LoadTestMessageHandler()
70     : test_passed_(false) {
71 }
72 
Log(const std::string & type,const std::string & message)73 void LoadTestMessageHandler::Log(const std::string& type,
74                                  const std::string& message) {
75   // TODO(ncbray) better logging.
76   LOG(INFO) << type << " " << message;
77 }
78 
HandleStructuredMessage(const std::string & type,base::DictionaryValue * msg)79 MessageResponse LoadTestMessageHandler::HandleStructuredMessage(
80    const std::string& type,
81    base::DictionaryValue* msg) {
82   if (type == "Log") {
83     std::string message;
84     if (!msg->GetString("message", &message))
85       return MissingField(type, "message");
86     Log("LOG", message);
87     return CONTINUE;
88   } else if (type == "Shutdown") {
89     std::string message;
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);
95     return DONE;
96   } else {
97     return InternalError("Unknown message type: " + type);
98   }
99 }
100 
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 {
107  public:
108   NaClIntegrationMessageHandler();
109 
110   void Log(const std::string& message);
111 
112   virtual MessageResponse HandleStructuredMessage(
113       const std::string& type,
114       base::DictionaryValue* msg) OVERRIDE;
115 
test_passed() const116   bool test_passed() const {
117     return test_passed_;
118   }
119 
120  private:
121   bool test_passed_;
122 
123   DISALLOW_COPY_AND_ASSIGN(NaClIntegrationMessageHandler);
124 };
125 
NaClIntegrationMessageHandler()126 NaClIntegrationMessageHandler::NaClIntegrationMessageHandler()
127     : test_passed_(false) {
128 }
129 
Log(const std::string & message)130 void NaClIntegrationMessageHandler::Log(const std::string& message) {
131   // TODO(ncbray) better logging.
132   LOG(INFO) << "|||| " << message;
133 }
134 
HandleStructuredMessage(const std::string & type,base::DictionaryValue * msg)135 MessageResponse NaClIntegrationMessageHandler::HandleStructuredMessage(
136     const std::string& type,
137     base::DictionaryValue* msg) {
138   if (type == "TestLog") {
139     std::string message;
140     if (!msg->GetString("message", &message))
141       return MissingField(type, "message");
142     Log(message);
143     return CONTINUE;
144   } else if (type == "Shutdown") {
145     std::string message;
146     if (!msg->GetString("message", &message))
147       return MissingField(type, "message");
148     if (!msg->GetBoolean("passed", &test_passed_))
149       return MissingField(type, "passed");
150     Log(message);
151     return DONE;
152   } else if (type == "Ping") {
153     return CONTINUE;
154   } else if (type == "JavaScriptIsAlive") {
155     return CONTINUE;
156   } else {
157     return InternalError("Unknown message type: " + type);
158   }
159 }
160 
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:
167 // out/
168 //     Release/
169 //             nacl_test_data/
170 //                            newlib/
171 //                            glibc/
172 //                            pnacl/
GetNaClVariantRoot(const base::FilePath::StringType & variant,base::FilePath * document_root)173 static bool GetNaClVariantRoot(const base::FilePath::StringType& variant,
174                                base::FilePath* document_root) {
175   if (!ui_test_utils::GetRelativeBuildDirectory(document_root))
176     return false;
177   *document_root = document_root->Append(FILE_PATH_LITERAL("nacl_test_data"));
178   *document_root = document_root->Append(variant);
179   return true;
180 }
181 
AddPnaclParm(const base::FilePath::StringType & url,base::FilePath::StringType * url_with_parm)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");
186   } else {
187     *url_with_parm = url + FILE_PATH_LITERAL("&pnacl=1");
188   }
189 }
190 
AddPnaclDisabledParm(const base::FilePath::StringType & url,base::FilePath::StringType * url_with_parm)191 static void AddPnaclDisabledParm(const base::FilePath::StringType& url,
192                                  base::FilePath::StringType* url_with_parm) {
193   if (url.find(FILE_PATH_LITERAL("?")) == base::FilePath::StringType::npos) {
194     *url_with_parm = url + FILE_PATH_LITERAL("?pnacl_disabled=1");
195   } else {
196     *url_with_parm = url + FILE_PATH_LITERAL("&pnacl_disabled=1");
197   }
198 }
199 
NaClBrowserTestBase()200 NaClBrowserTestBase::NaClBrowserTestBase() {
201 }
202 
~NaClBrowserTestBase()203 NaClBrowserTestBase::~NaClBrowserTestBase() {
204 }
205 
SetUpCommandLine(base::CommandLine * command_line)206 void NaClBrowserTestBase::SetUpCommandLine(base::CommandLine* command_line) {
207   command_line->AppendSwitch(switches::kEnableNaCl);
208 }
209 
SetUpOnMainThread()210 void NaClBrowserTestBase::SetUpOnMainThread() {
211   // Sanity check.
212   base::FilePath plugin_lib;
213   ASSERT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib));
214   ASSERT_TRUE(base::PathExists(plugin_lib)) << plugin_lib.value();
215 
216   ASSERT_TRUE(StartTestServer()) << "Cannot start test server.";
217 }
218 
GetDocumentRoot(base::FilePath * document_root)219 bool NaClBrowserTestBase::GetDocumentRoot(base::FilePath* document_root) {
220   return GetNaClVariantRoot(Variant(), document_root);
221 }
222 
IsAPnaclTest()223 bool NaClBrowserTestBase::IsAPnaclTest() {
224   return false;
225 }
226 
IsPnaclDisabled()227 bool NaClBrowserTestBase::IsPnaclDisabled() {
228   return false;
229 }
230 
TestURL(const base::FilePath::StringType & url_fragment)231 GURL NaClBrowserTestBase::TestURL(
232     const base::FilePath::StringType& url_fragment) {
233   base::FilePath expanded_url = base::FilePath(FILE_PATH_LITERAL("files"));
234   expanded_url = expanded_url.Append(url_fragment);
235   return test_server_->GetURL(expanded_url.MaybeAsASCII());
236 }
237 
RunJavascriptTest(const GURL & url,content::TestMessageHandler * handler)238 bool NaClBrowserTestBase::RunJavascriptTest(
239     const GURL& url,
240     content::TestMessageHandler* handler) {
241   content::JavascriptTestObserver observer(
242       browser()->tab_strip_model()->GetActiveWebContents(),
243       handler);
244   ui_test_utils::NavigateToURL(browser(), url);
245   return observer.Run();
246 }
247 
RunLoadTest(const base::FilePath::StringType & test_file)248 void NaClBrowserTestBase::RunLoadTest(
249     const base::FilePath::StringType& test_file) {
250   LoadTestMessageHandler handler;
251   base::FilePath::StringType test_file_with_pnacl = test_file;
252   if (IsAPnaclTest()) {
253     AddPnaclParm(test_file, &test_file_with_pnacl);
254   }
255   base::FilePath::StringType test_file_with_both = test_file_with_pnacl;
256   if (IsPnaclDisabled()) {
257     AddPnaclDisabledParm(test_file_with_pnacl, &test_file_with_both);
258   }
259   bool ok = RunJavascriptTest(TestURL(test_file_with_both), &handler);
260   ASSERT_TRUE(ok) << handler.error_message();
261   ASSERT_TRUE(handler.test_passed()) << "Test failed.";
262 }
263 
RunNaClIntegrationTest(const base::FilePath::StringType & url_fragment,bool full_url)264 void NaClBrowserTestBase::RunNaClIntegrationTest(
265     const base::FilePath::StringType& url_fragment, bool full_url) {
266   NaClIntegrationMessageHandler handler;
267   base::FilePath::StringType url_fragment_with_pnacl = url_fragment;
268   if (IsAPnaclTest()) {
269     AddPnaclParm(url_fragment, &url_fragment_with_pnacl);
270   }
271   base::FilePath::StringType url_fragment_with_both = url_fragment_with_pnacl;
272   if (IsPnaclDisabled()) {
273     AddPnaclDisabledParm(url_fragment_with_pnacl, &url_fragment_with_both);
274   }
275   bool ok = RunJavascriptTest(full_url
276                               ? GURL(url_fragment_with_both)
277                               : TestURL(url_fragment_with_both),
278                               &handler);
279   ASSERT_TRUE(ok) << handler.error_message();
280   ASSERT_TRUE(handler.test_passed()) << "Test failed.";
281 }
282 
StartTestServer()283 bool NaClBrowserTestBase::StartTestServer() {
284   // Launch the web server.
285   base::FilePath document_root;
286   if (!GetDocumentRoot(&document_root))
287     return false;
288   test_server_.reset(new net::SpawnedTestServer(
289                          net::SpawnedTestServer::TYPE_HTTP,
290                          net::SpawnedTestServer::kLocalhost,
291                          document_root));
292   return test_server_->Start();
293 }
294 
Variant()295 base::FilePath::StringType NaClBrowserTestNewlib::Variant() {
296   return FILE_PATH_LITERAL("newlib");
297 }
298 
Variant()299 base::FilePath::StringType NaClBrowserTestGLibc::Variant() {
300   return FILE_PATH_LITERAL("glibc");
301 }
302 
Variant()303 base::FilePath::StringType NaClBrowserTestPnacl::Variant() {
304   return FILE_PATH_LITERAL("pnacl");
305 }
306 
IsAPnaclTest()307 bool NaClBrowserTestPnacl::IsAPnaclTest() {
308   return true;
309 }
310 
Variant()311 base::FilePath::StringType NaClBrowserTestPnaclDisabled::Variant() {
312   return FILE_PATH_LITERAL("pnacl");
313 }
314 
IsAPnaclTest()315 bool NaClBrowserTestPnaclDisabled::IsAPnaclTest() {
316   return true;
317 }
318 
IsPnaclDisabled()319 bool NaClBrowserTestPnaclDisabled::IsPnaclDisabled() {
320   return true;
321 }
SetUpCommandLine(base::CommandLine * command_line)322 void NaClBrowserTestPnaclDisabled::SetUpCommandLine(
323     base::CommandLine* command_line) {
324   NaClBrowserTestBase::SetUpCommandLine(command_line);
325   command_line->AppendSwitch(switches::kDisablePnacl);
326 }
327 
Variant()328 base::FilePath::StringType NaClBrowserTestNonSfiMode::Variant() {
329   return FILE_PATH_LITERAL("libc-free");
330 }
331 
SetUpCommandLine(base::CommandLine * command_line)332 void NaClBrowserTestNonSfiMode::SetUpCommandLine(
333     base::CommandLine* command_line) {
334   NaClBrowserTestBase::SetUpCommandLine(command_line);
335   command_line->AppendSwitch(switches::kEnableNaClNonSfiMode);
336 }
337 
Variant()338 base::FilePath::StringType NaClBrowserTestStatic::Variant() {
339   return FILE_PATH_LITERAL("static");
340 }
341 
GetDocumentRoot(base::FilePath * document_root)342 bool NaClBrowserTestStatic::GetDocumentRoot(base::FilePath* document_root) {
343   *document_root = base::FilePath(FILE_PATH_LITERAL("chrome/test/data/nacl"));
344   return true;
345 }
346 
Variant()347 base::FilePath::StringType NaClBrowserTestPnaclNonSfi::Variant() {
348   return FILE_PATH_LITERAL("nonsfi");
349 }
350 
SetUpCommandLine(base::CommandLine * command_line)351 void NaClBrowserTestPnaclNonSfi::SetUpCommandLine(
352     base::CommandLine* command_line) {
353   NaClBrowserTestBase::SetUpCommandLine(command_line);
354   command_line->AppendSwitch(switches::kEnableNaClNonSfiMode);
355 }
356 
SetUpCommandLine(CommandLine * command_line)357 void NaClBrowserTestNewlibExtension::SetUpCommandLine(
358     CommandLine* command_line) {
359   NaClBrowserTestBase::SetUpCommandLine(command_line);
360   base::FilePath src_root;
361   ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_root));
362 
363   // Extension-based tests should specialize the GetDocumentRoot() / Variant()
364   // to point at the isolated the test extension directory.
365   // Otherwise, multiple NaCl extensions tests will end up sharing the
366   // same directory when loading the extension files.
367   base::FilePath document_root;
368   ASSERT_TRUE(GetDocumentRoot(&document_root));
369 
370   // Document root is relative to source root, and source root may not be CWD.
371   command_line->AppendSwitchPath(switches::kLoadExtension,
372                                  src_root.Append(document_root));
373 }
374