device_enumeration_resource_helper_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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
24namespace ppapi {
25namespace proxy {
26
27namespace {
28
29typedef PluginProxyTest DeviceEnumerationResourceHelperTest;
30
31Connection 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
39bool 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
67class 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
92class 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
122class 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
171class 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
223TEST_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
273TEST_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