Home | History | Annotate | Download | only in nacl
      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 "content/public/browser/plugin_service.h"
     18 #include "content/public/browser/web_contents.h"
     19 #include "content/public/common/webplugininfo.h"
     20 #include "net/base/net_util.h"
     21 
     22 typedef TestMessageHandler::MessageResponse MessageResponse;
     23 
     24 MessageResponse StructuredMessageHandler::HandleMessage(
     25     const std::string& json) {
     26   scoped_ptr<Value> value;
     27   base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
     28   // Automation messages are stringified before they are sent because the
     29   // automation channel cannot handle arbitrary objects.  This means we
     30   // need to decode the json twice to get the original message.
     31   value.reset(reader.ReadToValue(json));
     32   if (!value.get())
     33     return InternalError("Could parse automation JSON: " + json +
     34                          " because " + reader.GetErrorMessage());
     35 
     36   std::string temp;
     37   if (!value->GetAsString(&temp))
     38     return InternalError("Message was not a string: " + json);
     39 
     40   value.reset(reader.ReadToValue(temp));
     41   if (!value.get())
     42     return InternalError("Could not parse message JSON: " + temp +
     43                          " because " + reader.GetErrorMessage());
     44 
     45   DictionaryValue* msg;
     46   if (!value->GetAsDictionary(&msg))
     47     return InternalError("Message was not an object: " + temp);
     48 
     49   std::string type;
     50   if (!msg->GetString("type", &type))
     51     return MissingField("unknown", "type");
     52 
     53   return HandleStructuredMessage(type, msg);
     54 }
     55 
     56 MessageResponse StructuredMessageHandler::MissingField(
     57     const std::string& type,
     58     const std::string& field) {
     59   return InternalError(type + " message did not have field: " + field);
     60 }
     61 
     62 MessageResponse StructuredMessageHandler::InternalError(
     63     const std::string& reason) {
     64   SetError(reason);
     65   return DONE;
     66 }
     67 
     68 LoadTestMessageHandler::LoadTestMessageHandler()
     69     : test_passed_(false) {
     70 }
     71 
     72 void LoadTestMessageHandler::Log(const std::string& type,
     73                                  const std::string& message) {
     74   // TODO(ncbray) better logging.
     75   LOG(INFO) << type << " " << message;
     76 }
     77 
     78 MessageResponse LoadTestMessageHandler::HandleStructuredMessage(
     79    const std::string& type,
     80    DictionaryValue* msg) {
     81   if (type == "Log") {
     82     std::string message;
     83     if (!msg->GetString("message", &message))
     84       return MissingField(type, "message");
     85     Log("LOG", message);
     86     return CONTINUE;
     87   } else if (type == "Shutdown") {
     88     std::string message;
     89     if (!msg->GetString("message", &message))
     90       return MissingField(type, "message");
     91     if (!msg->GetBoolean("passed", &test_passed_))
     92       return MissingField(type, "passed");
     93     Log("SHUTDOWN", message);
     94     return DONE;
     95   } else {
     96     return InternalError("Unknown message type: " + type);
     97   }
     98 }
     99 
    100 // A message handler for nacl_integration tests ported to be browser_tests.
    101 // nacl_integration tests report to their test jig using a series of RPC calls
    102 // that are encoded as URL requests. When these tests run as browser_tests,
    103 // they make the same RPC requests, but use the automation channel instead of
    104 // URL requests. This message handler decodes and responds to these requests.
    105 class NaClIntegrationMessageHandler : public StructuredMessageHandler {
    106  public:
    107   NaClIntegrationMessageHandler();
    108 
    109   void Log(const std::string& message);
    110 
    111   virtual MessageResponse HandleStructuredMessage(
    112       const std::string& type,
    113       base::DictionaryValue* msg) OVERRIDE;
    114 
    115   bool test_passed() const {
    116     return test_passed_;
    117   }
    118 
    119  private:
    120   bool test_passed_;
    121 
    122   DISALLOW_COPY_AND_ASSIGN(NaClIntegrationMessageHandler);
    123 };
    124 
    125 NaClIntegrationMessageHandler::NaClIntegrationMessageHandler()
    126     : test_passed_(false) {
    127 }
    128 
    129 void NaClIntegrationMessageHandler::Log(const std::string& message) {
    130   // TODO(ncbray) better logging.
    131   LOG(INFO) << "|||| " << message;
    132 }
    133 
    134 MessageResponse NaClIntegrationMessageHandler::HandleStructuredMessage(
    135     const std::string& type,
    136     DictionaryValue* msg) {
    137   if (type == "TestLog") {
    138     std::string message;
    139     if (!msg->GetString("message", &message))
    140       return MissingField(type, "message");
    141     Log(message);
    142     return CONTINUE;
    143   } else if (type == "Shutdown") {
    144     std::string message;
    145     if (!msg->GetString("message", &message))
    146       return MissingField(type, "message");
    147     if (!msg->GetBoolean("passed", &test_passed_))
    148       return MissingField(type, "passed");
    149     Log(message);
    150     return DONE;
    151   } else if (type == "Ping") {
    152     return CONTINUE;
    153   } else if (type == "JavaScriptIsAlive") {
    154     return CONTINUE;
    155   } else {
    156     return InternalError("Unknown message type: " + type);
    157   }
    158 }
    159 
    160 // NaCl browser tests serve files out of the build directory because nexes and
    161 // pexes are artifacts of the build.  To keep things tidy, all test data is kept
    162 // in a subdirectory.  Several variants of a test may be run, for example when
    163 // linked against newlib and when linked against glibc.  These variants are kept
    164 // in different subdirectories.  For example, the build directory will look
    165 // something like this on Linux:
    166 // out/
    167 //     Release/
    168 //             nacl_test_data/
    169 //                            newlib/
    170 //                            glibc/
    171 static bool GetNaClVariantRoot(const base::FilePath::StringType& variant,
    172                                base::FilePath* document_root) {
    173   if (!ui_test_utils::GetRelativeBuildDirectory(document_root))
    174     return false;
    175   *document_root = document_root->Append(FILE_PATH_LITERAL("nacl_test_data"));
    176   *document_root = document_root->Append(variant);
    177   return true;
    178 }
    179 
    180 static void AddPnaclParm(const base::FilePath::StringType& url,
    181                          base::FilePath::StringType* url_with_parm) {
    182   if (url.find(FILE_PATH_LITERAL("?")) == base::FilePath::StringType::npos) {
    183     *url_with_parm = url + FILE_PATH_LITERAL("?pnacl=1");
    184   } else {
    185     *url_with_parm = url + FILE_PATH_LITERAL("&pnacl=1");
    186   }
    187 }
    188 
    189 static void AddPnaclDisabledParm(const base::FilePath::StringType& url,
    190                                  base::FilePath::StringType* url_with_parm) {
    191   if (url.find(FILE_PATH_LITERAL("?")) == base::FilePath::StringType::npos) {
    192     *url_with_parm = url + FILE_PATH_LITERAL("?pnacl_disabled=1");
    193   } else {
    194     *url_with_parm = url + FILE_PATH_LITERAL("&pnacl_disabled=1");
    195   }
    196 }
    197 
    198 NaClBrowserTestBase::NaClBrowserTestBase() {
    199 }
    200 
    201 NaClBrowserTestBase::~NaClBrowserTestBase() {
    202 }
    203 
    204 void NaClBrowserTestBase::SetUpCommandLine(CommandLine* command_line) {
    205   command_line->AppendSwitch(switches::kEnableNaCl);
    206 }
    207 
    208 void NaClBrowserTestBase::SetUpInProcessBrowserTestFixture() {
    209   // Sanity check.
    210   base::FilePath plugin_lib;
    211   ASSERT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib));
    212   ASSERT_TRUE(base::PathExists(plugin_lib)) << plugin_lib.value();
    213 
    214   ASSERT_TRUE(StartTestServer()) << "Cannot start test server.";
    215 }
    216 
    217 bool NaClBrowserTestBase::GetDocumentRoot(base::FilePath* document_root) {
    218   return GetNaClVariantRoot(Variant(), document_root);
    219 }
    220 
    221 bool NaClBrowserTestBase::IsAPnaclTest() {
    222   return false;
    223 }
    224 
    225 bool NaClBrowserTestBase::IsPnaclDisabled() {
    226   return false;
    227 }
    228 
    229 GURL NaClBrowserTestBase::TestURL(
    230     const base::FilePath::StringType& url_fragment) {
    231   base::FilePath expanded_url = base::FilePath(FILE_PATH_LITERAL("files"));
    232   expanded_url = expanded_url.Append(url_fragment);
    233   return test_server_->GetURL(expanded_url.MaybeAsASCII());
    234 }
    235 
    236 bool NaClBrowserTestBase::RunJavascriptTest(const GURL& url,
    237                                             TestMessageHandler* handler) {
    238   JavascriptTestObserver observer(
    239       browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
    240       handler);
    241   ui_test_utils::NavigateToURL(browser(), url);
    242   return observer.Run();
    243 }
    244 
    245 void NaClBrowserTestBase::RunLoadTest(
    246     const base::FilePath::StringType& test_file) {
    247   LoadTestMessageHandler handler;
    248   base::FilePath::StringType test_file_with_pnacl = test_file;
    249   if (IsAPnaclTest()) {
    250     AddPnaclParm(test_file, &test_file_with_pnacl);
    251   }
    252   base::FilePath::StringType test_file_with_both = test_file_with_pnacl;
    253   if (IsPnaclDisabled()) {
    254     AddPnaclDisabledParm(test_file_with_pnacl, &test_file_with_both);
    255   }
    256   bool ok = RunJavascriptTest(TestURL(test_file_with_both), &handler);
    257   ASSERT_TRUE(ok) << handler.error_message();
    258   ASSERT_TRUE(handler.test_passed()) << "Test failed.";
    259 }
    260 
    261 void NaClBrowserTestBase::RunNaClIntegrationTest(
    262     const base::FilePath::StringType& url_fragment) {
    263   NaClIntegrationMessageHandler handler;
    264   base::FilePath::StringType url_fragment_with_pnacl = url_fragment;
    265   if (IsAPnaclTest()) {
    266     AddPnaclParm(url_fragment, &url_fragment_with_pnacl);
    267   }
    268   base::FilePath::StringType url_fragment_with_both = url_fragment_with_pnacl;
    269   if (IsPnaclDisabled()) {
    270     AddPnaclDisabledParm(url_fragment_with_pnacl, &url_fragment_with_both);
    271   }
    272   bool ok = RunJavascriptTest(TestURL(url_fragment_with_both), &handler);
    273   ASSERT_TRUE(ok) << handler.error_message();
    274   ASSERT_TRUE(handler.test_passed()) << "Test failed.";
    275 }
    276 
    277 bool NaClBrowserTestBase::StartTestServer() {
    278   // Launch the web server.
    279   base::FilePath document_root;
    280   if (!GetDocumentRoot(&document_root))
    281     return false;
    282   test_server_.reset(new net::SpawnedTestServer(
    283                          net::SpawnedTestServer::TYPE_HTTP,
    284                          net::SpawnedTestServer::kLocalhost,
    285                          document_root));
    286   return test_server_->Start();
    287 }
    288 
    289 base::FilePath::StringType NaClBrowserTestNewlib::Variant() {
    290   return FILE_PATH_LITERAL("newlib");
    291 }
    292 
    293 base::FilePath::StringType NaClBrowserTestGLibc::Variant() {
    294   return FILE_PATH_LITERAL("glibc");
    295 }
    296 
    297 base::FilePath::StringType NaClBrowserTestPnacl::Variant() {
    298   return FILE_PATH_LITERAL("pnacl");
    299 }
    300 
    301 bool NaClBrowserTestPnacl::IsAPnaclTest() {
    302   return true;
    303 }
    304 
    305 base::FilePath::StringType NaClBrowserTestPnaclDisabled::Variant() {
    306   return FILE_PATH_LITERAL("pnacl");
    307 }
    308 
    309 bool NaClBrowserTestPnaclDisabled::IsAPnaclTest() {
    310   return true;
    311 }
    312 
    313 bool NaClBrowserTestPnaclDisabled::IsPnaclDisabled() {
    314   return true;
    315 }
    316 void NaClBrowserTestPnaclDisabled::SetUpCommandLine(CommandLine* command_line) {
    317   NaClBrowserTestBase::SetUpCommandLine(command_line);
    318   command_line->AppendSwitch(switches::kDisablePnacl);
    319 }
    320 
    321 base::FilePath::StringType NaClBrowserTestStatic::Variant() {
    322   return FILE_PATH_LITERAL("static");
    323 }
    324 
    325 bool NaClBrowserTestStatic::GetDocumentRoot(base::FilePath* document_root) {
    326   *document_root = base::FilePath(FILE_PATH_LITERAL("chrome/test/data/nacl"));
    327   return true;
    328 }
    329