Home | History | Annotate | Download | only in proxy
      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/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"
     20 
     21 namespace ppapi {
     22 namespace proxy {
     23 
     24 namespace {
     25 // HostDispatcher requires a PPB_Proxy_Private, so we always provide a fallback
     26 // do-nothing implementation.
     27 void PluginCrashed(PP_Module module) {
     28   NOTREACHED();
     29 };
     30 
     31 PP_Instance GetInstanceForResource(PP_Resource resource) {
     32   // If a test relies on this, we need to implement it.
     33   NOTREACHED();
     34   return 0;
     35 }
     36 
     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.
     41 }
     42 
     43 void AddRefModule(PP_Module module) {}
     44 void ReleaseModule(PP_Module module) {}
     45 PP_Bool IsInModuleDestructor(PP_Module module) { return PP_FALSE; }
     46 
     47 PPB_Proxy_Private ppb_proxy_private = {
     48   &PluginCrashed,
     49   &GetInstanceForResource,
     50   &SetReserveInstanceIDCallback,
     51   &AddRefModule,
     52   &ReleaseModule,
     53   &IsInModuleDestructor
     54 };
     55 
     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_;
     65 
     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);
     71     if (interface)
     72       return interface;
     73   }
     74   if (strcmp(name, PPB_PROXY_PRIVATE_INTERFACE) == 0)
     75     return &ppb_proxy_private;
     76   return NULL;
     77 }
     78 
     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();
     87 }
     88 
     89 void TearDownRemoteHarness(ProxyTestHarnessBase* harness,
     90                            base::WaitableEvent* harness_torn_down) {
     91   harness->TearDownHarness();
     92   harness_torn_down->Signal();
     93 }
     94 
     95 void RunTaskOnRemoteHarness(const base::Closure& task,
     96                             base::WaitableEvent* task_complete) {
     97  task.Run();
     98  task_complete->Signal();
     99 }
    100 
    101 }  // namespace
    102 
    103 // ProxyTestHarnessBase --------------------------------------------------------
    104 
    105 ProxyTestHarnessBase::ProxyTestHarnessBase() : pp_module_(0x98765),
    106                                                pp_instance_(0x12345) {
    107   get_interface_handlers_.AddObserver(this);
    108 }
    109 
    110 ProxyTestHarnessBase::~ProxyTestHarnessBase() {
    111   get_interface_handlers_.RemoveObserver(this);
    112 }
    113 
    114 const void* ProxyTestHarnessBase::GetInterface(const char* name) {
    115   return registered_interfaces_[name];
    116 }
    117 
    118 void ProxyTestHarnessBase::RegisterTestInterface(const char* name,
    119                                                  const void* test_interface) {
    120   registered_interfaces_[name] = test_interface;
    121 }
    122 
    123 bool ProxyTestHarnessBase::SupportsInterface(const char* name) {
    124   sink().ClearMessages();
    125 
    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);
    131 
    132   const IPC::Message* reply_msg =
    133       sink().GetUniqueMessageMatching(IPC_REPLY_ID);
    134   EXPECT_TRUE(reply_msg);
    135   if (!reply_msg)
    136     return false;
    137 
    138   TupleTypes<PpapiMsg_SupportsInterface::ReplyParam>::ValueTuple reply_data;
    139   EXPECT_TRUE(PpapiMsg_SupportsInterface::ReadReplyParam(
    140       reply_msg, &reply_data));
    141 
    142   sink().ClearMessages();
    143   return reply_data.a;
    144 }
    145 
    146 // PluginProxyTestHarness ------------------------------------------------------
    147 
    148 PluginProxyTestHarness::PluginProxyTestHarness(
    149     GlobalsConfiguration globals_config)
    150     : globals_config_(globals_config) {
    151 }
    152 
    153 PluginProxyTestHarness::~PluginProxyTestHarness() {
    154 }
    155 
    156 PpapiGlobals* PluginProxyTestHarness::GetGlobals() {
    157   return plugin_globals_.get();
    158 }
    159 
    160 Dispatcher* PluginProxyTestHarness::GetDispatcher() {
    161   return plugin_dispatcher_.get();
    162 }
    163 
    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.
    168   ProxyAutoLock lock;
    169 
    170   resource_tracker().DidCreateInstance(pp_instance());
    171 
    172   plugin_dispatcher_.reset(new PluginDispatcher(
    173       &MockGetInterface,
    174       PpapiPermissions(),
    175       false));
    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());
    185 }
    186 
    187 void PluginProxyTestHarness::SetUpHarnessWithChannel(
    188     const IPC::ChannelHandle& channel_handle,
    189     base::MessageLoopProxy* ipc_message_loop,
    190     base::WaitableEvent* shutdown_event,
    191     bool is_client) {
    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.
    195   ProxyAutoLock lock;
    196 
    197   resource_tracker().DidCreateInstance(pp_instance());
    198   plugin_delegate_mock_.Init(ipc_message_loop, shutdown_event);
    199 
    200   plugin_dispatcher_.reset(new PluginDispatcher(
    201       &MockGetInterface,
    202       PpapiPermissions(),
    203       false));
    204   plugin_dispatcher_->InitPluginWithChannel(&plugin_delegate_mock_,
    205                                             base::kNullProcessId,
    206                                             channel_handle,
    207                                             is_client);
    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());
    211 }
    212 
    213 void PluginProxyTestHarness::TearDownHarness() {
    214   {
    215     // Some of the methods called during tear-down check that the lock is held.
    216     ProxyAutoLock lock;
    217 
    218     plugin_dispatcher_->DidDestroyInstance(pp_instance());
    219     plugin_dispatcher_.reset();
    220 
    221     resource_tracker().DidDeleteInstance(pp_instance());
    222   }
    223   plugin_globals_.reset();
    224 }
    225 
    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
    231     // locking.
    232     ProxyLock::EnableLockingOnThreadForTest();
    233   } else {
    234     plugin_globals_.reset(new PluginGlobals());
    235     ProxyLock::EnableLockingOnThreadForTest();
    236   }
    237 }
    238 
    239 base::MessageLoopProxy*
    240 PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() {
    241   return ipc_message_loop_;
    242 }
    243 
    244 base::WaitableEvent*
    245 PluginProxyTestHarness::PluginDelegateMock::GetShutdownEvent() {
    246   return shutdown_event_;
    247 }
    248 
    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);
    257 }
    258 
    259 std::set<PP_Instance>*
    260 PluginProxyTestHarness::PluginDelegateMock::GetGloballySeenInstanceIDSet() {
    261   return &instance_id_set_;
    262 }
    263 
    264 uint32 PluginProxyTestHarness::PluginDelegateMock::Register(
    265     PluginDispatcher* plugin_dispatcher) {
    266   return 0;
    267 }
    268 
    269 void PluginProxyTestHarness::PluginDelegateMock::Unregister(
    270     uint32 plugin_dispatcher_id) {
    271 }
    272 
    273 IPC::Sender* PluginProxyTestHarness::PluginDelegateMock::GetBrowserSender() {
    274   return browser_sender_;
    275 }
    276 
    277 std::string PluginProxyTestHarness::PluginDelegateMock::GetUILanguage() {
    278   return std::string("en-US");
    279 }
    280 
    281 void PluginProxyTestHarness::PluginDelegateMock::PreCacheFont(
    282     const void* logfontw) {
    283 }
    284 
    285 void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL(
    286     const std::string& url) {
    287 }
    288 
    289 PP_Resource PluginProxyTestHarness::PluginDelegateMock::CreateBrowserFont(
    290     Connection connection,
    291     PP_Instance instance,
    292     const PP_BrowserFont_Trusted_Description& desc,
    293     const Preferences& prefs) {
    294   return 0;
    295 }
    296 
    297 // PluginProxyTest -------------------------------------------------------------
    298 
    299 PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS) {
    300 }
    301 
    302 PluginProxyTest::~PluginProxyTest() {
    303 }
    304 
    305 void PluginProxyTest::SetUp() {
    306   SetUpHarness();
    307 }
    308 
    309 void PluginProxyTest::TearDown() {
    310   TearDownHarness();
    311 }
    312 
    313 // PluginProxyMultiThreadTest --------------------------------------------------
    314 
    315 PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() {
    316 }
    317 
    318 PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() {
    319 }
    320 
    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());
    326   nested_main_thread_message_loop_.reset(new base::RunLoop());
    327 
    328   secondary_thread_.reset(new base::DelegateSimpleThread(
    329       this, "PluginProxyMultiThreadTest"));
    330 
    331   {
    332     ProxyAutoLock auto_lock;
    333 
    334     // MessageLoopResource assumes that the proxy lock has been acquired.
    335     secondary_thread_message_loop_ = new MessageLoopResource(pp_instance());
    336 
    337     ASSERT_EQ(PP_OK,
    338         secondary_thread_message_loop_->PostWork(
    339             PP_MakeCompletionCallback(
    340                 &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread,
    341                 this),
    342             0));
    343   }
    344 
    345   SetUpTestOnMainThread();
    346 
    347   secondary_thread_->Start();
    348   nested_main_thread_message_loop_->Run();
    349   secondary_thread_->Join();
    350 
    351   {
    352     ProxyAutoLock auto_lock;
    353 
    354     // The destruction requires a valid PpapiGlobals instance, so we should
    355     // explicitly release it.
    356     secondary_thread_message_loop_ = NULL;
    357   }
    358 
    359   secondary_thread_.reset(NULL);
    360   nested_main_thread_message_loop_.reset(NULL);
    361   main_thread_message_loop_proxy_ = NULL;
    362 }
    363 
    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());
    368   } else {
    369     ASSERT_EQ(secondary_thread_message_loop_.get(),
    370               MessageLoopResource::GetCurrent());
    371   }
    372 }
    373 
    374 void PluginProxyMultiThreadTest::PostQuitForMainThread() {
    375   main_thread_message_loop_proxy_->PostTask(
    376       FROM_HERE,
    377       base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop,
    378                  base::Unretained(this)));
    379 }
    380 
    381 void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() {
    382   ProxyAutoLock auto_lock;
    383   secondary_thread_message_loop_->PostQuit(PP_TRUE);
    384 }
    385 
    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();
    391 }
    392 
    393 void PluginProxyMultiThreadTest::QuitNestedLoop() {
    394   nested_main_thread_message_loop_->Quit();
    395 }
    396 
    397 // static
    398 void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread(
    399     void* user_data,
    400     int32_t result) {
    401   EXPECT_EQ(PP_OK, result);
    402   PluginProxyMultiThreadTest* thiz =
    403       static_cast<PluginProxyMultiThreadTest*>(user_data);
    404   thiz->CheckOnThread(SECONDARY_THREAD);
    405   thiz->SetUpTestOnSecondaryThread();
    406 }
    407 
    408 // HostProxyTestHarness --------------------------------------------------------
    409 
    410 class HostProxyTestHarness::MockSyncMessageStatusReceiver
    411     : public HostDispatcher::SyncMessageStatusReceiver {
    412  public:
    413   virtual void BeginBlockOnSyncMessage() OVERRIDE {}
    414   virtual void EndBlockOnSyncMessage() OVERRIDE {}
    415 };
    416 
    417 HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config)
    418     : globals_config_(globals_config),
    419       status_receiver_(new MockSyncMessageStatusReceiver) {
    420 }
    421 
    422 HostProxyTestHarness::~HostProxyTestHarness() {
    423 }
    424 
    425 PpapiGlobals* HostProxyTestHarness::GetGlobals() {
    426   return host_globals_.get();
    427 }
    428 
    429 Dispatcher* HostProxyTestHarness::GetDispatcher() {
    430   return host_dispatcher_.get();
    431 }
    432 
    433 void HostProxyTestHarness::SetUpHarness() {
    434   // These must be first since the dispatcher set-up uses them.
    435   CreateHostGlobals();
    436 
    437   host_dispatcher_.reset(new HostDispatcher(
    438       pp_module(),
    439       &MockGetInterface,
    440       status_receiver_.release(),
    441       PpapiPermissions::AllPermissions()));
    442   host_dispatcher_->InitWithTestSink(&sink());
    443   HostDispatcher::SetForInstance(pp_instance(), host_dispatcher_.get());
    444 }
    445 
    446 void HostProxyTestHarness::SetUpHarnessWithChannel(
    447     const IPC::ChannelHandle& channel_handle,
    448     base::MessageLoopProxy* ipc_message_loop,
    449     base::WaitableEvent* shutdown_event,
    450     bool is_client) {
    451   // These must be first since the dispatcher set-up uses them.
    452   CreateHostGlobals();
    453 
    454   delegate_mock_.Init(ipc_message_loop, shutdown_event);
    455 
    456   host_dispatcher_.reset(new HostDispatcher(
    457       pp_module(),
    458       &MockGetInterface,
    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());
    466 }
    467 
    468 void HostProxyTestHarness::TearDownHarness() {
    469   HostDispatcher::RemoveForInstance(pp_instance());
    470   host_dispatcher_.reset();
    471   host_globals_.reset();
    472 }
    473 
    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();
    480   } else {
    481     ProxyLock::DisableLockingOnThreadForTest();
    482     host_globals_.reset(new TestGlobals());
    483   }
    484 }
    485 
    486 base::MessageLoopProxy*
    487 HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() {
    488   return ipc_message_loop_;
    489 }
    490 
    491 base::WaitableEvent* HostProxyTestHarness::DelegateMock::GetShutdownEvent() {
    492   return shutdown_event_;
    493 }
    494 
    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);
    503 }
    504 
    505 
    506 // HostProxyTest ---------------------------------------------------------------
    507 
    508 HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS) {
    509 }
    510 
    511 HostProxyTest::~HostProxyTest() {
    512 }
    513 
    514 void HostProxyTest::SetUp() {
    515   SetUpHarness();
    516 }
    517 
    518 void HostProxyTest::TearDown() {
    519   TearDownHarness();
    520 }
    521 
    522 // TwoWayTest ---------------------------------------------------------------
    523 
    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_;
    537   } else {
    538     remote_harness_ = &host_;
    539     local_harness_ = &plugin_;
    540   }
    541 }
    542 
    543 TwoWayTest::~TwoWayTest() {
    544   shutdown_event_.Signal();
    545 }
    546 
    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();
    552 
    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(
    560       FROM_HERE,
    561       base::Bind(&SetUpRemoteHarness,
    562                  remote_harness_,
    563                  handle,
    564                  io_thread_.message_loop_proxy(),
    565                  &shutdown_event_,
    566                  &remote_harness_set_up));
    567   remote_harness_set_up.Wait();
    568   local_harness_->SetUpHarnessWithChannel(handle,
    569                                           io_thread_.message_loop_proxy().get(),
    570                                           &shutdown_event_,
    571                                           true);  // is_client
    572 }
    573 
    574 void TwoWayTest::TearDown() {
    575   base::WaitableEvent remote_harness_torn_down(true, false);
    576   plugin_thread_.message_loop_proxy()->PostTask(
    577       FROM_HERE,
    578       base::Bind(&TearDownRemoteHarness,
    579                  remote_harness_,
    580                  &remote_harness_torn_down));
    581   remote_harness_torn_down.Wait();
    582 
    583   local_harness_->TearDownHarness();
    584 
    585   io_thread_.Stop();
    586 }
    587 
    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,
    592                  task,
    593                  &task_complete));
    594   task_complete.Wait();
    595 }
    596 
    597 
    598 }  // namespace proxy
    599 }  // namespace ppapi
    600