OLD | NEW |
(Empty) | |
| 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 "base/command_line.h" |
| 6 #include "base/json/json_reader.h" |
| 7 #include "base/path_service.h" |
| 8 #include "base/timer.h" |
| 9 #include "chrome/browser/ui/browser_tabstrip.h" |
| 10 #include "chrome/common/chrome_paths.h" |
| 11 #include "chrome/common/chrome_switches.h" |
| 12 #include "chrome/test/base/in_process_browser_test.h" |
| 13 #include "chrome/test/base/ui_test_utils.h" |
| 14 #include "content/public/browser/dom_operation_notification_details.h" |
| 15 #include "content/public/browser/notification_observer.h" |
| 16 #include "content/public/browser/notification_registrar.h" |
| 17 #include "content/public/browser/notification_types.h" |
| 18 #include "content/public/browser/plugin_service.h" |
| 19 #include "content/public/browser/render_view_host.h" |
| 20 #include "content/public/browser/web_contents.h" |
| 21 #include "net/base/net_util.h" |
| 22 #include "webkit/plugins/webplugininfo.h" |
| 23 |
| 24 namespace { |
| 25 |
| 26 // Base class for handling a stream of automation messages produced by a |
| 27 // JavascriptTestObserver. |
| 28 class TestMessageHandler { |
| 29 public: |
| 30 enum MessageResponse { |
| 31 // Reset the timeout and keep running. |
| 32 CONTINUE, |
| 33 // Stop runnning. |
| 34 DONE |
| 35 }; |
| 36 |
| 37 TestMessageHandler() : ok_(true) {} |
| 38 virtual ~TestMessageHandler() {}; |
| 39 |
| 40 virtual MessageResponse HandleMessage(const std::string& json) = 0; |
| 41 |
| 42 void SetError(const std::string& message) { |
| 43 ok_ = false; |
| 44 error_message_ = message; |
| 45 } |
| 46 |
| 47 bool ok() const { |
| 48 return ok_; |
| 49 } |
| 50 |
| 51 const std::string& error_message() const { |
| 52 return error_message_; |
| 53 } |
| 54 |
| 55 private: |
| 56 bool ok_; |
| 57 std::string error_message_; |
| 58 }; |
| 59 |
| 60 // A helper base class that decodes structured automation messages of the form: |
| 61 // {"type": type_name, ...} |
| 62 class StructuredMessageHandler : public TestMessageHandler { |
| 63 public: |
| 64 virtual MessageResponse HandleMessage(const std::string& json) OVERRIDE { |
| 65 scoped_ptr<Value> value; |
| 66 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS); |
| 67 // Automation messages are stringified before they are sent because the |
| 68 // automation channel cannot handle arbitrary objects. This means we |
| 69 // need to decode the json twice to get the original message. |
| 70 value.reset(reader.ReadToValue(json)); |
| 71 if (!value.get()) |
| 72 return InternalError("Could parse automation JSON: " + json + |
| 73 " because " + reader.GetErrorMessage()); |
| 74 |
| 75 std::string temp; |
| 76 if (!value->GetAsString(&temp)) |
| 77 return InternalError("Message was not a string: " + json); |
| 78 |
| 79 value.reset(reader.ReadToValue(temp)); |
| 80 if (!value.get()) |
| 81 return InternalError("Could not parse message JSON: " + temp + |
| 82 " because " + reader.GetErrorMessage()); |
| 83 |
| 84 DictionaryValue* msg; |
| 85 if (!value->GetAsDictionary(&msg)) |
| 86 return InternalError("Message was not an object: " + temp); |
| 87 |
| 88 std::string type; |
| 89 if (!msg->GetString("type", &type)) |
| 90 return MissingField("unknown", "type"); |
| 91 |
| 92 return HandleStructuredMessage(type, msg); |
| 93 } |
| 94 |
| 95 virtual MessageResponse HandleStructuredMessage(const std::string& type, |
| 96 DictionaryValue* msg) = 0; |
| 97 |
| 98 MessageResponse MissingField(const std::string& type, |
| 99 const std::string& field) WARN_UNUSED_RESULT { |
| 100 return InternalError(type + " message did not have field: " + field); |
| 101 } |
| 102 |
| 103 MessageResponse InternalError(const std::string& reason) WARN_UNUSED_RESULT { |
| 104 SetError(reason); |
| 105 return DONE; |
| 106 } |
| 107 }; |
| 108 |
| 109 // A simple structured message handler for tests that load nexes. |
| 110 class LoadTestMessageHandler : public StructuredMessageHandler { |
| 111 public: |
| 112 LoadTestMessageHandler() : test_passed_(false) {} |
| 113 |
| 114 void Log(const std::string& type, const std::string& message) { |
| 115 // TODO(ncbray) better logging. |
| 116 LOG(INFO) << type << " " << message; |
| 117 } |
| 118 |
| 119 virtual MessageResponse HandleStructuredMessage( |
| 120 const std::string& type, |
| 121 DictionaryValue* msg) OVERRIDE { |
| 122 if (type == "Log") { |
| 123 std::string message; |
| 124 if (!msg->GetString("message", &message)) |
| 125 return MissingField(type, "message"); |
| 126 Log("LOG", message); |
| 127 return CONTINUE; |
| 128 } else if (type == "Shutdown") { |
| 129 std::string message; |
| 130 if (!msg->GetString("message", &message)) |
| 131 return MissingField(type, "message"); |
| 132 if (!msg->GetBoolean("passed", &test_passed_)) |
| 133 return MissingField(type, "passed"); |
| 134 Log("SHUTDOWN", message); |
| 135 return DONE; |
| 136 } else { |
| 137 return InternalError("Unknown message type: " + type); |
| 138 } |
| 139 } |
| 140 |
| 141 bool test_passed() const { |
| 142 return test_passed_; |
| 143 } |
| 144 |
| 145 private: |
| 146 bool test_passed_; |
| 147 |
| 148 DISALLOW_COPY_AND_ASSIGN(LoadTestMessageHandler); |
| 149 }; |
| 150 |
| 151 // This class captures a stream of automation messages coming from a Javascript |
| 152 // test and dispatches them to a message handler. |
| 153 // TODO(ncbray) factor out and share with PPAPI tests. |
| 154 class JavascriptTestObserver : public content::NotificationObserver { |
| 155 public: |
| 156 JavascriptTestObserver( |
| 157 content::RenderViewHost* render_view_host, |
| 158 TestMessageHandler* handler) |
| 159 : handler_(handler), |
| 160 running_(false) { |
| 161 registrar_.Add(this, content::NOTIFICATION_DOM_OPERATION_RESPONSE, |
| 162 content::Source<content::RenderViewHost>(render_view_host)); |
| 163 } |
| 164 |
| 165 // Pump the message loop until the message handler indicates the Javascript |
| 166 // test is done running. Return true if the test jig functioned correctly and |
| 167 // nothing timed out. |
| 168 bool Run() { |
| 169 running_ = true; |
| 170 content::RunMessageLoop(); |
| 171 running_ = false; |
| 172 return handler_->ok(); |
| 173 } |
| 174 |
| 175 virtual void Observe( |
| 176 int type, |
| 177 const content::NotificationSource& source, |
| 178 const content::NotificationDetails& details) OVERRIDE { |
| 179 DCHECK(type == content::NOTIFICATION_DOM_OPERATION_RESPONSE); |
| 180 content::Details<content::DomOperationNotificationDetails> dom_op_details( |
| 181 details); |
| 182 // We might receive responses for other script execution, but we only |
| 183 // care about the test finished message. |
| 184 TestMessageHandler::MessageResponse response = |
| 185 handler_->HandleMessage(dom_op_details->json); |
| 186 |
| 187 if (response == TestMessageHandler::DONE) { |
| 188 EndTest(); |
| 189 } else { |
| 190 Continue(); |
| 191 } |
| 192 } |
| 193 |
| 194 private: |
| 195 void Continue() { |
| 196 } |
| 197 |
| 198 void EndTest() { |
| 199 if (running_) { |
| 200 MessageLoopForUI::current()->Quit(); |
| 201 } |
| 202 } |
| 203 |
| 204 TestMessageHandler* handler_; |
| 205 bool running_; |
| 206 content::NotificationRegistrar registrar_; |
| 207 |
| 208 DISALLOW_COPY_AND_ASSIGN(JavascriptTestObserver); |
| 209 }; |
| 210 |
| 211 // NaCl browser tests serve files out of the build directory because nexes and |
| 212 // pexes are artifacts of the build. To keep things tidy, all test data is kept |
| 213 // in a subdirectory. Several variants of a test may be run, for example when |
| 214 // linked against newlib and when linked against glibc. These variants are kept |
| 215 // in different subdirectories. For example, the build directory will look |
| 216 // something like this on Linux: |
| 217 // out/ |
| 218 // Release/ |
| 219 // nacl_test_data/ |
| 220 // newlib/ |
| 221 // glibc/ |
| 222 bool GetNaClVariantRoot(const FilePath::StringType& variant, |
| 223 FilePath* document_root) { |
| 224 if (!ui_test_utils::GetRelativeBuildDirectory(document_root)) |
| 225 return false; |
| 226 *document_root = document_root->Append(FILE_PATH_LITERAL("nacl_test_data")); |
| 227 *document_root = document_root->Append(variant); |
| 228 return true; |
| 229 } |
| 230 |
| 231 class NaClBrowserTestBase : public InProcessBrowserTest { |
| 232 public: |
| 233 NaClBrowserTestBase() {} |
| 234 |
| 235 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { |
| 236 command_line->AppendSwitch(switches::kNoFirstRun); |
| 237 command_line->AppendSwitch(switches::kEnableNaCl); |
| 238 } |
| 239 |
| 240 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { |
| 241 // Sanity check. |
| 242 FilePath plugin_lib; |
| 243 ASSERT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib)); |
| 244 ASSERT_TRUE(file_util::PathExists(plugin_lib)) << plugin_lib.value(); |
| 245 |
| 246 ASSERT_TRUE(StartTestServer()) << "Cannot start test server."; |
| 247 } |
| 248 |
| 249 virtual FilePath::StringType Variant() = 0; |
| 250 |
| 251 GURL TestURL(const FilePath::StringType& test_file) { |
| 252 FilePath real_path = test_server_->document_root().Append(test_file); |
| 253 EXPECT_TRUE(file_util::PathExists(real_path)) << real_path.value(); |
| 254 |
| 255 FilePath url_path = FilePath(FILE_PATH_LITERAL("files")); |
| 256 url_path = url_path.Append(test_file); |
| 257 return test_server_->GetURL(url_path.MaybeAsASCII()); |
| 258 } |
| 259 |
| 260 bool RunJavascriptTest(const GURL& url, TestMessageHandler* handler) { |
| 261 JavascriptTestObserver observer( |
| 262 chrome::GetActiveWebContents(browser())->GetRenderViewHost(), |
| 263 handler); |
| 264 ui_test_utils::NavigateToURL(browser(), url); |
| 265 return observer.Run(); |
| 266 } |
| 267 |
| 268 void RunLoadTest(const FilePath::StringType& test_file) { |
| 269 LoadTestMessageHandler handler; |
| 270 bool ok = RunJavascriptTest(TestURL(test_file), &handler); |
| 271 ASSERT_TRUE(ok) << handler.error_message(); |
| 272 ASSERT_TRUE(handler.test_passed()) << "Test failed."; |
| 273 } |
| 274 |
| 275 private: |
| 276 bool StartTestServer() { |
| 277 // Launch the web server. |
| 278 FilePath document_root; |
| 279 if (!GetNaClVariantRoot(Variant(), &document_root)) |
| 280 return false; |
| 281 test_server_.reset(new net::TestServer(net::TestServer::TYPE_HTTP, |
| 282 net::TestServer::kLocalhost, |
| 283 document_root)); |
| 284 return test_server_->Start(); |
| 285 } |
| 286 |
| 287 scoped_ptr<net::TestServer> test_server_; |
| 288 }; |
| 289 |
| 290 class NaClBrowserTestNewlib : public NaClBrowserTestBase { |
| 291 virtual FilePath::StringType Variant() { |
| 292 return FILE_PATH_LITERAL("newlib"); |
| 293 } |
| 294 }; |
| 295 |
| 296 class NaClBrowserTestGLibc : public NaClBrowserTestBase { |
| 297 virtual FilePath::StringType Variant() { |
| 298 return FILE_PATH_LITERAL("glibc"); |
| 299 } |
| 300 }; |
| 301 |
| 302 // Disable tests under Linux ASAN. |
| 303 // Linux ASAN doesn't work with NaCl. See: http://crbug.com/104832. |
| 304 // TODO(ncbray) enable after http://codereview.chromium.org/10830009/ lands. |
| 305 #if !(defined(ADDRESS_SANITIZER) && defined(OS_LINUX)) |
| 306 |
| 307 IN_PROC_BROWSER_TEST_F(NaClBrowserTestNewlib, SimpleLoadTest) { |
| 308 RunLoadTest(FILE_PATH_LITERAL("nacl_load_test.html")); |
| 309 } |
| 310 |
| 311 IN_PROC_BROWSER_TEST_F(NaClBrowserTestGLibc, SimpleLoadTest) { |
| 312 RunLoadTest(FILE_PATH_LITERAL("nacl_load_test.html")); |
| 313 } |
| 314 |
| 315 #endif // !(defined(ADDRESS_SANITIZER) && defined(OS_LINUX)) |
| 316 |
| 317 } // namespace anonymous |
OLD | NEW |