ppapi_proxy_test.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 "ppapi/proxy/ppapi_proxy_test.h" 6 7#include <sstream> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/message_loop_proxy.h" 12#include "base/observer_list.h" 13#include "base/process_util.h" 14#include "base/run_loop.h" 15#include "ipc/ipc_sync_channel.h" 16#include "ppapi/c/pp_errors.h" 17#include "ppapi/c/private/ppb_proxy_private.h" 18#include "ppapi/proxy/ppapi_messages.h" 19#include "ppapi/proxy/ppb_message_loop_proxy.h" 20#include "ppapi/shared_impl/proxy_lock.h" 21 22namespace ppapi { 23namespace proxy { 24 25namespace { 26// HostDispatcher requires a PPB_Proxy_Private, so we always provide a fallback 27// do-nothing implementation. 28void PluginCrashed(PP_Module module) { 29 NOTREACHED(); 30}; 31 32PP_Instance GetInstanceForResource(PP_Resource resource) { 33 // If a test relies on this, we need to implement it. 34 NOTREACHED(); 35 return 0; 36} 37 38void 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. 42} 43 44int32_t GetURLLoaderBufferedBytes(PP_Resource url_loader) { 45 NOTREACHED(); 46 return 0; 47} 48 49void AddRefModule(PP_Module module) {} 50void ReleaseModule(PP_Module module) {} 51PP_Bool IsInModuleDestructor(PP_Module module) { return PP_FALSE; } 52 53PPB_Proxy_Private ppb_proxy_private = { 54 &PluginCrashed, 55 &GetInstanceForResource, 56 &SetReserveInstanceIDCallback, 57 &GetURLLoaderBufferedBytes, 58 &AddRefModule, 59 &ReleaseModule, 60 &IsInModuleDestructor 61}; 62 63// We allow multiple harnesses at a time to respond to 'GetInterface' calls. 64// We assume that only 1 harness's GetInterface function will ever support a 65// given interface name. In practice, there will either be only 1 GetInterface 66// handler (for PluginProxyTest or HostProxyTest), or there will be only 2 67// GetInterface handlers (for TwoWayTest). In the latter case, one handler is 68// for the PluginProxyTestHarness and should only respond for PPP interfaces, 69// and the other handler is for the HostProxyTestHarness which should only 70// ever respond for PPB interfaces. 71ObserverList<ProxyTestHarnessBase> get_interface_handlers_; 72 73const void* MockGetInterface(const char* name) { 74 ObserverList<ProxyTestHarnessBase>::Iterator it = 75 get_interface_handlers_; 76 while (ProxyTestHarnessBase* observer = it.GetNext()) { 77 const void* interface = observer->GetInterface(name); 78 if (interface) 79 return interface; 80 } 81 if (strcmp(name, PPB_PROXY_PRIVATE_INTERFACE) == 0) 82 return &ppb_proxy_private; 83 return NULL; 84} 85 86void SetUpRemoteHarness(ProxyTestHarnessBase* harness, 87 const IPC::ChannelHandle& handle, 88 base::MessageLoopProxy* ipc_message_loop_proxy, 89 base::WaitableEvent* shutdown_event, 90 base::WaitableEvent* harness_set_up) { 91 harness->SetUpHarnessWithChannel(handle, ipc_message_loop_proxy, 92 shutdown_event, false); 93 harness_set_up->Signal(); 94} 95 96void TearDownRemoteHarness(ProxyTestHarnessBase* harness, 97 base::WaitableEvent* harness_torn_down) { 98 harness->TearDownHarness(); 99 harness_torn_down->Signal(); 100} 101 102void RunTaskOnRemoteHarness(const base::Closure& task, 103 base::WaitableEvent* task_complete) { 104 task.Run(); 105 task_complete->Signal(); 106} 107 108} // namespace 109 110// ProxyTestHarnessBase -------------------------------------------------------- 111 112ProxyTestHarnessBase::ProxyTestHarnessBase() : pp_module_(0x98765), 113 pp_instance_(0x12345) { 114 get_interface_handlers_.AddObserver(this); 115} 116 117ProxyTestHarnessBase::~ProxyTestHarnessBase() { 118 get_interface_handlers_.RemoveObserver(this); 119} 120 121const void* ProxyTestHarnessBase::GetInterface(const char* name) { 122 return registered_interfaces_[name]; 123} 124 125void ProxyTestHarnessBase::RegisterTestInterface(const char* name, 126 const void* test_interface) { 127 registered_interfaces_[name] = test_interface; 128} 129 130bool ProxyTestHarnessBase::SupportsInterface(const char* name) { 131 sink().ClearMessages(); 132 133 // IPC doesn't actually write to this when we send a message manually 134 // not actually using IPC. 135 bool unused_result = false; 136 PpapiMsg_SupportsInterface msg(name, &unused_result); 137 GetDispatcher()->OnMessageReceived(msg); 138 139 const IPC::Message* reply_msg = 140 sink().GetUniqueMessageMatching(IPC_REPLY_ID); 141 EXPECT_TRUE(reply_msg); 142 if (!reply_msg) 143 return false; 144 145 TupleTypes<PpapiMsg_SupportsInterface::ReplyParam>::ValueTuple reply_data; 146 EXPECT_TRUE(PpapiMsg_SupportsInterface::ReadReplyParam( 147 reply_msg, &reply_data)); 148 149 sink().ClearMessages(); 150 return reply_data.a; 151} 152 153// PluginProxyTestHarness ------------------------------------------------------ 154 155PluginProxyTestHarness::PluginProxyTestHarness( 156 GlobalsConfiguration globals_config) 157 : globals_config_(globals_config) { 158} 159 160PluginProxyTestHarness::~PluginProxyTestHarness() { 161} 162 163PpapiGlobals* PluginProxyTestHarness::GetGlobals() { 164 return plugin_globals_.get(); 165} 166 167Dispatcher* PluginProxyTestHarness::GetDispatcher() { 168 return plugin_dispatcher_.get(); 169} 170 171void PluginProxyTestHarness::SetUpHarness() { 172 // These must be first since the dispatcher set-up uses them. 173 CreatePluginGlobals(); 174 175 resource_tracker().DidCreateInstance(pp_instance()); 176 177 plugin_dispatcher_.reset(new PluginDispatcher( 178 &MockGetInterface, 179 PpapiPermissions(), 180 false)); 181 plugin_dispatcher_->InitWithTestSink(&sink()); 182 // The plugin proxy delegate is needed for 183 // |PluginProxyDelegate::GetBrowserSender| which is used 184 // in |ResourceCreationProxy::GetConnection| to get the channel to the 185 // browser. In this case we just use the |plugin_dispatcher_| as the channel 186 // for test purposes. 187 plugin_delegate_mock_.set_browser_sender(plugin_dispatcher_.get()); 188 PluginGlobals::Get()->set_plugin_proxy_delegate(&plugin_delegate_mock_); 189 plugin_dispatcher_->DidCreateInstance(pp_instance()); 190} 191 192void PluginProxyTestHarness::SetUpHarnessWithChannel( 193 const IPC::ChannelHandle& channel_handle, 194 base::MessageLoopProxy* ipc_message_loop, 195 base::WaitableEvent* shutdown_event, 196 bool is_client) { 197 // These must be first since the dispatcher set-up uses them. 198 CreatePluginGlobals(); 199 200 resource_tracker().DidCreateInstance(pp_instance()); 201 plugin_delegate_mock_.Init(ipc_message_loop, shutdown_event); 202 203 plugin_dispatcher_.reset(new PluginDispatcher( 204 &MockGetInterface, 205 PpapiPermissions(), 206 false)); 207 plugin_dispatcher_->InitPluginWithChannel(&plugin_delegate_mock_, 208 base::kNullProcessId, 209 channel_handle, 210 is_client); 211 plugin_delegate_mock_.set_browser_sender(plugin_dispatcher_.get()); 212 PluginGlobals::Get()->set_plugin_proxy_delegate(&plugin_delegate_mock_); 213 plugin_dispatcher_->DidCreateInstance(pp_instance()); 214} 215 216void PluginProxyTestHarness::TearDownHarness() { 217 plugin_dispatcher_->DidDestroyInstance(pp_instance()); 218 plugin_dispatcher_.reset(); 219 220 resource_tracker().DidDeleteInstance(pp_instance()); 221 plugin_globals_.reset(); 222} 223 224void PluginProxyTestHarness::CreatePluginGlobals() { 225 if (globals_config_ == PER_THREAD_GLOBALS) { 226 plugin_globals_.reset(new PluginGlobals(PpapiGlobals::PerThreadForTest())); 227 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); 228 } else { 229 plugin_globals_.reset(new PluginGlobals()); 230 } 231} 232 233base::MessageLoopProxy* 234PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() { 235 return ipc_message_loop_; 236} 237 238base::WaitableEvent* 239PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() { 240 return shutdown_event_; 241} 242 243IPC::PlatformFileForTransit 244PluginProxyTestHarness::PluginDelegateMock::ShareHandleWithRemote( 245 base::PlatformFile handle, 246 base::ProcessId /* remote_pid */, 247 bool should_close_source) { 248 return IPC::GetFileHandleForProcess(handle, 249 base::Process::Current().handle(), 250 should_close_source); 251} 252 253std::set<PP_Instance>* 254PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() { 255 return &instance_id_set_; 256} 257 258uint32 PluginProxyTestHarness::PluginDelegateMock::Register( 259 PluginDispatcher* plugin_dispatcher) { 260 return 0; 261} 262 263void PluginProxyTestHarness::PluginDelegateMock::Unregister( 264 uint32 plugin_dispatcher_id) { 265} 266 267IPC::Sender* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() { 268 return browser_sender_; 269} 270 271std::string PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() { 272 return std::string("en-US"); 273} 274 275void PluginProxyTestHarness::PluginDelegateMock::PreCacheFont( 276 const void* logfontw) { 277} 278 279void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL( 280 const std::string& url) { 281} 282 283// PluginProxyTest ------------------------------------------------------------- 284 285PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS) { 286} 287 288PluginProxyTest::~PluginProxyTest() { 289} 290 291void PluginProxyTest::SetUp() { 292 SetUpHarness(); 293} 294 295void PluginProxyTest::TearDown() { 296 TearDownHarness(); 297} 298 299// PluginProxyMultiThreadTest -------------------------------------------------- 300 301PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() { 302} 303 304PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() { 305} 306 307void PluginProxyMultiThreadTest::RunTest() { 308 main_thread_message_loop_proxy_ = 309 PpapiGlobals::Get()->GetMainThreadMessageLoop(); 310 ASSERT_EQ(main_thread_message_loop_proxy_.get(), 311 base::MessageLoopProxy::current()); 312 nested_main_thread_message_loop_.reset(new base::RunLoop()); 313 314 secondary_thread_.reset(new base::DelegateSimpleThread( 315 this, "PluginProxyMultiThreadTest")); 316 317 { 318 ProxyAutoLock auto_lock; 319 320 // MessageLoopResource assumes that the proxy lock has been acquired. 321 secondary_thread_message_loop_ = new MessageLoopResource(pp_instance()); 322 323 ASSERT_EQ(PP_OK, 324 secondary_thread_message_loop_->PostWork( 325 PP_MakeCompletionCallback( 326 &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread, 327 this), 328 0)); 329 } 330 331 SetUpTestOnMainThread(); 332 333 secondary_thread_->Start(); 334 nested_main_thread_message_loop_->Run(); 335 secondary_thread_->Join(); 336 337 { 338 ProxyAutoLock auto_lock; 339 340 // The destruction requires a valid PpapiGlobals instance, so we should 341 // explicitly release it. 342 secondary_thread_message_loop_ = NULL; 343 } 344 345 secondary_thread_.reset(NULL); 346 nested_main_thread_message_loop_.reset(NULL); 347 main_thread_message_loop_proxy_ = NULL; 348} 349 350void PluginProxyMultiThreadTest::CheckOnThread(ThreadType thread_type) { 351 ProxyAutoLock auto_lock; 352 if (thread_type == MAIN_THREAD) { 353 ASSERT_TRUE(MessageLoopResource::GetCurrent()->is_main_thread_loop()); 354 } else { 355 ASSERT_EQ(secondary_thread_message_loop_.get(), 356 MessageLoopResource::GetCurrent()); 357 } 358} 359 360void PluginProxyMultiThreadTest::PostQuitForMainThread() { 361 main_thread_message_loop_proxy_->PostTask( 362 FROM_HERE, 363 base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop, 364 base::Unretained(this))); 365} 366 367void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() { 368 ProxyAutoLock auto_lock; 369 secondary_thread_message_loop_->PostQuit(PP_TRUE); 370} 371 372void PluginProxyMultiThreadTest::Run() { 373 ProxyAutoLock auto_lock; 374 ASSERT_EQ(PP_OK, secondary_thread_message_loop_->AttachToCurrentThread()); 375 ASSERT_EQ(PP_OK, secondary_thread_message_loop_->Run()); 376} 377 378void PluginProxyMultiThreadTest::QuitNestedLoop() { 379 nested_main_thread_message_loop_->Quit(); 380} 381 382// static 383void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread( 384 void* user_data, 385 int32_t result) { 386 EXPECT_EQ(PP_OK, result); 387 PluginProxyMultiThreadTest* thiz = 388 static_cast<PluginProxyMultiThreadTest*>(user_data); 389 thiz->CheckOnThread(SECONDARY_THREAD); 390 thiz->SetUpTestOnSecondaryThread(); 391} 392 393// HostProxyTestHarness -------------------------------------------------------- 394 395class HostProxyTestHarness::MockSyncMessageStatusReceiver 396 : public HostDispatcher::SyncMessageStatusReceiver { 397 public: 398 virtual void BeginBlockOnSyncMessage() OVERRIDE {} 399 virtual void EndBlockOnSyncMessage() OVERRIDE {} 400}; 401 402HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config) 403 : globals_config_(globals_config), 404 status_receiver_(new MockSyncMessageStatusReceiver) { 405} 406 407HostProxyTestHarness::~HostProxyTestHarness() { 408} 409 410PpapiGlobals* HostProxyTestHarness::GetGlobals() { 411 return host_globals_.get(); 412} 413 414Dispatcher* HostProxyTestHarness::GetDispatcher() { 415 return host_dispatcher_.get(); 416} 417 418void HostProxyTestHarness::SetUpHarness() { 419 // These must be first since the dispatcher set-up uses them. 420 CreateHostGlobals(); 421 422 host_dispatcher_.reset(new HostDispatcher( 423 pp_module(), 424 &MockGetInterface, 425 status_receiver_.release(), 426 PpapiPermissions::AllPermissions())); 427 host_dispatcher_->InitWithTestSink(&sink()); 428 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get()); 429} 430 431void HostProxyTestHarness::SetUpHarnessWithChannel( 432 const IPC::ChannelHandle& channel_handle, 433 base::MessageLoopProxy* ipc_message_loop, 434 base::WaitableEvent* shutdown_event, 435 bool is_client) { 436 // These must be first since the dispatcher set-up uses them. 437 CreateHostGlobals(); 438 439 delegate_mock_.Init(ipc_message_loop, shutdown_event); 440 441 host_dispatcher_.reset(new HostDispatcher( 442 pp_module(), 443 &MockGetInterface, 444 status_receiver_.release(), 445 PpapiPermissions::AllPermissions())); 446 ppapi::Preferences preferences; 447 host_dispatcher_->InitHostWithChannel(&delegate_mock_, 448 base::kNullProcessId, channel_handle, 449 is_client, preferences); 450 HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get()); 451} 452 453void HostProxyTestHarness::TearDownHarness() { 454 HostDispatcher::RemoveForInstance(pp_instance()); 455 host_dispatcher_.reset(); 456 host_globals_.reset(); 457} 458 459void HostProxyTestHarness::CreateHostGlobals() { 460 if (globals_config_ == PER_THREAD_GLOBALS) { 461 host_globals_.reset(new TestGlobals(PpapiGlobals::PerThreadForTest())); 462 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); 463 } else { 464 host_globals_.reset(new TestGlobals()); 465 } 466} 467 468base::MessageLoopProxy* 469HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() { 470 return ipc_message_loop_; 471} 472 473base::WaitableEvent* HostProxyTestHarness::DelegateMock::GetShutdownEvent() { 474 return shutdown_event_; 475} 476 477IPC::PlatformFileForTransit 478HostProxyTestHarness::DelegateMock::ShareHandleWithRemote( 479 base::PlatformFile handle, 480 base::ProcessId /* remote_pid */, 481 bool should_close_source) { 482 return IPC::GetFileHandleForProcess(handle, 483 base::Process::Current().handle(), 484 should_close_source); 485} 486 487 488// HostProxyTest --------------------------------------------------------------- 489 490HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS) { 491} 492 493HostProxyTest::~HostProxyTest() { 494} 495 496void HostProxyTest::SetUp() { 497 SetUpHarness(); 498} 499 500void HostProxyTest::TearDown() { 501 TearDownHarness(); 502} 503 504// TwoWayTest --------------------------------------------------------------- 505 506TwoWayTest::TwoWayTest(TwoWayTest::TwoWayTestMode test_mode) 507 : test_mode_(test_mode), 508 host_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), 509 plugin_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), 510 io_thread_("TwoWayTest_IOThread"), 511 plugin_thread_("TwoWayTest_PluginThread"), 512 remote_harness_(NULL), 513 local_harness_(NULL), 514 channel_created_(true, false), 515 shutdown_event_(true, false) { 516 if (test_mode == TEST_PPP_INTERFACE) { 517 remote_harness_ = &plugin_; 518 local_harness_ = &host_; 519 } else { 520 remote_harness_ = &host_; 521 local_harness_ = &plugin_; 522 } 523} 524 525TwoWayTest::~TwoWayTest() { 526 shutdown_event_.Signal(); 527} 528 529void TwoWayTest::SetUp() { 530 base::Thread::Options options; 531 options.message_loop_type = MessageLoop::TYPE_IO; 532 io_thread_.StartWithOptions(options); 533 plugin_thread_.Start(); 534 535 // Construct the IPC handle name using the process ID so we can safely run 536 // multiple |TwoWayTest|s concurrently. 537 std::ostringstream handle_name; 538 handle_name << "TwoWayTestChannel" << base::GetCurrentProcId(); 539 IPC::ChannelHandle handle(handle_name.str()); 540 base::WaitableEvent remote_harness_set_up(true, false); 541 plugin_thread_.message_loop_proxy()->PostTask( 542 FROM_HERE, 543 base::Bind(&SetUpRemoteHarness, 544 remote_harness_, 545 handle, 546 io_thread_.message_loop_proxy(), 547 &shutdown_event_, 548 &remote_harness_set_up)); 549 remote_harness_set_up.Wait(); 550 local_harness_->SetUpHarnessWithChannel(handle, 551 io_thread_.message_loop_proxy(), 552 &shutdown_event_, 553 true); // is_client 554} 555 556void TwoWayTest::TearDown() { 557 base::WaitableEvent remote_harness_torn_down(true, false); 558 plugin_thread_.message_loop_proxy()->PostTask( 559 FROM_HERE, 560 base::Bind(&TearDownRemoteHarness, 561 remote_harness_, 562 &remote_harness_torn_down)); 563 remote_harness_torn_down.Wait(); 564 565 local_harness_->TearDownHarness(); 566 567 io_thread_.Stop(); 568} 569 570void TwoWayTest::PostTaskOnRemoteHarness(const base::Closure& task) { 571 base::WaitableEvent task_complete(true, false); 572 plugin_thread_.message_loop_proxy()->PostTask(FROM_HERE, 573 base::Bind(&RunTaskOnRemoteHarness, 574 task, 575 &task_complete)); 576 task_complete.Wait(); 577} 578 579 580} // namespace proxy 581} // namespace ppapi 582