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.
5 #include "ppapi/proxy/ppapi_proxy_test.h"
10 #include "base/bind_helpers.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/observer_list.h"
13 #include "base/process/process_handle.h"
14 #include "base/run_loop.h"
15 #include "ipc/ipc_sync_channel.h"
16 #include "ipc/message_filter.h"
17 #include "ppapi/c/pp_errors.h"
18 #include "ppapi/c/private/ppb_proxy_private.h"
19 #include "ppapi/proxy/ppapi_messages.h"
20 #include "ppapi/proxy/ppb_message_loop_proxy.h"
26 // HostDispatcher requires a PPB_Proxy_Private, so we always provide a fallback
27 // do-nothing implementation.
28 void PluginCrashed(PP_Module module
) {
32 PP_Instance
GetInstanceForResource(PP_Resource resource
) {
33 // If a test relies on this, we need to implement it.
38 void SetReserveInstanceIDCallback(PP_Module module
,
39 PP_Bool (*is_seen
)(PP_Module
, PP_Instance
)) {
40 // This function gets called in HostDispatcher's constructor. We simply don't
41 // worry about Instance uniqueness in tests, so we can ignore the call.
44 void AddRefModule(PP_Module module
) {}
45 void ReleaseModule(PP_Module module
) {}
46 PP_Bool
IsInModuleDestructor(PP_Module module
) { return PP_FALSE
; }
48 PPB_Proxy_Private ppb_proxy_private
= {
50 &GetInstanceForResource
,
51 &SetReserveInstanceIDCallback
,
57 // We allow multiple harnesses at a time to respond to 'GetInterface' calls.
58 // We assume that only 1 harness's GetInterface function will ever support a
59 // given interface name. In practice, there will either be only 1 GetInterface
60 // handler (for PluginProxyTest or HostProxyTest), or there will be only 2
61 // GetInterface handlers (for TwoWayTest). In the latter case, one handler is
62 // for the PluginProxyTestHarness and should only respond for PPP interfaces,
63 // and the other handler is for the HostProxyTestHarness which should only
64 // ever respond for PPB interfaces.
65 ObserverList
<ProxyTestHarnessBase
> get_interface_handlers_
;
67 const void* MockGetInterface(const char* name
) {
68 ObserverList
<ProxyTestHarnessBase
>::Iterator it
=
69 get_interface_handlers_
;
70 while (ProxyTestHarnessBase
* observer
= it
.GetNext()) {
71 const void* interface
= observer
->GetInterface(name
);
75 if (strcmp(name
, PPB_PROXY_PRIVATE_INTERFACE
) == 0)
76 return &ppb_proxy_private
;
80 void SetUpRemoteHarness(ProxyTestHarnessBase
* harness
,
81 const IPC::ChannelHandle
& handle
,
82 base::MessageLoopProxy
* ipc_message_loop_proxy
,
83 base::WaitableEvent
* shutdown_event
,
84 base::WaitableEvent
* harness_set_up
) {
85 harness
->SetUpHarnessWithChannel(handle
, ipc_message_loop_proxy
,
86 shutdown_event
, false);
87 harness_set_up
->Signal();
90 void TearDownRemoteHarness(ProxyTestHarnessBase
* harness
,
91 base::WaitableEvent
* harness_torn_down
) {
92 harness
->TearDownHarness();
93 harness_torn_down
->Signal();
96 void RunTaskOnRemoteHarness(const base::Closure
& task
,
97 base::WaitableEvent
* task_complete
) {
99 task_complete
->Signal();
104 // ProxyTestHarnessBase --------------------------------------------------------
106 ProxyTestHarnessBase::ProxyTestHarnessBase() : pp_module_(0x98765),
107 pp_instance_(0x12345) {
108 get_interface_handlers_
.AddObserver(this);
111 ProxyTestHarnessBase::~ProxyTestHarnessBase() {
112 get_interface_handlers_
.RemoveObserver(this);
115 const void* ProxyTestHarnessBase::GetInterface(const char* name
) {
116 return registered_interfaces_
[name
];
119 void ProxyTestHarnessBase::RegisterTestInterface(const char* name
,
120 const void* test_interface
) {
121 registered_interfaces_
[name
] = test_interface
;
124 bool ProxyTestHarnessBase::SupportsInterface(const char* name
) {
125 sink().ClearMessages();
127 // IPC doesn't actually write to this when we send a message manually
128 // not actually using IPC.
129 bool unused_result
= false;
130 PpapiMsg_SupportsInterface
msg(name
, &unused_result
);
131 GetDispatcher()->OnMessageReceived(msg
);
133 const IPC::Message
* reply_msg
=
134 sink().GetUniqueMessageMatching(IPC_REPLY_ID
);
135 EXPECT_TRUE(reply_msg
);
139 TupleTypes
<PpapiMsg_SupportsInterface::ReplyParam
>::ValueTuple reply_data
;
140 EXPECT_TRUE(PpapiMsg_SupportsInterface::ReadReplyParam(
141 reply_msg
, &reply_data
));
143 sink().ClearMessages();
144 return get
<0>(reply_data
);
147 // PluginProxyTestHarness ------------------------------------------------------
149 PluginProxyTestHarness::PluginProxyTestHarness(
150 GlobalsConfiguration globals_config
)
151 : globals_config_(globals_config
) {
154 PluginProxyTestHarness::~PluginProxyTestHarness() {
157 PpapiGlobals
* PluginProxyTestHarness::GetGlobals() {
158 return plugin_globals_
.get();
161 Dispatcher
* PluginProxyTestHarness::GetDispatcher() {
162 return plugin_dispatcher_
.get();
165 void PluginProxyTestHarness::SetUpHarness() {
166 // These must be first since the dispatcher set-up uses them.
167 CreatePluginGlobals();
168 // Some of the methods called during set-up check that the lock is held.
171 resource_tracker().DidCreateInstance(pp_instance());
173 plugin_dispatcher_
.reset(new PluginDispatcher(
177 plugin_dispatcher_
->InitWithTestSink(&sink());
178 // The plugin proxy delegate is needed for
179 // |PluginProxyDelegate::GetBrowserSender| which is used
180 // in |ResourceCreationProxy::GetConnection| to get the channel to the
181 // browser. In this case we just use the |plugin_dispatcher_| as the channel
182 // for test purposes.
183 plugin_delegate_mock_
.set_browser_sender(plugin_dispatcher_
.get());
184 PluginGlobals::Get()->SetPluginProxyDelegate(&plugin_delegate_mock_
);
185 plugin_dispatcher_
->DidCreateInstance(pp_instance());
188 void PluginProxyTestHarness::SetUpHarnessWithChannel(
189 const IPC::ChannelHandle
& channel_handle
,
190 base::MessageLoopProxy
* ipc_message_loop
,
191 base::WaitableEvent
* shutdown_event
,
193 // These must be first since the dispatcher set-up uses them.
194 CreatePluginGlobals();
195 // Some of the methods called during set-up check that the lock is held.
198 resource_tracker().DidCreateInstance(pp_instance());
199 plugin_delegate_mock_
.Init(ipc_message_loop
, shutdown_event
);
201 plugin_dispatcher_
.reset(new PluginDispatcher(
205 plugin_dispatcher_
->InitPluginWithChannel(&plugin_delegate_mock_
,
206 base::kNullProcessId
,
209 plugin_delegate_mock_
.set_browser_sender(plugin_dispatcher_
.get());
210 PluginGlobals::Get()->SetPluginProxyDelegate(&plugin_delegate_mock_
);
211 plugin_dispatcher_
->DidCreateInstance(pp_instance());
214 void PluginProxyTestHarness::TearDownHarness() {
216 // Some of the methods called during tear-down check that the lock is held.
219 plugin_dispatcher_
->DidDestroyInstance(pp_instance());
220 plugin_dispatcher_
.reset();
222 resource_tracker().DidDeleteInstance(pp_instance());
224 plugin_globals_
.reset();
227 void PluginProxyTestHarness::CreatePluginGlobals() {
228 if (globals_config_
== PER_THREAD_GLOBALS
) {
229 plugin_globals_
.reset(new PluginGlobals(PpapiGlobals::PerThreadForTest()));
230 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals());
232 plugin_globals_
.reset(new PluginGlobals());
236 base::MessageLoopProxy
*
237 PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() {
238 return ipc_message_loop_
;
242 PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() {
243 return shutdown_event_
;
246 IPC::PlatformFileForTransit
247 PluginProxyTestHarness::PluginDelegateMock::ShareHandleWithRemote(
248 base::PlatformFile handle
,
249 base::ProcessId
/* remote_pid */,
250 bool should_close_source
) {
251 return IPC::GetFileHandleForProcess(handle
,
252 base::GetCurrentProcessHandle(),
253 should_close_source
);
256 std::set
<PP_Instance
>*
257 PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() {
258 return &instance_id_set_
;
261 uint32
PluginProxyTestHarness::PluginDelegateMock::Register(
262 PluginDispatcher
* plugin_dispatcher
) {
266 void PluginProxyTestHarness::PluginDelegateMock::Unregister(
267 uint32 plugin_dispatcher_id
) {
270 IPC::Sender
* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() {
271 return browser_sender_
;
274 std::string
PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() {
275 return std::string("en-US");
278 void PluginProxyTestHarness::PluginDelegateMock::PreCacheFont(
279 const void* logfontw
) {
282 void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL(
283 const std::string
& url
) {
286 PP_Resource
PluginProxyTestHarness::PluginDelegateMock::CreateBrowserFont(
287 Connection connection
,
288 PP_Instance instance
,
289 const PP_BrowserFont_Trusted_Description
& desc
,
290 const Preferences
& prefs
) {
294 // PluginProxyTest -------------------------------------------------------------
296 PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS
) {
299 PluginProxyTest::~PluginProxyTest() {
302 void PluginProxyTest::SetUp() {
306 void PluginProxyTest::TearDown() {
310 // PluginProxyMultiThreadTest --------------------------------------------------
312 PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() {
315 PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() {
318 void PluginProxyMultiThreadTest::RunTest() {
319 main_thread_message_loop_proxy_
=
320 PpapiGlobals::Get()->GetMainThreadMessageLoop();
321 ASSERT_EQ(main_thread_message_loop_proxy_
.get(),
322 base::MessageLoopProxy::current().get());
323 nested_main_thread_message_loop_
.reset(new base::RunLoop());
325 secondary_thread_
.reset(new base::DelegateSimpleThread(
326 this, "PluginProxyMultiThreadTest"));
329 ProxyAutoLock auto_lock
;
331 // MessageLoopResource assumes that the proxy lock has been acquired.
332 secondary_thread_message_loop_
= new MessageLoopResource(pp_instance());
335 secondary_thread_message_loop_
->PostWork(
336 PP_MakeCompletionCallback(
337 &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread
,
342 SetUpTestOnMainThread();
344 secondary_thread_
->Start();
345 nested_main_thread_message_loop_
->Run();
346 secondary_thread_
->Join();
349 ProxyAutoLock auto_lock
;
351 // The destruction requires a valid PpapiGlobals instance, so we should
352 // explicitly release it.
353 secondary_thread_message_loop_
= NULL
;
356 secondary_thread_
.reset(NULL
);
357 nested_main_thread_message_loop_
.reset(NULL
);
358 main_thread_message_loop_proxy_
= NULL
;
361 void PluginProxyMultiThreadTest::CheckOnThread(ThreadType thread_type
) {
362 ProxyAutoLock auto_lock
;
363 if (thread_type
== MAIN_THREAD
) {
364 ASSERT_TRUE(MessageLoopResource::GetCurrent()->is_main_thread_loop());
366 ASSERT_EQ(secondary_thread_message_loop_
.get(),
367 MessageLoopResource::GetCurrent());
371 void PluginProxyMultiThreadTest::PostQuitForMainThread() {
372 main_thread_message_loop_proxy_
->PostTask(
374 base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop
,
375 base::Unretained(this)));
378 void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() {
379 ProxyAutoLock auto_lock
;
380 secondary_thread_message_loop_
->PostQuit(PP_TRUE
);
383 void PluginProxyMultiThreadTest::Run() {
384 ProxyAutoLock auto_lock
;
385 ASSERT_EQ(PP_OK
, secondary_thread_message_loop_
->AttachToCurrentThread());
386 ASSERT_EQ(PP_OK
, secondary_thread_message_loop_
->Run());
387 secondary_thread_message_loop_
->DetachFromThread();
390 void PluginProxyMultiThreadTest::QuitNestedLoop() {
391 nested_main_thread_message_loop_
->Quit();
395 void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread(
398 EXPECT_EQ(PP_OK
, result
);
399 PluginProxyMultiThreadTest
* thiz
=
400 static_cast<PluginProxyMultiThreadTest
*>(user_data
);
401 thiz
->CheckOnThread(SECONDARY_THREAD
);
402 thiz
->SetUpTestOnSecondaryThread();
405 // HostProxyTestHarness --------------------------------------------------------
407 HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config
)
408 : globals_config_(globals_config
) {
411 HostProxyTestHarness::~HostProxyTestHarness() {
414 PpapiGlobals
* HostProxyTestHarness::GetGlobals() {
415 return host_globals_
.get();
418 Dispatcher
* HostProxyTestHarness::GetDispatcher() {
419 return host_dispatcher_
.get();
422 void HostProxyTestHarness::SetUpHarness() {
423 // These must be first since the dispatcher set-up uses them.
426 host_dispatcher_
.reset(new HostDispatcher(
429 PpapiPermissions::AllPermissions()));
430 host_dispatcher_
->InitWithTestSink(&sink());
431 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_
.get());
434 void HostProxyTestHarness::SetUpHarnessWithChannel(
435 const IPC::ChannelHandle
& channel_handle
,
436 base::MessageLoopProxy
* ipc_message_loop
,
437 base::WaitableEvent
* shutdown_event
,
439 // These must be first since the dispatcher set-up uses them.
442 delegate_mock_
.Init(ipc_message_loop
, shutdown_event
);
444 host_dispatcher_
.reset(new HostDispatcher(
447 PpapiPermissions::AllPermissions()));
448 ppapi::Preferences preferences
;
449 host_dispatcher_
->InitHostWithChannel(&delegate_mock_
,
450 base::kNullProcessId
, channel_handle
,
451 is_client
, preferences
);
452 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_
.get());
455 void HostProxyTestHarness::TearDownHarness() {
456 HostDispatcher::RemoveForInstance(pp_instance());
457 host_dispatcher_
.reset();
458 host_globals_
.reset();
461 void HostProxyTestHarness::CreateHostGlobals() {
462 disable_locking_
.reset(new ProxyLock::LockingDisablerForTest
);
463 if (globals_config_
== PER_THREAD_GLOBALS
) {
464 host_globals_
.reset(new TestGlobals(PpapiGlobals::PerThreadForTest()));
465 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals());
467 host_globals_
.reset(new TestGlobals());
471 base::MessageLoopProxy
*
472 HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() {
473 return ipc_message_loop_
;
476 base::WaitableEvent
* HostProxyTestHarness::DelegateMock::GetShutdownEvent() {
477 return shutdown_event_
;
480 IPC::PlatformFileForTransit
481 HostProxyTestHarness::DelegateMock::ShareHandleWithRemote(
482 base::PlatformFile handle
,
483 base::ProcessId
/* remote_pid */,
484 bool should_close_source
) {
485 return IPC::GetFileHandleForProcess(handle
,
486 base::GetCurrentProcessHandle(),
487 should_close_source
);
491 // HostProxyTest ---------------------------------------------------------------
493 HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS
) {
496 HostProxyTest::~HostProxyTest() {
499 void HostProxyTest::SetUp() {
503 void HostProxyTest::TearDown() {
507 // TwoWayTest ---------------------------------------------------------------
509 TwoWayTest::TwoWayTest(TwoWayTest::TwoWayTestMode test_mode
)
510 : test_mode_(test_mode
),
511 host_(ProxyTestHarnessBase::PER_THREAD_GLOBALS
),
512 plugin_(ProxyTestHarnessBase::PER_THREAD_GLOBALS
),
513 io_thread_("TwoWayTest_IOThread"),
514 plugin_thread_("TwoWayTest_PluginThread"),
515 remote_harness_(NULL
),
516 local_harness_(NULL
),
517 channel_created_(true, false),
518 shutdown_event_(true, false) {
519 if (test_mode
== TEST_PPP_INTERFACE
) {
520 remote_harness_
= &plugin_
;
521 local_harness_
= &host_
;
523 remote_harness_
= &host_
;
524 local_harness_
= &plugin_
;
528 TwoWayTest::~TwoWayTest() {
529 shutdown_event_
.Signal();
532 void TwoWayTest::SetUp() {
533 base::Thread::Options options
;
534 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
535 io_thread_
.StartWithOptions(options
);
536 plugin_thread_
.Start();
538 // Construct the IPC handle name using the process ID so we can safely run
539 // multiple |TwoWayTest|s concurrently.
540 std::ostringstream handle_name
;
541 handle_name
<< "TwoWayTestChannel" << base::GetCurrentProcId();
542 IPC::ChannelHandle
handle(handle_name
.str());
543 base::WaitableEvent
remote_harness_set_up(true, false);
544 plugin_thread_
.message_loop_proxy()->PostTask(
546 base::Bind(&SetUpRemoteHarness
,
549 io_thread_
.message_loop_proxy(),
551 &remote_harness_set_up
));
552 remote_harness_set_up
.Wait();
553 local_harness_
->SetUpHarnessWithChannel(handle
,
554 io_thread_
.message_loop_proxy().get(),
559 void TwoWayTest::TearDown() {
560 base::WaitableEvent
remote_harness_torn_down(true, false);
561 plugin_thread_
.message_loop_proxy()->PostTask(
563 base::Bind(&TearDownRemoteHarness
,
565 &remote_harness_torn_down
));
566 remote_harness_torn_down
.Wait();
568 local_harness_
->TearDownHarness();
573 void TwoWayTest::PostTaskOnRemoteHarness(const base::Closure
& task
) {
574 base::WaitableEvent
task_complete(true, false);
575 plugin_thread_
.message_loop_proxy()->PostTask(FROM_HERE
,
576 base::Bind(&RunTaskOnRemoteHarness
,
579 task_complete
.Wait();