Index: chrome/test/nacl/nacl_browsertest.cc |
diff --git a/chrome/test/nacl/nacl_browsertest.cc b/chrome/test/nacl/nacl_browsertest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8e5cf3f20f19dfb024e448ce1d0dbaf2786daf43 |
--- /dev/null |
+++ b/chrome/test/nacl/nacl_browsertest.cc |
@@ -0,0 +1,317 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/command_line.h" |
+#include "base/json/json_reader.h" |
+#include "base/path_service.h" |
+#include "base/timer.h" |
+#include "chrome/browser/ui/browser_tabstrip.h" |
+#include "chrome/common/chrome_paths.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "chrome/test/base/in_process_browser_test.h" |
+#include "chrome/test/base/ui_test_utils.h" |
+#include "content/public/browser/dom_operation_notification_details.h" |
+#include "content/public/browser/notification_observer.h" |
+#include "content/public/browser/notification_registrar.h" |
+#include "content/public/browser/notification_types.h" |
+#include "content/public/browser/plugin_service.h" |
+#include "content/public/browser/render_view_host.h" |
+#include "content/public/browser/web_contents.h" |
+#include "net/base/net_util.h" |
+#include "webkit/plugins/webplugininfo.h" |
+ |
+namespace { |
+ |
+// Base class for handling a stream of automation messages produced by a |
+// JavascriptTestObserver. |
+class TestMessageHandler { |
+ public: |
+ enum MessageResponse { |
+ // Reset the timeout and keep running. |
+ CONTINUE, |
+ // Stop runnning. |
+ DONE |
+ }; |
+ |
+ TestMessageHandler() : ok_(true) {} |
+ virtual ~TestMessageHandler() {}; |
+ |
+ virtual MessageResponse HandleMessage(const std::string& json) = 0; |
+ |
+ void SetError(const std::string& message) { |
+ ok_ = false; |
+ error_message_ = message; |
+ } |
+ |
+ bool ok() const { |
+ return ok_; |
+ } |
+ |
+ const std::string& error_message() const { |
+ return error_message_; |
+ } |
+ |
+ private: |
+ bool ok_; |
+ std::string error_message_; |
+}; |
+ |
+// A helper base class that decodes structured automation messages of the form: |
+// {"type": type_name, ...} |
+class StructuredMessageHandler : public TestMessageHandler { |
+ public: |
+ virtual MessageResponse HandleMessage(const std::string& json) OVERRIDE { |
+ scoped_ptr<Value> value; |
+ base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS); |
+ // Automation messages are stringified before they are sent because the |
+ // automation channel cannot handle arbitrary objects. This means we |
+ // need to decode the json twice to get the original message. |
+ value.reset(reader.ReadToValue(json)); |
+ if (!value.get()) |
+ return InternalError("Could parse automation JSON: " + json + |
+ " because " + reader.GetErrorMessage()); |
+ |
+ std::string temp; |
+ if (!value->GetAsString(&temp)) |
+ return InternalError("Message was not a string: " + json); |
+ |
+ value.reset(reader.ReadToValue(temp)); |
+ if (!value.get()) |
+ return InternalError("Could not parse message JSON: " + temp + |
+ " because " + reader.GetErrorMessage()); |
+ |
+ DictionaryValue* msg; |
+ if (!value->GetAsDictionary(&msg)) |
+ return InternalError("Message was not an object: " + temp); |
+ |
+ std::string type; |
+ if (!msg->GetString("type", &type)) |
+ return MissingField("unknown", "type"); |
+ |
+ return HandleStructuredMessage(type, msg); |
+ } |
+ |
+ virtual MessageResponse HandleStructuredMessage(const std::string& type, |
+ DictionaryValue* msg) = 0; |
+ |
+ MessageResponse MissingField(const std::string& type, |
+ const std::string& field) WARN_UNUSED_RESULT { |
+ return InternalError(type + " message did not have field: " + field); |
+ } |
+ |
+ MessageResponse InternalError(const std::string& reason) WARN_UNUSED_RESULT { |
+ SetError(reason); |
+ return DONE; |
+ } |
+}; |
+ |
+// A simple structured message handler for tests that load nexes. |
+class LoadTestMessageHandler : public StructuredMessageHandler { |
+ public: |
+ LoadTestMessageHandler() : test_passed_(false) {} |
+ |
+ void Log(const std::string& type, const std::string& message) { |
+ // TODO(ncbray) better logging. |
+ LOG(INFO) << type << " " << message; |
+ } |
+ |
+ virtual MessageResponse HandleStructuredMessage( |
+ const std::string& type, |
+ DictionaryValue* msg) OVERRIDE { |
+ if (type == "Log") { |
+ std::string message; |
+ if (!msg->GetString("message", &message)) |
+ return MissingField(type, "message"); |
+ Log("LOG", message); |
+ return CONTINUE; |
+ } else if (type == "Shutdown") { |
+ std::string message; |
+ if (!msg->GetString("message", &message)) |
+ return MissingField(type, "message"); |
+ if (!msg->GetBoolean("passed", &test_passed_)) |
+ return MissingField(type, "passed"); |
+ Log("SHUTDOWN", message); |
+ return DONE; |
+ } else { |
+ return InternalError("Unknown message type: " + type); |
+ } |
+ } |
+ |
+ bool test_passed() const { |
+ return test_passed_; |
+ } |
+ |
+ private: |
+ bool test_passed_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(LoadTestMessageHandler); |
+}; |
+ |
+// This class captures a stream of automation messages coming from a Javascript |
+// test and dispatches them to a message handler. |
+// TODO(ncbray) factor out and share with PPAPI tests. |
+class JavascriptTestObserver : public content::NotificationObserver { |
+ public: |
+ JavascriptTestObserver( |
+ content::RenderViewHost* render_view_host, |
+ TestMessageHandler* handler) |
+ : handler_(handler), |
+ running_(false) { |
+ registrar_.Add(this, content::NOTIFICATION_DOM_OPERATION_RESPONSE, |
+ content::Source<content::RenderViewHost>(render_view_host)); |
+ } |
+ |
+ // Pump the message loop until the message handler indicates the Javascript |
+ // test is done running. Return true if the test jig functioned correctly and |
+ // nothing timed out. |
+ bool Run() { |
+ running_ = true; |
+ content::RunMessageLoop(); |
+ running_ = false; |
+ return handler_->ok(); |
+ } |
+ |
+ virtual void Observe( |
+ int type, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) OVERRIDE { |
+ DCHECK(type == content::NOTIFICATION_DOM_OPERATION_RESPONSE); |
+ content::Details<content::DomOperationNotificationDetails> dom_op_details( |
+ details); |
+ // We might receive responses for other script execution, but we only |
+ // care about the test finished message. |
+ TestMessageHandler::MessageResponse response = |
+ handler_->HandleMessage(dom_op_details->json); |
+ |
+ if (response == TestMessageHandler::DONE) { |
+ EndTest(); |
+ } else { |
+ Continue(); |
+ } |
+ } |
+ |
+ private: |
+ void Continue() { |
+ } |
+ |
+ void EndTest() { |
+ if (running_) { |
+ MessageLoopForUI::current()->Quit(); |
+ } |
+ } |
+ |
+ TestMessageHandler* handler_; |
+ bool running_; |
+ content::NotificationRegistrar registrar_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(JavascriptTestObserver); |
+}; |
+ |
+// NaCl browser tests serve files out of the build directory because nexes and |
+// pexes are artifacts of the build. To keep things tidy, all test data is kept |
+// in a subdirectory. Several variants of a test may be run, for example when |
+// linked against newlib and when linked against glibc. These variants are kept |
+// in different subdirectories. For example, the build directory will look |
+// something like this on Linux: |
+// out/ |
+// Release/ |
+// nacl_test_data/ |
+// newlib/ |
+// glibc/ |
+bool GetNaClVariantRoot(const FilePath::StringType& variant, |
+ FilePath* document_root) { |
+ if (!ui_test_utils::GetRelativeBuildDirectory(document_root)) |
+ return false; |
+ *document_root = document_root->Append(FILE_PATH_LITERAL("nacl_test_data")); |
+ *document_root = document_root->Append(variant); |
+ return true; |
+} |
+ |
+class NaClBrowserTestBase : public InProcessBrowserTest { |
+ public: |
+ NaClBrowserTestBase() {} |
+ |
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { |
+ command_line->AppendSwitch(switches::kNoFirstRun); |
+ command_line->AppendSwitch(switches::kEnableNaCl); |
+ } |
+ |
+ virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { |
+ // Sanity check. |
+ FilePath plugin_lib; |
+ ASSERT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib)); |
+ ASSERT_TRUE(file_util::PathExists(plugin_lib)) << plugin_lib.value(); |
+ |
+ ASSERT_TRUE(StartTestServer()) << "Cannot start test server."; |
+ } |
+ |
+ virtual FilePath::StringType Variant() = 0; |
+ |
+ GURL TestURL(const FilePath::StringType& test_file) { |
+ FilePath real_path = test_server_->document_root().Append(test_file); |
+ EXPECT_TRUE(file_util::PathExists(real_path)) << real_path.value(); |
+ |
+ FilePath url_path = FilePath(FILE_PATH_LITERAL("files")); |
+ url_path = url_path.Append(test_file); |
+ return test_server_->GetURL(url_path.MaybeAsASCII()); |
+ } |
+ |
+ bool RunJavascriptTest(const GURL& url, TestMessageHandler* handler) { |
+ JavascriptTestObserver observer( |
+ chrome::GetActiveWebContents(browser())->GetRenderViewHost(), |
+ handler); |
+ ui_test_utils::NavigateToURL(browser(), url); |
+ return observer.Run(); |
+ } |
+ |
+ void RunLoadTest(const FilePath::StringType& test_file) { |
+ LoadTestMessageHandler handler; |
+ bool ok = RunJavascriptTest(TestURL(test_file), &handler); |
+ ASSERT_TRUE(ok) << handler.error_message(); |
+ ASSERT_TRUE(handler.test_passed()) << "Test failed."; |
+ } |
+ |
+ private: |
+ bool StartTestServer() { |
+ // Launch the web server. |
+ FilePath document_root; |
+ if (!GetNaClVariantRoot(Variant(), &document_root)) |
+ return false; |
+ test_server_.reset(new net::TestServer(net::TestServer::TYPE_HTTP, |
+ net::TestServer::kLocalhost, |
+ document_root)); |
+ return test_server_->Start(); |
+ } |
+ |
+ scoped_ptr<net::TestServer> test_server_; |
+}; |
+ |
+class NaClBrowserTestNewlib : public NaClBrowserTestBase { |
+ virtual FilePath::StringType Variant() { |
+ return FILE_PATH_LITERAL("newlib"); |
+ } |
+}; |
+ |
+class NaClBrowserTestGLibc : public NaClBrowserTestBase { |
+ virtual FilePath::StringType Variant() { |
+ return FILE_PATH_LITERAL("glibc"); |
+ } |
+}; |
+ |
+// Disable tests under Linux ASAN. |
+// Linux ASAN doesn't work with NaCl. See: http://crbug.com/104832. |
+// TODO(ncbray) enable after http://codereview.chromium.org/10830009/ lands. |
+#if !(defined(ADDRESS_SANITIZER) && defined(OS_LINUX)) |
+ |
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestNewlib, SimpleLoadTest) { |
+ RunLoadTest(FILE_PATH_LITERAL("nacl_load_test.html")); |
+} |
+ |
+IN_PROC_BROWSER_TEST_F(NaClBrowserTestGLibc, SimpleLoadTest) { |
+ RunLoadTest(FILE_PATH_LITERAL("nacl_load_test.html")); |
+} |
+ |
+#endif // !(defined(ADDRESS_SANITIZER) && defined(OS_LINUX)) |
+ |
+} // namespace anonymous |