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