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/run_loop.h"
14 #include "ipc/ipc_sync_channel.h"
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/private/ppb_proxy_private.h"
17 #include "ppapi/proxy/ppapi_messages.h"
18 #include "ppapi/proxy/ppb_message_loop_proxy.h"
19 #include "ppapi/shared_impl/proxy_lock.h"
25 // HostDispatcher requires a PPB_Proxy_Private, so we always provide a fallback
26 // do-nothing implementation.
27 void PluginCrashed(PP_Module module
) {
31 PP_Instance
GetInstanceForResource(PP_Resource resource
) {
32 // If a test relies on this, we need to implement it.
37 void SetReserveInstanceIDCallback(PP_Module module
,
38 PP_Bool (*is_seen
)(PP_Module
, PP_Instance
)) {
39 // This function gets called in HostDispatcher's constructor. We simply don't
40 // worry about Instance uniqueness in tests, so we can ignore the call.
43 void AddRefModule(PP_Module module
) {}
44 void ReleaseModule(PP_Module module
) {}
45 PP_Bool
IsInModuleDestructor(PP_Module module
) { return PP_FALSE
; }
47 PPB_Proxy_Private ppb_proxy_private
= {
49 &GetInstanceForResource
,
50 &SetReserveInstanceIDCallback
,
56 // We allow multiple harnesses at a time to respond to 'GetInterface' calls.
57 // We assume that only 1 harness's GetInterface function will ever support a
58 // given interface name. In practice, there will either be only 1 GetInterface
59 // handler (for PluginProxyTest or HostProxyTest), or there will be only 2
60 // GetInterface handlers (for TwoWayTest). In the latter case, one handler is
61 // for the PluginProxyTestHarness and should only respond for PPP interfaces,
62 // and the other handler is for the HostProxyTestHarness which should only
63 // ever respond for PPB interfaces.
64 ObserverList
<ProxyTestHarnessBase
> get_interface_handlers_
;
66 const void* MockGetInterface(const char* name
) {
67 ObserverList
<ProxyTestHarnessBase
>::Iterator it
=
68 get_interface_handlers_
;
69 while (ProxyTestHarnessBase
* observer
= it
.GetNext()) {
70 const void* interface
= observer
->GetInterface(name
);
74 if (strcmp(name
, PPB_PROXY_PRIVATE_INTERFACE
) == 0)
75 return &ppb_proxy_private
;
79 void SetUpRemoteHarness(ProxyTestHarnessBase
* harness
,
80 const IPC::ChannelHandle
& handle
,
81 base::MessageLoopProxy
* ipc_message_loop_proxy
,
82 base::WaitableEvent
* shutdown_event
,
83 base::WaitableEvent
* harness_set_up
) {
84 harness
->SetUpHarnessWithChannel(handle
, ipc_message_loop_proxy
,
85 shutdown_event
, false);
86 harness_set_up
->Signal();
89 void TearDownRemoteHarness(ProxyTestHarnessBase
* harness
,
90 base::WaitableEvent
* harness_torn_down
) {
91 harness
->TearDownHarness();
92 harness_torn_down
->Signal();
95 void RunTaskOnRemoteHarness(const base::Closure
& task
,
96 base::WaitableEvent
* task_complete
) {
98 task_complete
->Signal();
103 // ProxyTestHarnessBase --------------------------------------------------------
105 ProxyTestHarnessBase::ProxyTestHarnessBase() : pp_module_(0x98765),
106 pp_instance_(0x12345) {
107 get_interface_handlers_
.AddObserver(this);
110 ProxyTestHarnessBase::~ProxyTestHarnessBase() {
111 get_interface_handlers_
.RemoveObserver(this);
114 const void* ProxyTestHarnessBase::GetInterface(const char* name
) {
115 return registered_interfaces_
[name
];
118 void ProxyTestHarnessBase::RegisterTestInterface(const char* name
,
119 const void* test_interface
) {
120 registered_interfaces_
[name
] = test_interface
;
123 bool ProxyTestHarnessBase::SupportsInterface(const char* name
) {
124 sink().ClearMessages();
126 // IPC doesn't actually write to this when we send a message manually
127 // not actually using IPC.
128 bool unused_result
= false;
129 PpapiMsg_SupportsInterface
msg(name
, &unused_result
);
130 GetDispatcher()->OnMessageReceived(msg
);
132 const IPC::Message
* reply_msg
=
133 sink().GetUniqueMessageMatching(IPC_REPLY_ID
);
134 EXPECT_TRUE(reply_msg
);
138 TupleTypes
<PpapiMsg_SupportsInterface::ReplyParam
>::ValueTuple reply_data
;
139 EXPECT_TRUE(PpapiMsg_SupportsInterface::ReadReplyParam(
140 reply_msg
, &reply_data
));
142 sink().ClearMessages();
146 // PluginProxyTestHarness ------------------------------------------------------
148 PluginProxyTestHarness::PluginProxyTestHarness(
149 GlobalsConfiguration globals_config
)
150 : globals_config_(globals_config
) {
153 PluginProxyTestHarness::~PluginProxyTestHarness() {
156 PpapiGlobals
* PluginProxyTestHarness::GetGlobals() {
157 return plugin_globals_
.get();
160 Dispatcher
* PluginProxyTestHarness::GetDispatcher() {
161 return plugin_dispatcher_
.get();
164 void PluginProxyTestHarness::SetUpHarness() {
165 // These must be first since the dispatcher set-up uses them.
166 CreatePluginGlobals();
167 // Some of the methods called during set-up check that the lock is held.
170 resource_tracker().DidCreateInstance(pp_instance());
172 plugin_dispatcher_
.reset(new PluginDispatcher(
176 plugin_dispatcher_
->InitWithTestSink(&sink());
177 // The plugin proxy delegate is needed for
178 // |PluginProxyDelegate::GetBrowserSender| which is used
179 // in |ResourceCreationProxy::GetConnection| to get the channel to the
180 // browser. In this case we just use the |plugin_dispatcher_| as the channel
181 // for test purposes.
182 plugin_delegate_mock_
.set_browser_sender(plugin_dispatcher_
.get());
183 PluginGlobals::Get()->set_plugin_proxy_delegate(&plugin_delegate_mock_
);
184 plugin_dispatcher_
->DidCreateInstance(pp_instance());
187 void PluginProxyTestHarness::SetUpHarnessWithChannel(
188 const IPC::ChannelHandle
& channel_handle
,
189 base::MessageLoopProxy
* ipc_message_loop
,
190 base::WaitableEvent
* shutdown_event
,
192 // These must be first since the dispatcher set-up uses them.
193 CreatePluginGlobals();
194 // Some of the methods called during set-up check that the lock is held.
197 resource_tracker().DidCreateInstance(pp_instance());
198 plugin_delegate_mock_
.Init(ipc_message_loop
, shutdown_event
);
200 plugin_dispatcher_
.reset(new PluginDispatcher(
204 plugin_dispatcher_
->InitPluginWithChannel(&plugin_delegate_mock_
,
205 base::kNullProcessId
,
208 plugin_delegate_mock_
.set_browser_sender(plugin_dispatcher_
.get());
209 PluginGlobals::Get()->set_plugin_proxy_delegate(&plugin_delegate_mock_
);
210 plugin_dispatcher_
->DidCreateInstance(pp_instance());
213 void PluginProxyTestHarness::TearDownHarness() {
215 // Some of the methods called during tear-down check that the lock is held.
218 plugin_dispatcher_
->DidDestroyInstance(pp_instance());
219 plugin_dispatcher_
.reset();
221 resource_tracker().DidDeleteInstance(pp_instance());
223 plugin_globals_
.reset();
226 void PluginProxyTestHarness::CreatePluginGlobals() {
227 if (globals_config_
== PER_THREAD_GLOBALS
) {
228 plugin_globals_
.reset(new PluginGlobals(PpapiGlobals::PerThreadForTest()));
229 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals());
230 // Enable locking in case some other unit test ran before us and disabled
232 ProxyLock::EnableLockingOnThreadForTest();
234 plugin_globals_
.reset(new PluginGlobals());
235 ProxyLock::EnableLockingOnThreadForTest();
239 base::MessageLoopProxy
*
240 PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() {
241 return ipc_message_loop_
;
245 PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() {
246 return shutdown_event_
;
249 IPC::PlatformFileForTransit
250 PluginProxyTestHarness::PluginDelegateMock::ShareHandleWithRemote(
251 base::PlatformFile handle
,
252 base::ProcessId
/* remote_pid */,
253 bool should_close_source
) {
254 return IPC::GetFileHandleForProcess(handle
,
255 base::Process::Current().handle(),
256 should_close_source
);
259 std::set
<PP_Instance
>*
260 PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() {
261 return &instance_id_set_
;
264 uint32
PluginProxyTestHarness::PluginDelegateMock::Register(
265 PluginDispatcher
* plugin_dispatcher
) {
269 void PluginProxyTestHarness::PluginDelegateMock::Unregister(
270 uint32 plugin_dispatcher_id
) {
273 IPC::Sender
* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() {
274 return browser_sender_
;
277 std::string
PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() {
278 return std::string("en-US");
281 void PluginProxyTestHarness::PluginDelegateMock::PreCacheFont(
282 const void* logfontw
) {
285 void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL(
286 const std::string
& url
) {
289 PP_Resource
PluginProxyTestHarness::PluginDelegateMock::CreateBrowserFont(
290 Connection connection
,
291 PP_Instance instance
,
292 const PP_BrowserFont_Trusted_Description
& desc
,
293 const Preferences
& prefs
) {
297 // PluginProxyTest -------------------------------------------------------------
299 PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS
) {
302 PluginProxyTest::~PluginProxyTest() {
305 void PluginProxyTest::SetUp() {
309 void PluginProxyTest::TearDown() {
313 // PluginProxyMultiThreadTest --------------------------------------------------
315 PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() {
318 PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() {
321 void PluginProxyMultiThreadTest::RunTest() {
322 main_thread_message_loop_proxy_
=
323 PpapiGlobals::Get()->GetMainThreadMessageLoop();
324 ASSERT_EQ(main_thread_message_loop_proxy_
.get(),
325 base::MessageLoopProxy::current().get());
326 nested_main_thread_message_loop_
.reset(new base::RunLoop());
328 secondary_thread_
.reset(new base::DelegateSimpleThread(
329 this, "PluginProxyMultiThreadTest"));
332 ProxyAutoLock auto_lock
;
334 // MessageLoopResource assumes that the proxy lock has been acquired.
335 secondary_thread_message_loop_
= new MessageLoopResource(pp_instance());
338 secondary_thread_message_loop_
->PostWork(
339 PP_MakeCompletionCallback(
340 &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread
,
345 SetUpTestOnMainThread();
347 secondary_thread_
->Start();
348 nested_main_thread_message_loop_
->Run();
349 secondary_thread_
->Join();
352 ProxyAutoLock auto_lock
;
354 // The destruction requires a valid PpapiGlobals instance, so we should
355 // explicitly release it.
356 secondary_thread_message_loop_
= NULL
;
359 secondary_thread_
.reset(NULL
);
360 nested_main_thread_message_loop_
.reset(NULL
);
361 main_thread_message_loop_proxy_
= NULL
;
364 void PluginProxyMultiThreadTest::CheckOnThread(ThreadType thread_type
) {
365 ProxyAutoLock auto_lock
;
366 if (thread_type
== MAIN_THREAD
) {
367 ASSERT_TRUE(MessageLoopResource::GetCurrent()->is_main_thread_loop());
369 ASSERT_EQ(secondary_thread_message_loop_
.get(),
370 MessageLoopResource::GetCurrent());
374 void PluginProxyMultiThreadTest::PostQuitForMainThread() {
375 main_thread_message_loop_proxy_
->PostTask(
377 base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop
,
378 base::Unretained(this)));
381 void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() {
382 ProxyAutoLock auto_lock
;
383 secondary_thread_message_loop_
->PostQuit(PP_TRUE
);
386 void PluginProxyMultiThreadTest::Run() {
387 ProxyAutoLock auto_lock
;
388 ASSERT_EQ(PP_OK
, secondary_thread_message_loop_
->AttachToCurrentThread());
389 ASSERT_EQ(PP_OK
, secondary_thread_message_loop_
->Run());
390 secondary_thread_message_loop_
->DetachFromThread();
393 void PluginProxyMultiThreadTest::QuitNestedLoop() {
394 nested_main_thread_message_loop_
->Quit();
398 void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread(
401 EXPECT_EQ(PP_OK
, result
);
402 PluginProxyMultiThreadTest
* thiz
=
403 static_cast<PluginProxyMultiThreadTest
*>(user_data
);
404 thiz
->CheckOnThread(SECONDARY_THREAD
);
405 thiz
->SetUpTestOnSecondaryThread();
408 // HostProxyTestHarness --------------------------------------------------------
410 class HostProxyTestHarness::MockSyncMessageStatusReceiver
411 : public HostDispatcher::SyncMessageStatusReceiver
{
413 virtual void BeginBlockOnSyncMessage() OVERRIDE
{}
414 virtual void EndBlockOnSyncMessage() OVERRIDE
{}
417 HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config
)
418 : globals_config_(globals_config
),
419 status_receiver_(new MockSyncMessageStatusReceiver
) {
422 HostProxyTestHarness::~HostProxyTestHarness() {
425 PpapiGlobals
* HostProxyTestHarness::GetGlobals() {
426 return host_globals_
.get();
429 Dispatcher
* HostProxyTestHarness::GetDispatcher() {
430 return host_dispatcher_
.get();
433 void HostProxyTestHarness::SetUpHarness() {
434 // These must be first since the dispatcher set-up uses them.
437 host_dispatcher_
.reset(new HostDispatcher(
440 status_receiver_
.release(),
441 PpapiPermissions::AllPermissions()));
442 host_dispatcher_
->InitWithTestSink(&sink());
443 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_
.get());
446 void HostProxyTestHarness::SetUpHarnessWithChannel(
447 const IPC::ChannelHandle
& channel_handle
,
448 base::MessageLoopProxy
* ipc_message_loop
,
449 base::WaitableEvent
* shutdown_event
,
451 // These must be first since the dispatcher set-up uses them.
454 delegate_mock_
.Init(ipc_message_loop
, shutdown_event
);
456 host_dispatcher_
.reset(new HostDispatcher(
459 status_receiver_
.release(),
460 PpapiPermissions::AllPermissions()));
461 ppapi::Preferences preferences
;
462 host_dispatcher_
->InitHostWithChannel(&delegate_mock_
,
463 base::kNullProcessId
, channel_handle
,
464 is_client
, preferences
);
465 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_
.get());
468 void HostProxyTestHarness::TearDownHarness() {
469 HostDispatcher::RemoveForInstance(pp_instance());
470 host_dispatcher_
.reset();
471 host_globals_
.reset();
474 void HostProxyTestHarness::CreateHostGlobals() {
475 if (globals_config_
== PER_THREAD_GLOBALS
) {
476 host_globals_
.reset(new TestGlobals(PpapiGlobals::PerThreadForTest()));
477 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals());
478 // The host side of the proxy does not lock.
479 ProxyLock::DisableLockingOnThreadForTest();
481 ProxyLock::DisableLockingOnThreadForTest();
482 host_globals_
.reset(new TestGlobals());
486 base::MessageLoopProxy
*
487 HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() {
488 return ipc_message_loop_
;
491 base::WaitableEvent
* HostProxyTestHarness::DelegateMock::GetShutdownEvent() {
492 return shutdown_event_
;
495 IPC::PlatformFileForTransit
496 HostProxyTestHarness::DelegateMock::ShareHandleWithRemote(
497 base::PlatformFile handle
,
498 base::ProcessId
/* remote_pid */,
499 bool should_close_source
) {
500 return IPC::GetFileHandleForProcess(handle
,
501 base::Process::Current().handle(),
502 should_close_source
);
506 // HostProxyTest ---------------------------------------------------------------
508 HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS
) {
511 HostProxyTest::~HostProxyTest() {
514 void HostProxyTest::SetUp() {
518 void HostProxyTest::TearDown() {
522 // TwoWayTest ---------------------------------------------------------------
524 TwoWayTest::TwoWayTest(TwoWayTest::TwoWayTestMode test_mode
)
525 : test_mode_(test_mode
),
526 host_(ProxyTestHarnessBase::PER_THREAD_GLOBALS
),
527 plugin_(ProxyTestHarnessBase::PER_THREAD_GLOBALS
),
528 io_thread_("TwoWayTest_IOThread"),
529 plugin_thread_("TwoWayTest_PluginThread"),
530 remote_harness_(NULL
),
531 local_harness_(NULL
),
532 channel_created_(true, false),
533 shutdown_event_(true, false) {
534 if (test_mode
== TEST_PPP_INTERFACE
) {
535 remote_harness_
= &plugin_
;
536 local_harness_
= &host_
;
538 remote_harness_
= &host_
;
539 local_harness_
= &plugin_
;
543 TwoWayTest::~TwoWayTest() {
544 shutdown_event_
.Signal();
547 void TwoWayTest::SetUp() {
548 base::Thread::Options options
;
549 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
550 io_thread_
.StartWithOptions(options
);
551 plugin_thread_
.Start();
553 // Construct the IPC handle name using the process ID so we can safely run
554 // multiple |TwoWayTest|s concurrently.
555 std::ostringstream handle_name
;
556 handle_name
<< "TwoWayTestChannel" << base::GetCurrentProcId();
557 IPC::ChannelHandle
handle(handle_name
.str());
558 base::WaitableEvent
remote_harness_set_up(true, false);
559 plugin_thread_
.message_loop_proxy()->PostTask(
561 base::Bind(&SetUpRemoteHarness
,
564 io_thread_
.message_loop_proxy(),
566 &remote_harness_set_up
));
567 remote_harness_set_up
.Wait();
568 local_harness_
->SetUpHarnessWithChannel(handle
,
569 io_thread_
.message_loop_proxy().get(),
574 void TwoWayTest::TearDown() {
575 base::WaitableEvent
remote_harness_torn_down(true, false);
576 plugin_thread_
.message_loop_proxy()->PostTask(
578 base::Bind(&TearDownRemoteHarness
,
580 &remote_harness_torn_down
));
581 remote_harness_torn_down
.Wait();
583 local_harness_
->TearDownHarness();
588 void TwoWayTest::PostTaskOnRemoteHarness(const base::Closure
& task
) {
589 base::WaitableEvent
task_complete(true, false);
590 plugin_thread_
.message_loop_proxy()->PostTask(FROM_HERE
,
591 base::Bind(&RunTaskOnRemoteHarness
,
594 task_complete
.Wait();