nacl_browsertest_util.cc revision 558790d6acca3451cf3a6b497803a5f07d0bec58
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/environment.h"
10#include "base/json/json_reader.h"
11#include "base/path_service.h"
12#include "base/values.h"
13#include "chrome/browser/ui/browser.h"
14#include "chrome/browser/ui/tabs/tab_strip_model.h"
15#include "chrome/common/chrome_paths.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/test/base/ui_test_utils.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
23typedef TestMessageHandler::MessageResponse MessageResponse;
24
25MessageResponse StructuredMessageHandler::HandleMessage(
26    const std::string& json) {
27  scoped_ptr<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  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
57MessageResponse StructuredMessageHandler::MissingField(
58    const std::string& type,
59    const std::string& field) {
60  return InternalError(type + " message did not have field: " + field);
61}
62
63MessageResponse StructuredMessageHandler::InternalError(
64    const std::string& reason) {
65  SetError(reason);
66  return DONE;
67}
68
69LoadTestMessageHandler::LoadTestMessageHandler()
70    : test_passed_(false) {
71}
72
73void LoadTestMessageHandler::Log(const std::string& type,
74                                 const std::string& message) {
75  // TODO(ncbray) better logging.
76  LOG(INFO) << type << " " << message;
77}
78
79MessageResponse LoadTestMessageHandler::HandleStructuredMessage(
80   const std::string& type,
81   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.
106class 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
116  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
126NaClIntegrationMessageHandler::NaClIntegrationMessageHandler()
127    : test_passed_(false) {
128}
129
130void NaClIntegrationMessageHandler::Log(const std::string& message) {
131  // TODO(ncbray) better logging.
132  LOG(INFO) << "|||| " << message;
133}
134
135MessageResponse NaClIntegrationMessageHandler::HandleStructuredMessage(
136    const std::string& type,
137    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/
172static bool GetNaClVariantRoot(const base::FilePath::StringType& variant,
173                               base::FilePath* document_root) {
174  if (!ui_test_utils::GetRelativeBuildDirectory(document_root))
175    return false;
176  *document_root = document_root->Append(FILE_PATH_LITERAL("nacl_test_data"));
177  *document_root = document_root->Append(variant);
178  return true;
179}
180
181static 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");
185  } else {
186    *url_with_parm = url + FILE_PATH_LITERAL("&pnacl=1");
187  }
188}
189
190static 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");
194  } else {
195    *url_with_parm = url + FILE_PATH_LITERAL("&pnacl_disabled=1");
196  }
197}
198
199NaClBrowserTestBase::NaClBrowserTestBase() {
200}
201
202NaClBrowserTestBase::~NaClBrowserTestBase() {
203}
204
205void NaClBrowserTestBase::SetUpCommandLine(CommandLine* command_line) {
206  command_line->AppendSwitch(switches::kNoFirstRun);
207  command_line->AppendSwitch(switches::kEnableNaCl);
208}
209
210void NaClBrowserTestBase::SetUpInProcessBrowserTestFixture() {
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
219bool NaClBrowserTestBase::GetDocumentRoot(base::FilePath* document_root) {
220  return GetNaClVariantRoot(Variant(), document_root);
221}
222
223bool NaClBrowserTestBase::IsAPnaclTest() {
224  return false;
225}
226
227bool NaClBrowserTestBase::IsPnaclDisabled() {
228  return false;
229}
230
231GURL 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
238bool NaClBrowserTestBase::RunJavascriptTest(const GURL& url,
239                                            TestMessageHandler* handler) {
240  JavascriptTestObserver observer(
241      browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
242      handler);
243  ui_test_utils::NavigateToURL(browser(), url);
244  return observer.Run();
245}
246
247void NaClBrowserTestBase::RunLoadTest(
248    const base::FilePath::StringType& test_file) {
249  LoadTestMessageHandler handler;
250  base::FilePath::StringType test_file_with_pnacl = test_file;
251  if (IsAPnaclTest()) {
252    AddPnaclParm(test_file, &test_file_with_pnacl);
253  }
254  base::FilePath::StringType test_file_with_both = test_file_with_pnacl;
255  if (IsPnaclDisabled()) {
256    AddPnaclDisabledParm(test_file_with_pnacl, &test_file_with_both);
257  }
258  bool ok = RunJavascriptTest(TestURL(test_file_with_both), &handler);
259  ASSERT_TRUE(ok) << handler.error_message();
260  ASSERT_TRUE(handler.test_passed()) << "Test failed.";
261}
262
263void NaClBrowserTestBase::RunNaClIntegrationTest(
264    const base::FilePath::StringType& url_fragment) {
265  NaClIntegrationMessageHandler handler;
266  base::FilePath::StringType url_fragment_with_pnacl = url_fragment;
267  if (IsAPnaclTest()) {
268    AddPnaclParm(url_fragment, &url_fragment_with_pnacl);
269  }
270  base::FilePath::StringType url_fragment_with_both = url_fragment_with_pnacl;
271  if (IsPnaclDisabled()) {
272    AddPnaclDisabledParm(url_fragment_with_pnacl, &url_fragment_with_both);
273  }
274  bool ok = RunJavascriptTest(TestURL(url_fragment_with_both), &handler);
275  ASSERT_TRUE(ok) << handler.error_message();
276  ASSERT_TRUE(handler.test_passed()) << "Test failed.";
277}
278
279bool NaClBrowserTestBase::StartTestServer() {
280  // Launch the web server.
281  base::FilePath document_root;
282  if (!GetDocumentRoot(&document_root))
283    return false;
284  test_server_.reset(new net::SpawnedTestServer(
285                         net::SpawnedTestServer::TYPE_HTTP,
286                         net::SpawnedTestServer::kLocalhost,
287                         document_root));
288  return test_server_->Start();
289}
290
291base::FilePath::StringType NaClBrowserTestNewlib::Variant() {
292  return FILE_PATH_LITERAL("newlib");
293}
294
295base::FilePath::StringType NaClBrowserTestGLibc::Variant() {
296  return FILE_PATH_LITERAL("glibc");
297}
298
299base::FilePath::StringType NaClBrowserTestPnacl::Variant() {
300  return FILE_PATH_LITERAL("pnacl");
301}
302
303bool NaClBrowserTestPnacl::IsAPnaclTest() {
304  return true;
305}
306
307base::FilePath::StringType NaClBrowserTestPnaclDisabled::Variant() {
308  return FILE_PATH_LITERAL("pnacl");
309}
310
311bool NaClBrowserTestPnaclDisabled::IsAPnaclTest() {
312  return true;
313}
314
315bool NaClBrowserTestPnaclDisabled::IsPnaclDisabled() {
316  return true;
317}
318
319void NaClBrowserTestPnaclDisabled::SetUpCommandLine(CommandLine* command_line) {
320  NaClBrowserTestBase::SetUpCommandLine(command_line);
321  command_line->AppendSwitch(switches::kDisablePnacl);
322}
323
324NaClBrowserTestPnaclWithNewCache::NaClBrowserTestPnaclWithNewCache() {
325  scoped_ptr<base::Environment> env(base::Environment::Create());
326  env->SetVar("PNACL_USE_NEW_CACHE", "true");
327}
328
329base::FilePath::StringType NaClBrowserTestStatic::Variant() {
330  return FILE_PATH_LITERAL("static");
331}
332
333bool NaClBrowserTestStatic::GetDocumentRoot(base::FilePath* document_root) {
334  *document_root = base::FilePath(FILE_PATH_LITERAL("chrome/test/data/nacl"));
335  return true;
336}
337