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/location.h"
12 #include "base/observer_list.h"
13 #include "base/process/process_handle.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "ipc/ipc_sync_channel.h"
18 #include "ipc/message_filter.h"
19 #include "ppapi/c/pp_errors.h"
20 #include "ppapi/c/private/ppb_proxy_private.h"
21 #include "ppapi/proxy/ppapi_messages.h"
22 #include "ppapi/proxy/ppb_message_loop_proxy.h"
28 // HostDispatcher requires a PPB_Proxy_Private, so we always provide a fallback
29 // do-nothing implementation.
30 void PluginCrashed(PP_Module module
) {
34 PP_Instance
GetInstanceForResource(PP_Resource resource
) {
35 // If a test relies on this, we need to implement it.
40 void SetReserveInstanceIDCallback(PP_Module module
,
41 PP_Bool (*is_seen
)(PP_Module
, PP_Instance
)) {
42 // This function gets called in HostDispatcher's constructor. We simply don't
43 // worry about Instance uniqueness in tests, so we can ignore the call.
46 void AddRefModule(PP_Module module
) {}
47 void ReleaseModule(PP_Module module
) {}
48 PP_Bool
IsInModuleDestructor(PP_Module module
) { return PP_FALSE
; }
50 PPB_Proxy_Private ppb_proxy_private
= {
52 &GetInstanceForResource
,
53 &SetReserveInstanceIDCallback
,
59 // We allow multiple harnesses at a time to respond to 'GetInterface' calls.
60 // We assume that only 1 harness's GetInterface function will ever support a
61 // given interface name. In practice, there will either be only 1 GetInterface
62 // handler (for PluginProxyTest or HostProxyTest), or there will be only 2
63 // GetInterface handlers (for TwoWayTest). In the latter case, one handler is
64 // for the PluginProxyTestHarness and should only respond for PPP interfaces,
65 // and the other handler is for the HostProxyTestHarness which should only
66 // ever respond for PPB interfaces.
67 base::ObserverList
<ProxyTestHarnessBase
> get_interface_handlers_
;
69 const void* MockGetInterface(const char* name
) {
70 base::ObserverList
<ProxyTestHarnessBase
>::Iterator
it(
71 &get_interface_handlers_
);
72 while (ProxyTestHarnessBase
* observer
= it
.GetNext()) {
73 const void* interface
= observer
->GetInterface(name
);
77 if (strcmp(name
, PPB_PROXY_PRIVATE_INTERFACE
) == 0)
78 return &ppb_proxy_private
;
82 void SetUpRemoteHarness(ProxyTestHarnessBase
* harness
,
83 const IPC::ChannelHandle
& handle
,
84 base::SingleThreadTaskRunner
* ipc_task_runner
,
85 base::WaitableEvent
* shutdown_event
,
86 base::WaitableEvent
* harness_set_up
) {
87 harness
->SetUpHarnessWithChannel(handle
, ipc_task_runner
, shutdown_event
,
89 harness_set_up
->Signal();
92 void TearDownRemoteHarness(ProxyTestHarnessBase
* harness
,
93 base::WaitableEvent
* harness_torn_down
) {
94 harness
->TearDownHarness();
95 harness_torn_down
->Signal();
98 void RunTaskOnRemoteHarness(const base::Closure
& task
,
99 base::WaitableEvent
* task_complete
) {
101 task_complete
->Signal();
106 // ProxyTestHarnessBase --------------------------------------------------------
108 ProxyTestHarnessBase::ProxyTestHarnessBase() : pp_module_(0x98765),
109 pp_instance_(0x12345) {
110 get_interface_handlers_
.AddObserver(this);
113 ProxyTestHarnessBase::~ProxyTestHarnessBase() {
114 get_interface_handlers_
.RemoveObserver(this);
117 const void* ProxyTestHarnessBase::GetInterface(const char* name
) {
118 return registered_interfaces_
[name
];
121 void ProxyTestHarnessBase::RegisterTestInterface(const char* name
,
122 const void* test_interface
) {
123 registered_interfaces_
[name
] = test_interface
;
126 bool ProxyTestHarnessBase::SupportsInterface(const char* name
) {
127 sink().ClearMessages();
129 // IPC doesn't actually write to this when we send a message manually
130 // not actually using IPC.
131 bool unused_result
= false;
132 PpapiMsg_SupportsInterface
msg(name
, &unused_result
);
133 GetDispatcher()->OnMessageReceived(msg
);
135 const IPC::Message
* reply_msg
=
136 sink().GetUniqueMessageMatching(IPC_REPLY_ID
);
137 EXPECT_TRUE(reply_msg
);
141 base::TupleTypes
<PpapiMsg_SupportsInterface::ReplyParam
>::ValueTuple
143 EXPECT_TRUE(PpapiMsg_SupportsInterface::ReadReplyParam(
144 reply_msg
, &reply_data
));
146 sink().ClearMessages();
147 return base::get
<0>(reply_data
);
150 // PluginProxyTestHarness ------------------------------------------------------
152 PluginProxyTestHarness::PluginProxyTestHarness(
153 GlobalsConfiguration globals_config
)
154 : globals_config_(globals_config
) {
157 PluginProxyTestHarness::~PluginProxyTestHarness() {
160 PpapiGlobals
* PluginProxyTestHarness::GetGlobals() {
161 return plugin_globals_
.get();
164 Dispatcher
* PluginProxyTestHarness::GetDispatcher() {
165 return plugin_dispatcher_
.get();
168 void PluginProxyTestHarness::SetUpHarness() {
169 // These must be first since the dispatcher set-up uses them.
170 CreatePluginGlobals(nullptr /* ipc_task_runner */);
171 // Some of the methods called during set-up check that the lock is held.
174 resource_tracker().DidCreateInstance(pp_instance());
176 plugin_dispatcher_
.reset(new PluginDispatcher(
180 plugin_dispatcher_
->InitWithTestSink(&sink());
181 // The plugin proxy delegate is needed for
182 // |PluginProxyDelegate::GetBrowserSender| which is used
183 // in |ResourceCreationProxy::GetConnection| to get the channel to the
184 // browser. In this case we just use the |plugin_dispatcher_| as the channel
185 // for test purposes.
186 plugin_delegate_mock_
.set_browser_sender(plugin_dispatcher_
.get());
187 PluginGlobals::Get()->SetPluginProxyDelegate(&plugin_delegate_mock_
);
188 plugin_dispatcher_
->DidCreateInstance(pp_instance());
191 void PluginProxyTestHarness::SetUpHarnessWithChannel(
192 const IPC::ChannelHandle
& channel_handle
,
193 base::SingleThreadTaskRunner
* ipc_task_runner
,
194 base::WaitableEvent
* shutdown_event
,
196 // These must be first since the dispatcher set-up uses them.
197 CreatePluginGlobals(ipc_task_runner
);
198 // Some of the methods called during set-up check that the lock is held.
201 resource_tracker().DidCreateInstance(pp_instance());
202 plugin_delegate_mock_
.Init(ipc_task_runner
, shutdown_event
);
204 plugin_dispatcher_
.reset(new PluginDispatcher(
208 plugin_dispatcher_
->InitPluginWithChannel(&plugin_delegate_mock_
,
209 base::kNullProcessId
,
212 plugin_delegate_mock_
.set_browser_sender(plugin_dispatcher_
.get());
213 PluginGlobals::Get()->SetPluginProxyDelegate(&plugin_delegate_mock_
);
214 plugin_dispatcher_
->DidCreateInstance(pp_instance());
217 void PluginProxyTestHarness::TearDownHarness() {
219 // Some of the methods called during tear-down check that the lock is held.
222 plugin_dispatcher_
->DidDestroyInstance(pp_instance());
223 plugin_dispatcher_
.reset();
225 resource_tracker().DidDeleteInstance(pp_instance());
227 plugin_globals_
.reset();
230 void PluginProxyTestHarness::CreatePluginGlobals(
231 const scoped_refptr
<base::TaskRunner
>& ipc_task_runner
) {
232 if (globals_config_
== PER_THREAD_GLOBALS
) {
233 plugin_globals_
.reset(new PluginGlobals(PpapiGlobals::PerThreadForTest(),
235 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals());
237 plugin_globals_
.reset(new PluginGlobals(ipc_task_runner
));
241 base::SingleThreadTaskRunner
*
242 PluginProxyTestHarness::PluginDelegateMock::GetIPCTaskRunner() {
243 return ipc_task_runner_
;
247 PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() {
248 return shutdown_event_
;
251 IPC::PlatformFileForTransit
252 PluginProxyTestHarness::PluginDelegateMock::ShareHandleWithRemote(
253 base::PlatformFile handle
,
254 base::ProcessId
/* remote_pid */,
255 bool should_close_source
) {
256 return IPC::GetFileHandleForProcess(handle
,
257 base::GetCurrentProcessHandle(),
258 should_close_source
);
261 base::SharedMemoryHandle
262 PluginProxyTestHarness::PluginDelegateMock::ShareSharedMemoryHandleWithRemote(
263 const base::SharedMemoryHandle
& handle
,
264 base::ProcessId
/* remote_pid */) {
265 return base::SharedMemory::DuplicateHandle(handle
);
268 std::set
<PP_Instance
>*
269 PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() {
270 return &instance_id_set_
;
273 uint32
PluginProxyTestHarness::PluginDelegateMock::Register(
274 PluginDispatcher
* plugin_dispatcher
) {
278 void PluginProxyTestHarness::PluginDelegateMock::Unregister(
279 uint32 plugin_dispatcher_id
) {
282 IPC::Sender
* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() {
283 return browser_sender_
;
286 std::string
PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() {
287 return std::string("en-US");
290 void PluginProxyTestHarness::PluginDelegateMock::PreCacheFontForFlash(
291 const void* logfontw
) {
294 void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL(
295 const std::string
& url
) {
298 PP_Resource
PluginProxyTestHarness::PluginDelegateMock::CreateBrowserFont(
299 Connection connection
,
300 PP_Instance instance
,
301 const PP_BrowserFont_Trusted_Description
& desc
,
302 const Preferences
& prefs
) {
306 // PluginProxyTest -------------------------------------------------------------
308 PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS
) {
311 PluginProxyTest::~PluginProxyTest() {
314 void PluginProxyTest::SetUp() {
318 void PluginProxyTest::TearDown() {
322 // PluginProxyMultiThreadTest --------------------------------------------------
324 PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() {
327 PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() {
330 void PluginProxyMultiThreadTest::RunTest() {
331 main_thread_task_runner_
= PpapiGlobals::Get()->GetMainThreadMessageLoop();
332 ASSERT_EQ(main_thread_task_runner_
.get(),
333 base::ThreadTaskRunnerHandle::Get().get());
334 nested_main_thread_message_loop_
.reset(new base::RunLoop());
336 secondary_thread_
.reset(new base::DelegateSimpleThread(
337 this, "PluginProxyMultiThreadTest"));
340 ProxyAutoLock auto_lock
;
342 // MessageLoopResource assumes that the proxy lock has been acquired.
343 secondary_thread_message_loop_
= new MessageLoopResource(pp_instance());
346 secondary_thread_message_loop_
->PostWork(
347 PP_MakeCompletionCallback(
348 &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread
,
353 SetUpTestOnMainThread();
355 secondary_thread_
->Start();
356 nested_main_thread_message_loop_
->Run();
357 secondary_thread_
->Join();
360 ProxyAutoLock auto_lock
;
362 // The destruction requires a valid PpapiGlobals instance, so we should
363 // explicitly release it.
364 secondary_thread_message_loop_
= NULL
;
367 secondary_thread_
.reset(NULL
);
368 nested_main_thread_message_loop_
.reset(NULL
);
369 main_thread_task_runner_
= NULL
;
372 void PluginProxyMultiThreadTest::CheckOnThread(ThreadType thread_type
) {
373 ProxyAutoLock auto_lock
;
374 if (thread_type
== MAIN_THREAD
) {
375 ASSERT_TRUE(MessageLoopResource::GetCurrent()->is_main_thread_loop());
377 ASSERT_EQ(secondary_thread_message_loop_
.get(),
378 MessageLoopResource::GetCurrent());
382 void PluginProxyMultiThreadTest::PostQuitForMainThread() {
383 main_thread_task_runner_
->PostTask(
384 FROM_HERE
, base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop
,
385 base::Unretained(this)));
388 void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() {
389 ProxyAutoLock auto_lock
;
390 secondary_thread_message_loop_
->PostQuit(PP_TRUE
);
393 void PluginProxyMultiThreadTest::Run() {
394 ProxyAutoLock auto_lock
;
395 ASSERT_EQ(PP_OK
, secondary_thread_message_loop_
->AttachToCurrentThread());
396 ASSERT_EQ(PP_OK
, secondary_thread_message_loop_
->Run());
397 secondary_thread_message_loop_
->DetachFromThread();
400 void PluginProxyMultiThreadTest::QuitNestedLoop() {
401 nested_main_thread_message_loop_
->Quit();
405 void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread(
408 EXPECT_EQ(PP_OK
, result
);
409 PluginProxyMultiThreadTest
* thiz
=
410 static_cast<PluginProxyMultiThreadTest
*>(user_data
);
411 thiz
->CheckOnThread(SECONDARY_THREAD
);
412 thiz
->SetUpTestOnSecondaryThread();
415 // HostProxyTestHarness --------------------------------------------------------
417 HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config
)
418 : globals_config_(globals_config
) {
421 HostProxyTestHarness::~HostProxyTestHarness() {
424 PpapiGlobals
* HostProxyTestHarness::GetGlobals() {
425 return host_globals_
.get();
428 Dispatcher
* HostProxyTestHarness::GetDispatcher() {
429 return host_dispatcher_
.get();
432 void HostProxyTestHarness::SetUpHarness() {
433 // These must be first since the dispatcher set-up uses them.
436 host_dispatcher_
.reset(new HostDispatcher(
439 PpapiPermissions::AllPermissions()));
440 host_dispatcher_
->InitWithTestSink(&sink());
441 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_
.get());
444 void HostProxyTestHarness::SetUpHarnessWithChannel(
445 const IPC::ChannelHandle
& channel_handle
,
446 base::SingleThreadTaskRunner
* ipc_task_runner
,
447 base::WaitableEvent
* shutdown_event
,
449 // These must be first since the dispatcher set-up uses them.
452 delegate_mock_
.Init(ipc_task_runner
, shutdown_event
);
454 host_dispatcher_
.reset(new HostDispatcher(
457 PpapiPermissions::AllPermissions()));
458 ppapi::Preferences preferences
;
459 host_dispatcher_
->InitHostWithChannel(&delegate_mock_
,
460 base::kNullProcessId
, channel_handle
,
461 is_client
, preferences
);
462 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_
.get());
465 void HostProxyTestHarness::TearDownHarness() {
466 HostDispatcher::RemoveForInstance(pp_instance());
467 host_dispatcher_
.reset();
468 host_globals_
.reset();
471 void HostProxyTestHarness::CreateHostGlobals() {
472 disable_locking_
.reset(new ProxyLock::LockingDisablerForTest
);
473 if (globals_config_
== PER_THREAD_GLOBALS
) {
474 host_globals_
.reset(new TestGlobals(PpapiGlobals::PerThreadForTest()));
475 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals());
477 host_globals_
.reset(new TestGlobals());
481 base::SingleThreadTaskRunner
*
482 HostProxyTestHarness::DelegateMock::GetIPCTaskRunner() {
483 return ipc_task_runner_
;
486 base::WaitableEvent
* HostProxyTestHarness::DelegateMock::GetShutdownEvent() {
487 return shutdown_event_
;
490 IPC::PlatformFileForTransit
491 HostProxyTestHarness::DelegateMock::ShareHandleWithRemote(
492 base::PlatformFile handle
,
493 base::ProcessId
/* remote_pid */,
494 bool should_close_source
) {
495 return IPC::GetFileHandleForProcess(handle
,
496 base::GetCurrentProcessHandle(),
497 should_close_source
);
500 base::SharedMemoryHandle
501 HostProxyTestHarness::DelegateMock::ShareSharedMemoryHandleWithRemote(
502 const base::SharedMemoryHandle
& handle
,
503 base::ProcessId
/*remote_pid*/) {
504 return base::SharedMemory::DuplicateHandle(handle
);
507 // HostProxyTest ---------------------------------------------------------------
509 HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS
) {
512 HostProxyTest::~HostProxyTest() {
515 void HostProxyTest::SetUp() {
519 void HostProxyTest::TearDown() {
523 // TwoWayTest ---------------------------------------------------------------
525 TwoWayTest::TwoWayTest(TwoWayTest::TwoWayTestMode test_mode
)
526 : test_mode_(test_mode
),
527 host_(ProxyTestHarnessBase::PER_THREAD_GLOBALS
),
528 plugin_(ProxyTestHarnessBase::PER_THREAD_GLOBALS
),
529 io_thread_("TwoWayTest_IOThread"),
530 plugin_thread_("TwoWayTest_PluginThread"),
531 remote_harness_(NULL
),
532 local_harness_(NULL
),
533 channel_created_(true, false),
534 shutdown_event_(true, false) {
535 if (test_mode
== TEST_PPP_INTERFACE
) {
536 remote_harness_
= &plugin_
;
537 local_harness_
= &host_
;
539 remote_harness_
= &host_
;
540 local_harness_
= &plugin_
;
544 TwoWayTest::~TwoWayTest() {
545 shutdown_event_
.Signal();
548 void TwoWayTest::SetUp() {
549 base::Thread::Options options
;
550 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
551 io_thread_
.StartWithOptions(options
);
552 plugin_thread_
.Start();
554 // Construct the IPC handle name using the process ID so we can safely run
555 // multiple |TwoWayTest|s concurrently.
556 std::ostringstream handle_name
;
557 handle_name
<< "TwoWayTestChannel" << base::GetCurrentProcId();
558 IPC::ChannelHandle
handle(handle_name
.str());
559 base::WaitableEvent
remote_harness_set_up(true, false);
560 plugin_thread_
.task_runner()->PostTask(
561 FROM_HERE
, base::Bind(&SetUpRemoteHarness
, remote_harness_
, handle
,
562 io_thread_
.task_runner(), &shutdown_event_
,
563 &remote_harness_set_up
));
564 remote_harness_set_up
.Wait();
565 local_harness_
->SetUpHarnessWithChannel(
566 handle
, io_thread_
.task_runner().get(), &shutdown_event_
,
570 void TwoWayTest::TearDown() {
571 base::WaitableEvent
remote_harness_torn_down(true, false);
572 plugin_thread_
.task_runner()->PostTask(
573 FROM_HERE
, base::Bind(&TearDownRemoteHarness
, remote_harness_
,
574 &remote_harness_torn_down
));
575 remote_harness_torn_down
.Wait();
577 local_harness_
->TearDownHarness();
582 void TwoWayTest::PostTaskOnRemoteHarness(const base::Closure
& task
) {
583 base::WaitableEvent
task_complete(true, false);
584 plugin_thread_
.task_runner()->PostTask(
585 FROM_HERE
, base::Bind(&RunTaskOnRemoteHarness
, task
, &task_complete
));
586 task_complete
.Wait();