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/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 
     23 typedef TestMessageHandler::MessageResponse MessageResponse;
     24 
     25 MessageResponse 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 
     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 
     63 MessageResponse StructuredMessageHandler::InternalError(
     64     const std::string& reason) {
     65   SetError(reason);
     66   return DONE;
     67 }
     68 
     69 LoadTestMessageHandler::LoadTestMessageHandler()
     70     : test_passed_(false) {
     71 }
     72 
     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 
     79 MessageResponse 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.
    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 
    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 
    126 NaClIntegrationMessageHandler::NaClIntegrationMessageHandler()
    127     : test_passed_(false) {
    128 }
    129 
    130 void NaClIntegrationMessageHandler::Log(const std::string& message) {
    131   // TODO(ncbray) better logging.
    132   LOG(INFO) << "|||| " << message;
    133 }
    134 
    135 MessageResponse 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/
    172 static 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 
    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");
    185   } else {
    186     *url_with_parm = url + FILE_PATH_LITERAL("&pnacl=1");
    187   }
    188 }
    189 
    190 NaClBrowserTestBase::NaClBrowserTestBase() {
    191 }
    192 
    193 NaClBrowserTestBase::~NaClBrowserTestBase() {
    194 }
    195 
    196 void NaClBrowserTestBase::SetUpCommandLine(CommandLine* command_line) {
    197   command_line->AppendSwitch(switches::kEnableNaCl);
    198 }
    199 
    200 void NaClBrowserTestBase::SetUpInProcessBrowserTestFixture() {
    201   // Sanity check.
    202   base::FilePath plugin_lib;
    203   ASSERT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib));
    204   ASSERT_TRUE(base::PathExists(plugin_lib)) << plugin_lib.value();
    205 
    206   ASSERT_TRUE(StartTestServer()) << "Cannot start test server.";
    207 }
    208 
    209 bool NaClBrowserTestBase::GetDocumentRoot(base::FilePath* document_root) {
    210   return GetNaClVariantRoot(Variant(), document_root);
    211 }
    212 
    213 bool NaClBrowserTestBase::IsPnacl() {
    214   return false;
    215 }
    216 
    217 GURL NaClBrowserTestBase::TestURL(
    218     const base::FilePath::StringType& url_fragment) {
    219   base::FilePath expanded_url = base::FilePath(FILE_PATH_LITERAL("files"));
    220   expanded_url = expanded_url.Append(url_fragment);
    221   return test_server_->GetURL(expanded_url.MaybeAsASCII());
    222 }
    223 
    224 bool NaClBrowserTestBase::RunJavascriptTest(const GURL& url,
    225                                             TestMessageHandler* handler) {
    226   JavascriptTestObserver observer(
    227       browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
    228       handler);
    229   ui_test_utils::NavigateToURL(browser(), url);
    230   return observer.Run();
    231 }
    232 
    233 void NaClBrowserTestBase::RunLoadTest(
    234     const base::FilePath::StringType& test_file) {
    235   LoadTestMessageHandler handler;
    236   base::FilePath::StringType test_file_with_parm = test_file;
    237   if (IsPnacl()) {
    238     AddPnaclParm(test_file, &test_file_with_parm);
    239   }
    240   bool ok = RunJavascriptTest(TestURL(test_file_with_parm), &handler);
    241   ASSERT_TRUE(ok) << handler.error_message();
    242   ASSERT_TRUE(handler.test_passed()) << "Test failed.";
    243 }
    244 
    245 void NaClBrowserTestBase::RunNaClIntegrationTest(
    246     const base::FilePath::StringType& url_fragment) {
    247   NaClIntegrationMessageHandler handler;
    248   base::FilePath::StringType url_fragment_with_parm = url_fragment;
    249   if (IsPnacl()) {
    250     AddPnaclParm(url_fragment, &url_fragment_with_parm);
    251   }
    252   bool ok = RunJavascriptTest(TestURL(url_fragment_with_parm), &handler);
    253   ASSERT_TRUE(ok) << handler.error_message();
    254   ASSERT_TRUE(handler.test_passed()) << "Test failed.";
    255 }
    256 
    257 bool NaClBrowserTestBase::StartTestServer() {
    258   // Launch the web server.
    259   base::FilePath document_root;
    260   if (!GetDocumentRoot(&document_root))
    261     return false;
    262   test_server_.reset(new net::SpawnedTestServer(
    263                          net::SpawnedTestServer::TYPE_HTTP,
    264                          net::SpawnedTestServer::kLocalhost,
    265                          document_root));
    266   return test_server_->Start();
    267 }
    268 
    269 base::FilePath::StringType NaClBrowserTestNewlib::Variant() {
    270   return FILE_PATH_LITERAL("newlib");
    271 }
    272 
    273 base::FilePath::StringType NaClBrowserTestGLibc::Variant() {
    274   return FILE_PATH_LITERAL("glibc");
    275 }
    276 
    277 base::FilePath::StringType NaClBrowserTestPnacl::Variant() {
    278   return FILE_PATH_LITERAL("pnacl");
    279 }
    280 
    281 bool NaClBrowserTestPnacl::IsPnacl() {
    282   return true;
    283 }
    284 
    285 void NaClBrowserTestPnacl::SetUpCommandLine(CommandLine* command_line) {
    286   NaClBrowserTestBase::SetUpCommandLine(command_line);
    287   command_line->AppendSwitch(switches::kEnablePnacl);
    288 }
    289 
    290 NaClBrowserTestPnaclWithOldCache::NaClBrowserTestPnaclWithOldCache() {
    291   scoped_ptr<base::Environment> env(base::Environment::Create());
    292   env->SetVar("PNACL_USE_OLD_CACHE", "true");
    293 }
    294 
    295 base::FilePath::StringType NaClBrowserTestStatic::Variant() {
    296   return FILE_PATH_LITERAL("static");
    297 }
    298 
    299 bool NaClBrowserTestStatic::GetDocumentRoot(base::FilePath* document_root) {
    300   *document_root = base::FilePath(FILE_PATH_LITERAL("chrome/test/data/nacl"));
    301   return true;
    302 }
    303