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