Elim cr-checkbox
[chromium-blink-merge.git] / ppapi / tests / test_message_handler.cc
blobbfb3be3f57f29a647922b770bcdd80fe1d5b0623
1 // Copyright 2014 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.
5 #include "ppapi/tests/test_message_handler.h"
7 #include <string.h>
8 #include <algorithm>
9 #include <map>
10 #include <sstream>
12 #include "ppapi/c/pp_var.h"
13 #include "ppapi/c/ppb_file_io.h"
14 #include "ppapi/c/ppb_messaging.h"
15 #include "ppapi/c/ppp_message_handler.h"
16 #include "ppapi/cpp/completion_callback.h"
17 #include "ppapi/cpp/file_io.h"
18 #include "ppapi/cpp/file_ref.h"
19 #include "ppapi/cpp/file_system.h"
20 #include "ppapi/cpp/instance.h"
21 #include "ppapi/cpp/message_handler.h"
22 #include "ppapi/cpp/module_impl.h"
23 #include "ppapi/cpp/network_list.h"
24 #include "ppapi/cpp/network_monitor.h"
25 #include "ppapi/cpp/var.h"
26 #include "ppapi/cpp/var_array.h"
27 #include "ppapi/cpp/var_array_buffer.h"
28 #include "ppapi/cpp/var_dictionary.h"
29 #include "ppapi/tests/pp_thread.h"
30 #include "ppapi/tests/test_utils.h"
31 #include "ppapi/tests/testing_instance.h"
33 // Windows defines 'PostMessage', so we have to undef it.
34 #ifdef PostMessage
35 #undef PostMessage
36 #endif
38 REGISTER_TEST_CASE(MessageHandler);
40 namespace {
42 // Created and destroyed on the main thread. All public methods should be called
43 // on the main thread. Most data members are only accessed on the main thread.
44 // (Though it handles messages on the background thread).
45 class MyMessageHandler : public pp::MessageHandler {
46 public:
47 explicit MyMessageHandler(TestingInstance* instance,
48 const pp::MessageLoop& loop)
49 : testing_instance_(instance),
50 message_handler_loop_(loop),
51 is_registered_(false),
52 test_finished_event_(instance->pp_instance()),
53 destroy_event_(instance->pp_instance()),
54 async_message_received_(instance->pp_instance()) {
55 AssertOnMainThread();
57 void Register() {
58 AssertOnMainThread();
59 assert(!is_registered_);
60 int32_t result =
61 testing_instance_->RegisterMessageHandler(this, message_handler_loop_);
62 if (result == PP_OK) {
63 is_registered_ = true;
64 } else {
65 std::ostringstream stream;
66 stream << "Failed to register message handler; got error " << result;
67 AddError(stream.str());
68 test_finished_event_.Signal();
70 // Note, at this point, we can't safely read or write errors_ until we wait
71 // on destroy_event_.
73 void Unregister() {
74 AssertOnMainThread();
75 assert(is_registered_);
76 testing_instance_->UnregisterMessageHandler();
77 is_registered_ = false;
79 void WaitForTestFinishedMessage() {
80 test_finished_event_.Wait();
81 test_finished_event_.Reset();
83 // Wait for Destroy() to be called on the MessageHandler thread. When it's
84 // done, return any errors that occurred during the time the MessageHandler
85 // was getting messages.
86 std::string WaitForDestroy() {
87 AssertOnMainThread();
88 // If we haven't called Unregister, we'll be waiting forever.
89 assert(!is_registered_);
90 destroy_event_.Wait();
91 destroy_event_.Reset();
92 // Now that we know Destroy() has been called, we know errors_ isn't being
93 // written on the MessageHandler thread anymore. So we can safely read it
94 // here on the main thread (since destroy_event_ gave us a memory barrier).
95 std::string temp_errors;
96 errors_.swap(temp_errors);
97 return temp_errors;
99 pp::Var WaitForAsyncMessage() {
100 async_message_received_.Wait();
101 pp::Var var_to_return = last_async_message_received_;
102 last_async_message_received_ = pp::Var();
103 async_message_received_.Reset();
104 return var_to_return;
106 private:
107 static void AssertOnMainThread() {
108 assert(pp::MessageLoop::GetForMainThread() ==
109 pp::MessageLoop::GetCurrent());
111 void AddError(const std::string& error) {
112 if (!error.empty()) {
113 if (!errors_.empty())
114 errors_ += "<p>";
115 errors_ += error;
118 virtual void HandleMessage(pp::InstanceHandle instance, const pp::Var& var) {
119 if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
120 AddError("HandleMessage was called on the wrong thread!");
121 if (instance.pp_instance() != testing_instance_->pp_instance())
122 AddError("HandleMessage was passed the wrong instance!");
123 if (var.is_string() && var.AsString() == "FINISHED_TEST") {
124 test_finished_event_.Signal();
125 } else {
126 // Any client causing a message to arrive must wait for the message
127 // before continuing. See WaitForAsyncMessage().
128 assert(last_async_message_received_.is_undefined());
129 last_async_message_received_ = var;
130 async_message_received_.Signal();
134 virtual pp::Var HandleBlockingMessage(pp::InstanceHandle instance,
135 const pp::Var& var) {
136 if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
137 AddError("HandleBlockingMessage was called on the wrong thread!");
138 if (instance.pp_instance() != testing_instance_->pp_instance())
139 AddError("HandleBlockingMessage was passed the wrong instance!");
141 // Attempt a blocking operation; make sure it's disallowed.
142 pp::NetworkMonitor monitor(instance);
143 PP_Resource out_param = 0;
144 pp::CompletionCallbackWithOutput<pp::NetworkList> blocking_callback(
145 &out_param);
146 int32_t error = monitor.UpdateNetworkList(blocking_callback);
147 if (error != PP_ERROR_WOULD_BLOCK_THREAD) {
148 AddError("HandleBlockingMessage was allowed to do a blocking call!");
149 pp::Module::Get()->core()->ReleaseResource(out_param);
152 return var;
155 virtual void WasUnregistered(pp::InstanceHandle instance) {
156 if (pp::MessageLoop::GetCurrent() != message_handler_loop_)
157 AddError("Destroy was called on the wrong thread!");
158 if (instance.pp_instance() != testing_instance_->pp_instance())
159 AddError("Destroy was passed the wrong instance!");
160 destroy_event_.Signal();
163 // These data members are initialized on the main thread, but don't change for
164 // the life of the object, so are safe to access on the background thread,
165 // because there will be a memory barrier before the the MessageHandler calls
166 // are invoked.
167 TestingInstance* const testing_instance_;
168 const pp::MessageLoop message_handler_loop_;
169 const pp::MessageLoop main_loop_;
171 // is_registered_ is only read/written on the main thread.
172 bool is_registered_;
174 // errors_ is written on the MessageHandler thread. When Destroy() is
175 // called, we stop writing to errors_ and signal destroy_event_. This causes
176 // a memory barrier, so it's safe to read errors_ after that.
177 std::string errors_;
178 NestedEvent test_finished_event_;
179 NestedEvent destroy_event_;
181 pp::Var last_async_message_received_;
182 NestedEvent async_message_received_;
184 // Undefined & private to disallow copy and assign.
185 MyMessageHandler(const MyMessageHandler&);
186 MyMessageHandler& operator=(const MyMessageHandler&);
189 void FakeHandleMessage(PP_Instance instance,
190 void* user_data,
191 const PP_Var* message_data) {}
192 void FakeHandleBlockingMessage(PP_Instance instance,
193 void* user_data,
194 const PP_Var* message_data,
195 PP_Var* result) {}
196 void FakeDestroy(PP_Instance instance, void* user_data) {}
198 } // namespace
200 TestMessageHandler::TestMessageHandler(TestingInstance* instance)
201 : TestCase(instance),
202 message_received_(instance->pp_instance()),
203 ppb_messaging_if_(NULL),
204 handler_thread_(instance) {
207 TestMessageHandler::~TestMessageHandler() {
208 handler_thread_.Join();
211 bool TestMessageHandler::Init() {
212 ppb_messaging_if_ = static_cast<const PPB_Messaging_1_2*>(
213 pp::Module::Get()->GetBrowserInterface(PPB_MESSAGING_INTERFACE_1_2));
214 return ppb_messaging_if_ &&
215 CheckTestingInterface() &&
216 handler_thread_.Start();
219 void TestMessageHandler::RunTests(const std::string& filter) {
220 RUN_TEST(RegisterErrorConditions, filter);
221 RUN_TEST(PostMessageAndAwaitResponse, filter);
222 RUN_TEST(Exceptions, filter);
225 void TestMessageHandler::HandleMessage(const pp::Var& message_data) {
226 if (instance()->current_test_name() == "Exceptions") {
227 // For TestPostMessageAndAwaitResponse(), all messages should go to the
228 // background thread message handler.
229 assert(false);
230 } else {
231 // Any subtest causing a message to arrive here must wait for it before
232 // continuing. See WaitForMessage().
233 assert(last_message_.is_undefined());
234 last_message_ = message_data;
235 message_received_.Signal();
239 std::string TestMessageHandler::TestRegisterErrorConditions() {
241 // Test registering with the main thread as the message loop.
242 PPP_MessageHandler_0_2 fake_ppp_message_handler = {
243 &FakeHandleMessage, &FakeHandleBlockingMessage, &FakeDestroy
245 pp::MessageLoop main_loop = pp::MessageLoop::GetForMainThread();
246 int32_t result = ppb_messaging_if_->RegisterMessageHandler(
247 instance()->pp_instance(),
248 reinterpret_cast<void*>(0xdeadbeef),
249 &fake_ppp_message_handler,
250 main_loop.pp_resource());
251 ASSERT_EQ(PP_ERROR_WRONG_THREAD, result);
254 // Test registering with incomplete PPP_Messaging interface.
255 PPP_MessageHandler_0_2 bad_ppp_ifs[] = {
256 { NULL, &FakeHandleBlockingMessage, &FakeDestroy },
257 { &FakeHandleMessage, NULL, &FakeDestroy },
258 { &FakeHandleMessage, &FakeHandleBlockingMessage, NULL }};
259 for (size_t i = 0; i < sizeof(bad_ppp_ifs)/sizeof(bad_ppp_ifs[0]); ++i) {
260 int32_t result = ppb_messaging_if_->RegisterMessageHandler(
261 instance()->pp_instance(),
262 reinterpret_cast<void*>(0xdeadbeef),
263 &bad_ppp_ifs[i],
264 handler_thread_.message_loop().pp_resource());
265 ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
268 PASS();
271 std::string TestMessageHandler::TestPostMessageAndAwaitResponse() {
272 MyMessageHandler handler(instance(),
273 handler_thread_.message_loop());
274 handler.Register();
275 std::string js_code("var plugin = document.getElementById('plugin');\n");
276 js_code += "var result = undefined;\n";
277 const char* const values_to_test[] = {
278 "5",
279 "undefined",
280 "1.5",
281 "'hello'",
282 "{'key': 'value', 'array_key': [1, 2, 3, 4, 5]}",
283 NULL
285 for (size_t i = 0; values_to_test[i]; ++i) {
286 js_code += "result = plugin.postMessageAndAwaitResponse(";
287 js_code += values_to_test[i];
288 js_code += ");\n";
289 js_code += "if (!deepCompare(result, ";
290 js_code += values_to_test[i];
291 js_code += "))\n";
292 js_code += " InternalError(\" Failed postMessageAndAwaitResponse for: ";
293 js_code += values_to_test[i];
294 js_code += " result: \" + result);\n";
296 instance_->EvalScript(js_code);
297 instance_->EvalScript("plugin.postMessage('FINISHED_TEST');\n");
298 handler.WaitForTestFinishedMessage();
299 handler.Unregister();
300 ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
302 PASS();
305 std::string TestMessageHandler::TestExceptions() {
306 MyMessageHandler handler(instance(),
307 handler_thread_.message_loop());
309 // First, try sending a blocking message when there is no handler
310 // registered. It should throw an exception.
311 std::string js_code(
312 "var plugin = document.getElementById('plugin');\n"
313 "var caught_exception = false;\n"
314 "try {\n"
315 " plugin.postMessageAndAwaitResponse('Hello!');\n"
316 "} catch (err) {\n"
317 " caught_exception = true;\n"
318 "}\n"
319 "plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n");
320 instance_->EvalScript(js_code);
321 // Note that we want to wait for the Instance to get the SUCCESS/FAIL
322 // message here. |message_handler| is not yet registered, so the message
323 // goes to the instance instead.
324 pp::Var msg = WaitForMessage();
325 ASSERT_TRUE(msg.is_string());
326 ASSERT_EQ("SUCCESS", msg.AsString());
328 handler.Register();
330 // Now that a handler is registered, try requesting and sending a
331 // FileSystem. It should throw an exception. The file system is opened
332 // asynchronously. What *should* happen is that it opens successfully, then
333 // we try to send it via postMessageAndAwaitResponse, which fails with an
334 // exception. The test could fail either because the filesystem doesn't
335 // open or because postMessageAndAwaitResponse doesn't throw an exception.
336 std::string js_code(
337 "var plugin = document.getElementById('plugin');\n"
338 "function gotFileSystem(fs) {\n"
339 " var caught_exception = false;\n"
340 " try {\n"
341 " plugin.postMessageAndAwaitResponse(fs);\n"
342 " } catch (err) {\n"
343 " caught_exception = true;\n"
344 " }\n"
345 " plugin.postMessage(caught_exception ? 'SUCCESS' : 'FAIL');\n"
346 "}\n"
347 "function fileSystemError() {\n"
348 " plugin.postMessage('Failed to open filesystem');\n"
349 "}\n"
350 "window.webkitRequestFileSystem(\n"
351 " window.Temporary, 1024, gotFileSystem, fileSystemError)\n");
352 instance_->EvalScript(js_code);
353 pp::Var msg = handler.WaitForAsyncMessage();
354 ASSERT_EQ(PP_VARTYPE_STRING, msg.pp_var().type);
355 ASSERT_EQ("SUCCESS", msg.AsString());
357 handler.Unregister();
358 ASSERT_SUBTEST_SUCCESS(handler.WaitForDestroy());
360 PASS();
363 pp::Var TestMessageHandler::WaitForMessage() {
364 message_received_.Wait();
365 pp::Var var_to_return = last_message_;
366 last_message_ = pp::Var();
367 message_received_.Reset();
368 return var_to_return;