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 "base/basictypes.h"
      6 #include "base/compiler_specific.h"
      7 #include "ppapi/c/pp_errors.h"
      8 #include "ppapi/proxy/connection.h"
      9 #include "ppapi/proxy/device_enumeration_resource_helper.h"
     10 #include "ppapi/proxy/plugin_message_filter.h"
     11 #include "ppapi/proxy/plugin_resource.h"
     12 #include "ppapi/proxy/plugin_resource_tracker.h"
     13 #include "ppapi/proxy/plugin_var_tracker.h"
     14 #include "ppapi/proxy/ppapi_message_utils.h"
     15 #include "ppapi/proxy/ppapi_messages.h"
     16 #include "ppapi/proxy/ppapi_proxy_test.h"
     17 #include "ppapi/shared_impl/ppb_device_ref_shared.h"
     18 #include "ppapi/shared_impl/proxy_lock.h"
     19 #include "ppapi/shared_impl/var.h"
     20 #include "ppapi/thunk/enter.h"
     21 #include "ppapi/thunk/ppb_device_ref_api.h"
     22 #include "ppapi/thunk/thunk.h"
     23 
     24 namespace ppapi {
     25 namespace proxy {
     26 
     27 namespace {
     28 
     29 typedef PluginProxyTest DeviceEnumerationResourceHelperTest;
     30 
     31 Connection GetConnection(PluginProxyTestHarness* harness) {
     32   CHECK(harness->GetGlobals()->IsPluginGlobals());
     33 
     34   return Connection(
     35       static_cast<PluginGlobals*>(harness->GetGlobals())->GetBrowserSender(),
     36       harness->plugin_dispatcher());
     37 }
     38 
     39 bool CompareDeviceRef(PluginVarTracker* var_tracker,
     40                       PP_Resource resource,
     41                       const DeviceRefData& expected) {
     42   thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter(resource, true);
     43   if (enter.failed())
     44     return false;
     45 
     46   if (expected.type != enter.object()->GetType())
     47     return false;
     48 
     49   PP_Var name_pp_var = enter.object()->GetName();
     50   bool result = false;
     51   do {
     52     Var* name_var = var_tracker->GetVar(name_pp_var);
     53     if (!name_var)
     54       break;
     55     StringVar* name_string_var = name_var->AsStringVar();
     56     if (!name_string_var)
     57       break;
     58     if (expected.name != name_string_var->value())
     59       break;
     60 
     61     result = true;
     62   } while (false);
     63   var_tracker->ReleaseVar(name_pp_var);
     64   return result;
     65 }
     66 
     67 class TestResource : public PluginResource {
     68  public:
     69   TestResource(Connection connection, PP_Instance instance)
     70       : PluginResource(connection, instance),
     71         device_enumeration_(this) {
     72   }
     73 
     74   virtual ~TestResource() {}
     75 
     76   virtual void OnReplyReceived(const ResourceMessageReplyParams& params,
     77                                const IPC::Message& msg) OVERRIDE {
     78     if (!device_enumeration_.HandleReply(params, msg))
     79       PluginResource::OnReplyReceived(params, msg);
     80   }
     81 
     82   DeviceEnumerationResourceHelper& device_enumeration() {
     83     return device_enumeration_;
     84   }
     85 
     86  private:
     87   DeviceEnumerationResourceHelper device_enumeration_;
     88 
     89   DISALLOW_COPY_AND_ASSIGN(TestResource);
     90 };
     91 
     92 class TestCallback {
     93  public:
     94   TestCallback() : called_(false), result_(PP_ERROR_FAILED) {
     95   }
     96   ~TestCallback() {
     97     CHECK(called_);
     98   }
     99 
    100   PP_CompletionCallback MakeCompletionCallback() {
    101     return PP_MakeCompletionCallback(&CompletionCallbackBody, this);
    102   }
    103 
    104   bool called() const { return called_; }
    105   int32_t result() const { return result_; }
    106 
    107  private:
    108   static void CompletionCallbackBody(void* user_data, int32_t result) {
    109     TestCallback* callback = static_cast<TestCallback*>(user_data);
    110 
    111     CHECK(!callback->called_);
    112     callback->called_ = true;
    113     callback->result_ = result;
    114   }
    115 
    116   bool called_;
    117   int32_t result_;
    118 
    119   DISALLOW_COPY_AND_ASSIGN(TestCallback);
    120 };
    121 
    122 class TestArrayOutput {
    123  public:
    124   explicit TestArrayOutput(PluginResourceTracker* resource_tracker)
    125       : data_(NULL),
    126         count_(0),
    127         resource_tracker_(resource_tracker) {
    128   }
    129 
    130   ~TestArrayOutput() {
    131     if (count_ > 0) {
    132       for (size_t i = 0; i < count_; ++i)
    133         resource_tracker_->ReleaseResource(data_[i]);
    134       delete [] data_;
    135     }
    136   }
    137 
    138   PP_ArrayOutput MakeArrayOutput() {
    139     PP_ArrayOutput array_output = { &GetDataBuffer, this };
    140     return array_output;
    141   }
    142 
    143   const PP_Resource* data() const { return data_; }
    144   uint32_t count() const { return count_; }
    145 
    146  private:
    147   static void* GetDataBuffer(void* user_data,
    148                              uint32_t element_count,
    149                              uint32_t element_size) {
    150     CHECK_EQ(element_size, sizeof(PP_Resource));
    151 
    152     TestArrayOutput* output = static_cast<TestArrayOutput*>(user_data);
    153     CHECK(!output->data_);
    154 
    155     output->count_ = element_count;
    156     if (element_count > 0)
    157       output->data_ = new PP_Resource[element_count];
    158     else
    159       output->data_ = NULL;
    160 
    161     return output->data_;
    162   }
    163 
    164   PP_Resource* data_;
    165   uint32_t count_;
    166   PluginResourceTracker* resource_tracker_;
    167 
    168   DISALLOW_COPY_AND_ASSIGN(TestArrayOutput);
    169 };
    170 
    171 class TestMonitorDeviceChange {
    172  public:
    173   explicit TestMonitorDeviceChange(PluginVarTracker* var_tracker)
    174       : called_(false),
    175         same_as_expected_(false),
    176         var_tracker_(var_tracker) {
    177   }
    178 
    179   ~TestMonitorDeviceChange() {}
    180 
    181   void SetExpectedResult(const std::vector<DeviceRefData>& expected) {
    182     called_ = false;
    183     same_as_expected_ = false;
    184     expected_ = expected;
    185   }
    186 
    187   bool called() const { return called_; }
    188 
    189   bool same_as_expected() const { return same_as_expected_; }
    190 
    191   static void MonitorDeviceChangeCallback(void* user_data,
    192                                           uint32_t device_count,
    193                                           const PP_Resource devices[]) {
    194     ProxyAutoLock lock;
    195     TestMonitorDeviceChange* helper =
    196         static_cast<TestMonitorDeviceChange*>(user_data);
    197     CHECK(!helper->called_);
    198 
    199     helper->called_ = true;
    200     helper->same_as_expected_ = false;
    201     if (device_count != helper->expected_.size())
    202       return;
    203     for (size_t i = 0; i < device_count; ++i) {
    204       if (!CompareDeviceRef(helper->var_tracker_, devices[i],
    205                             helper->expected_[i])) {
    206         return;
    207       }
    208     }
    209     helper->same_as_expected_ = true;
    210   }
    211 
    212  private:
    213   bool called_;
    214   bool same_as_expected_;
    215   std::vector<DeviceRefData> expected_;
    216   PluginVarTracker* var_tracker_;
    217 
    218   DISALLOW_COPY_AND_ASSIGN(TestMonitorDeviceChange);
    219 };
    220 
    221 }  // namespace
    222 
    223 TEST_F(DeviceEnumerationResourceHelperTest, EnumerateDevices) {
    224   ProxyAutoLock lock;
    225 
    226   scoped_refptr<TestResource> resource(
    227       new TestResource(GetConnection(this), pp_instance()));
    228   DeviceEnumerationResourceHelper& device_enumeration =
    229       resource->device_enumeration();
    230 
    231   TestArrayOutput output(&resource_tracker());
    232   TestCallback callback;
    233   scoped_refptr<TrackedCallback> tracked_callback(
    234       new TrackedCallback(resource.get(), callback.MakeCompletionCallback()));
    235   int32_t result = device_enumeration.EnumerateDevices(output.MakeArrayOutput(),
    236                                                        tracked_callback);
    237   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    238 
    239   // Should have sent an EnumerateDevices message.
    240   ResourceMessageCallParams params;
    241   IPC::Message msg;
    242   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
    243       PpapiHostMsg_DeviceEnumeration_EnumerateDevices::ID, &params, &msg));
    244 
    245   // Synthesize a response.
    246   ResourceMessageReplyParams reply_params(params.pp_resource(),
    247                                           params.sequence());
    248   reply_params.set_result(PP_OK);
    249   std::vector<DeviceRefData> data;
    250   DeviceRefData data_item;
    251   data_item.type = PP_DEVICETYPE_DEV_AUDIOCAPTURE;
    252   data_item.name = "name_1";
    253   data_item.id = "id_1";
    254   data.push_back(data_item);
    255   data_item.type = PP_DEVICETYPE_DEV_VIDEOCAPTURE;
    256   data_item.name = "name_2";
    257   data_item.id = "id_2";
    258   data.push_back(data_item);
    259 
    260   {
    261     ProxyAutoUnlock unlock;
    262     PluginMessageFilter::DispatchResourceReplyForTest(
    263         reply_params,
    264         PpapiPluginMsg_DeviceEnumeration_EnumerateDevicesReply(data));
    265   }
    266   EXPECT_TRUE(callback.called());
    267   EXPECT_EQ(PP_OK, callback.result());
    268   EXPECT_EQ(2U, output.count());
    269   for (size_t i = 0; i < output.count(); ++i)
    270     EXPECT_TRUE(CompareDeviceRef(&var_tracker(), output.data()[i], data[i]));
    271 }
    272 
    273 TEST_F(DeviceEnumerationResourceHelperTest, MonitorDeviceChange) {
    274   ProxyAutoLock lock;
    275 
    276   scoped_refptr<TestResource> resource(
    277       new TestResource(GetConnection(this), pp_instance()));
    278   DeviceEnumerationResourceHelper& device_enumeration =
    279       resource->device_enumeration();
    280 
    281   TestMonitorDeviceChange helper(&var_tracker());
    282 
    283   int32_t result = device_enumeration.MonitorDeviceChange(
    284       &TestMonitorDeviceChange::MonitorDeviceChangeCallback, &helper);
    285   ASSERT_EQ(PP_OK, result);
    286 
    287   // Should have sent a MonitorDeviceChange message.
    288   ResourceMessageCallParams params;
    289   IPC::Message msg;
    290   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
    291       PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange::ID, &params, &msg));
    292   sink().ClearMessages();
    293 
    294   uint32_t callback_id = 0;
    295   ASSERT_TRUE(UnpackMessage<PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange>(
    296       msg, &callback_id));
    297 
    298   ResourceMessageReplyParams reply_params(params.pp_resource(), 0);
    299   reply_params.set_result(PP_OK);
    300   std::vector<DeviceRefData> data;
    301 
    302   helper.SetExpectedResult(data);
    303 
    304   {
    305     ProxyAutoUnlock unlock;
    306     // Synthesize a response with no device.
    307     PluginMessageFilter::DispatchResourceReplyForTest(
    308         reply_params,
    309         PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange(
    310             callback_id, data));
    311   }
    312   EXPECT_TRUE(helper.called() && helper.same_as_expected());
    313 
    314   DeviceRefData data_item;
    315   data_item.type = PP_DEVICETYPE_DEV_AUDIOCAPTURE;
    316   data_item.name = "name_1";
    317   data_item.id = "id_1";
    318   data.push_back(data_item);
    319   data_item.type = PP_DEVICETYPE_DEV_VIDEOCAPTURE;
    320   data_item.name = "name_2";
    321   data_item.id = "id_2";
    322   data.push_back(data_item);
    323 
    324   helper.SetExpectedResult(data);
    325 
    326   {
    327     ProxyAutoUnlock unlock;
    328     // Synthesize a response with some devices.
    329     PluginMessageFilter::DispatchResourceReplyForTest(
    330         reply_params,
    331         PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange(
    332             callback_id, data));
    333   }
    334   EXPECT_TRUE(helper.called() && helper.same_as_expected());
    335 
    336   TestMonitorDeviceChange helper2(&var_tracker());
    337 
    338   result = device_enumeration.MonitorDeviceChange(
    339       &TestMonitorDeviceChange::MonitorDeviceChangeCallback, &helper2);
    340   ASSERT_EQ(PP_OK, result);
    341 
    342   // Should have sent another MonitorDeviceChange message.
    343   ResourceMessageCallParams params2;
    344   IPC::Message msg2;
    345   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
    346       PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange::ID, &params2, &msg2));
    347   sink().ClearMessages();
    348 
    349   uint32_t callback_id2 = 0;
    350   ASSERT_TRUE(UnpackMessage<PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange>(
    351       msg2, &callback_id2));
    352 
    353   helper.SetExpectedResult(data);
    354   helper2.SetExpectedResult(data);
    355   {
    356     ProxyAutoUnlock unlock;
    357     // |helper2| should receive the result while |helper| shouldn't.
    358     PluginMessageFilter::DispatchResourceReplyForTest(
    359         reply_params,
    360         PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange(
    361             callback_id2, data));
    362   }
    363   EXPECT_TRUE(helper2.called() && helper2.same_as_expected());
    364   EXPECT_FALSE(helper.called());
    365 
    366   helper.SetExpectedResult(data);
    367   helper2.SetExpectedResult(data);
    368   {
    369     ProxyAutoUnlock unlock;
    370     // Even if a message with |callback_id| arrives. |helper| shouldn't receive
    371     // the result.
    372     PluginMessageFilter::DispatchResourceReplyForTest(
    373         reply_params,
    374         PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange(
    375             callback_id, data));
    376   }
    377   EXPECT_FALSE(helper2.called());
    378   EXPECT_FALSE(helper.called());
    379 
    380   result = device_enumeration.MonitorDeviceChange(NULL, NULL);
    381   ASSERT_EQ(PP_OK, result);
    382 
    383   // Should have sent a StopMonitoringDeviceChange message.
    384   ResourceMessageCallParams params3;
    385   IPC::Message msg3;
    386   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
    387       PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange::ID,
    388       &params3, &msg3));
    389   sink().ClearMessages();
    390 
    391   helper2.SetExpectedResult(data);
    392   {
    393     ProxyAutoUnlock unlock;
    394     // |helper2| shouldn't receive any result any more.
    395     PluginMessageFilter::DispatchResourceReplyForTest(
    396         reply_params,
    397         PpapiPluginMsg_DeviceEnumeration_NotifyDeviceChange(
    398             callback_id2, data));
    399   }
    400   EXPECT_FALSE(helper2.called());
    401 }
    402 
    403 }  // namespace proxy
    404 }  // namespace ppapi
    405