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 <string.h>
6
7#include "base/strings/stringprintf.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/extensions/extension_apitest.h"
10#include "chrome/browser/extensions/extension_function_test_utils.h"
11#include "chrome/browser/extensions/extension_service.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/test/base/ui_test_utils.h"
14#include "device/bluetooth/bluetooth_adapter.h"
15#include "device/bluetooth/bluetooth_uuid.h"
16#include "device/bluetooth/test/mock_bluetooth_adapter.h"
17#include "device/bluetooth/test/mock_bluetooth_device.h"
18#include "device/bluetooth/test/mock_bluetooth_discovery_session.h"
19#include "extensions/browser/api/bluetooth/bluetooth_api.h"
20#include "extensions/browser/api/bluetooth/bluetooth_event_router.h"
21#include "extensions/common/test_util.h"
22#include "extensions/test/extension_test_message_listener.h"
23#include "extensions/test/result_catcher.h"
24#include "testing/gmock/include/gmock/gmock.h"
25
26using device::BluetoothAdapter;
27using device::BluetoothDevice;
28using device::BluetoothDiscoverySession;
29using device::BluetoothUUID;
30using device::MockBluetoothAdapter;
31using device::MockBluetoothDevice;
32using device::MockBluetoothDiscoverySession;
33using extensions::Extension;
34using extensions::ResultCatcher;
35
36namespace utils = extension_function_test_utils;
37namespace api = extensions::core_api;
38
39namespace {
40
41static const char* kAdapterAddress = "A1:A2:A3:A4:A5:A6";
42static const char* kName = "whatsinaname";
43
44class BluetoothApiTest : public ExtensionApiTest {
45 public:
46  BluetoothApiTest() {}
47
48  virtual void SetUpOnMainThread() OVERRIDE {
49    ExtensionApiTest::SetUpOnMainThread();
50    empty_extension_ = extensions::test_util::CreateEmptyExtension();
51    SetUpMockAdapter();
52  }
53
54  virtual void TearDownOnMainThread() OVERRIDE {
55    EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_));
56  }
57
58  void SetUpMockAdapter() {
59    // The browser will clean this up when it is torn down
60    mock_adapter_ = new testing::StrictMock<MockBluetoothAdapter>();
61    event_router()->SetAdapterForTest(mock_adapter_);
62
63    device1_.reset(new testing::NiceMock<MockBluetoothDevice>(
64        mock_adapter_, 0, "d1", "11:12:13:14:15:16",
65        true /* paired */, true /* connected */));
66    device2_.reset(new testing::NiceMock<MockBluetoothDevice>(
67        mock_adapter_, 0, "d2", "21:22:23:24:25:26",
68        false /* paired */, false /* connected */));
69    device3_.reset(new testing::NiceMock<MockBluetoothDevice>(
70        mock_adapter_, 0, "d3", "31:32:33:34:35:36",
71        false /* paired */, false /* connected */));
72  }
73
74  void DiscoverySessionCallback(
75      const BluetoothAdapter::DiscoverySessionCallback& callback,
76      const BluetoothAdapter::ErrorCallback& error_callback) {
77    if (mock_session_.get()) {
78      callback.Run(
79          scoped_ptr<BluetoothDiscoverySession>(mock_session_.release()));
80      return;
81    }
82    error_callback.Run();
83  }
84
85  template <class T>
86  T* setupFunction(T* function) {
87    function->set_extension(empty_extension_.get());
88    function->set_has_callback(true);
89    return function;
90  }
91
92 protected:
93  testing::StrictMock<MockBluetoothAdapter>* mock_adapter_;
94  scoped_ptr<testing::NiceMock<MockBluetoothDiscoverySession> > mock_session_;
95  scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device1_;
96  scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device2_;
97  scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device3_;
98
99  extensions::BluetoothEventRouter* event_router() {
100    return bluetooth_api()->event_router();
101  }
102
103  extensions::BluetoothAPI* bluetooth_api() {
104    return extensions::BluetoothAPI::Get(browser()->profile());
105  }
106
107 private:
108  scoped_refptr<Extension> empty_extension_;
109};
110
111static void StopDiscoverySessionCallback(const base::Closure& callback,
112                                         const base::Closure& error_callback) {
113  callback.Run();
114}
115
116}  // namespace
117
118IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetAdapterState) {
119  EXPECT_CALL(*mock_adapter_, GetAddress())
120      .WillOnce(testing::Return(kAdapterAddress));
121  EXPECT_CALL(*mock_adapter_, GetName())
122      .WillOnce(testing::Return(kName));
123  EXPECT_CALL(*mock_adapter_, IsPresent())
124      .WillOnce(testing::Return(false));
125  EXPECT_CALL(*mock_adapter_, IsPowered())
126      .WillOnce(testing::Return(true));
127  EXPECT_CALL(*mock_adapter_, IsDiscovering())
128      .WillOnce(testing::Return(false));
129
130  scoped_refptr<api::BluetoothGetAdapterStateFunction> get_adapter_state;
131  get_adapter_state = setupFunction(new api::BluetoothGetAdapterStateFunction);
132
133  scoped_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult(
134      get_adapter_state.get(), "[]", browser()));
135  ASSERT_TRUE(result.get() != NULL);
136  api::bluetooth::AdapterState state;
137  ASSERT_TRUE(api::bluetooth::AdapterState::Populate(*result, &state));
138
139  EXPECT_FALSE(state.available);
140  EXPECT_TRUE(state.powered);
141  EXPECT_FALSE(state.discovering);
142  EXPECT_EQ(kName, state.name);
143  EXPECT_EQ(kAdapterAddress, state.address);
144}
145
146IN_PROC_BROWSER_TEST_F(BluetoothApiTest, DeviceEvents) {
147  ResultCatcher catcher;
148  catcher.RestrictToBrowserContext(browser()->profile());
149
150  ASSERT_TRUE(LoadExtension(
151        test_data_dir_.AppendASCII("bluetooth/device_events")));
152
153  ExtensionTestMessageListener events_received("ready", true);
154  event_router()->DeviceAdded(mock_adapter_, device1_.get());
155  event_router()->DeviceAdded(mock_adapter_, device2_.get());
156
157  EXPECT_CALL(*device2_.get(), GetDeviceName())
158    .WillRepeatedly(testing::Return("the real d2"));
159  EXPECT_CALL(*device2_.get(), GetName())
160    .WillRepeatedly(testing::Return(base::UTF8ToUTF16("the real d2")));
161  event_router()->DeviceChanged(mock_adapter_, device2_.get());
162
163  event_router()->DeviceAdded(mock_adapter_, device3_.get());
164  event_router()->DeviceRemoved(mock_adapter_, device1_.get());
165  EXPECT_TRUE(events_received.WaitUntilSatisfied());
166  events_received.Reply("go");
167
168  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
169}
170
171IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) {
172  // Try with a failure to start. This will return an error as we haven't
173  // initialied a session object.
174  EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_))
175      .WillOnce(
176          testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback));
177
178  // StartDiscovery failure will not reference the adapter.
179  scoped_refptr<api::BluetoothStartDiscoveryFunction> start_function;
180  start_function = setupFunction(new api::BluetoothStartDiscoveryFunction);
181  std::string error(
182      utils::RunFunctionAndReturnError(start_function.get(), "[]", browser()));
183  ASSERT_FALSE(error.empty());
184
185  // Reset the adapter and initiate a discovery session. The ownership of the
186  // mock session will be passed to the event router.
187  ASSERT_FALSE(mock_session_.get());
188  SetUpMockAdapter();
189
190  // Create a mock session to be returned as a result. Get a handle to it as
191  // its ownership will be passed and |mock_session_| will be reset.
192  mock_session_.reset(new testing::NiceMock<MockBluetoothDiscoverySession>());
193  MockBluetoothDiscoverySession* session = mock_session_.get();
194  EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_))
195      .WillOnce(
196          testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback));
197  start_function = setupFunction(new api::BluetoothStartDiscoveryFunction);
198  (void)
199      utils::RunFunctionAndReturnError(start_function.get(), "[]", browser());
200
201  // End the discovery session. The StopDiscovery function should succeed.
202  testing::Mock::VerifyAndClearExpectations(mock_adapter_);
203  EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(true));
204  EXPECT_CALL(*session, Stop(testing::_, testing::_))
205      .WillOnce(testing::Invoke(StopDiscoverySessionCallback));
206
207  // StopDiscovery success will remove the session object, unreferencing the
208  // adapter.
209  scoped_refptr<api::BluetoothStopDiscoveryFunction> stop_function;
210  stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction);
211  (void) utils::RunFunctionAndReturnSingleResult(
212      stop_function.get(), "[]", browser());
213
214  // Reset the adapter. Simulate failure for stop discovery. The event router
215  // still owns the session. Make it appear inactive.
216  SetUpMockAdapter();
217  EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(false));
218  stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction);
219  error =
220      utils::RunFunctionAndReturnError(stop_function.get(), "[]", browser());
221  ASSERT_FALSE(error.empty());
222  SetUpMockAdapter();
223}
224
225IN_PROC_BROWSER_TEST_F(BluetoothApiTest, DiscoveryCallback) {
226  mock_session_.reset(new testing::NiceMock<MockBluetoothDiscoverySession>());
227  MockBluetoothDiscoverySession* session = mock_session_.get();
228  EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_))
229      .WillOnce(
230          testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback));
231  EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(true));
232  EXPECT_CALL(*session, Stop(testing::_, testing::_))
233      .WillOnce(testing::Invoke(StopDiscoverySessionCallback));
234
235  ResultCatcher catcher;
236  catcher.RestrictToBrowserContext(browser()->profile());
237
238  ExtensionTestMessageListener discovery_started("ready", true);
239  ASSERT_TRUE(LoadExtension(
240        test_data_dir_.AppendASCII("bluetooth/discovery_callback")));
241  EXPECT_TRUE(discovery_started.WaitUntilSatisfied());
242
243  event_router()->DeviceAdded(mock_adapter_, device1_.get());
244
245  discovery_started.Reply("go");
246  ExtensionTestMessageListener discovery_stopped("ready", true);
247  EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_));
248  EXPECT_TRUE(discovery_stopped.WaitUntilSatisfied());
249
250  SetUpMockAdapter();
251  event_router()->DeviceAdded(mock_adapter_, device2_.get());
252  discovery_stopped.Reply("go");
253
254  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
255}
256
257IN_PROC_BROWSER_TEST_F(BluetoothApiTest, DiscoveryInProgress) {
258  EXPECT_CALL(*mock_adapter_, GetAddress())
259      .WillOnce(testing::Return(kAdapterAddress));
260  EXPECT_CALL(*mock_adapter_, GetName())
261      .WillOnce(testing::Return(kName));
262  EXPECT_CALL(*mock_adapter_, IsPresent())
263      .WillOnce(testing::Return(true));
264  EXPECT_CALL(*mock_adapter_, IsPowered())
265      .WillOnce(testing::Return(true));
266
267  // Fake that the adapter is discovering
268  EXPECT_CALL(*mock_adapter_, IsDiscovering())
269      .WillOnce(testing::Return(true));
270  event_router()->AdapterDiscoveringChanged(mock_adapter_, true);
271
272  // Cache a device before the extension starts discovering
273  event_router()->DeviceAdded(mock_adapter_, device1_.get());
274
275  ResultCatcher catcher;
276  catcher.RestrictToBrowserContext(browser()->profile());
277
278  mock_session_.reset(new testing::NiceMock<MockBluetoothDiscoverySession>());
279  MockBluetoothDiscoverySession* session = mock_session_.get();
280  EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_))
281      .WillOnce(
282          testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback));
283  EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(true));
284  EXPECT_CALL(*session, Stop(testing::_, testing::_))
285      .WillOnce(testing::Invoke(StopDiscoverySessionCallback));
286
287  ExtensionTestMessageListener discovery_started("ready", true);
288  ASSERT_TRUE(LoadExtension(
289        test_data_dir_.AppendASCII("bluetooth/discovery_in_progress")));
290  EXPECT_TRUE(discovery_started.WaitUntilSatisfied());
291
292  // Only this should be received. No additional notification should be sent for
293  // devices discovered before the discovery session started.
294  event_router()->DeviceAdded(mock_adapter_, device2_.get());
295
296  discovery_started.Reply("go");
297  ExtensionTestMessageListener discovery_stopped("ready", true);
298  EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_));
299  EXPECT_TRUE(discovery_stopped.WaitUntilSatisfied());
300
301  SetUpMockAdapter();
302  // This should never be received.
303  event_router()->DeviceAdded(mock_adapter_, device2_.get());
304  discovery_stopped.Reply("go");
305
306  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
307}
308
309IN_PROC_BROWSER_TEST_F(BluetoothApiTest, OnAdapterStateChanged) {
310  ResultCatcher catcher;
311  catcher.RestrictToBrowserContext(browser()->profile());
312
313  // Load and wait for setup
314  ExtensionTestMessageListener listener("ready", true);
315  ASSERT_TRUE(
316      LoadExtension(
317          test_data_dir_.AppendASCII("bluetooth/on_adapter_state_changed")));
318  EXPECT_TRUE(listener.WaitUntilSatisfied());
319
320  EXPECT_CALL(*mock_adapter_, GetAddress())
321      .WillOnce(testing::Return(kAdapterAddress));
322  EXPECT_CALL(*mock_adapter_, GetName())
323      .WillOnce(testing::Return(kName));
324  EXPECT_CALL(*mock_adapter_, IsPresent())
325      .WillOnce(testing::Return(false));
326  EXPECT_CALL(*mock_adapter_, IsPowered())
327      .WillOnce(testing::Return(false));
328  EXPECT_CALL(*mock_adapter_, IsDiscovering())
329      .WillOnce(testing::Return(false));
330  event_router()->AdapterPoweredChanged(mock_adapter_, false);
331
332  EXPECT_CALL(*mock_adapter_, GetAddress())
333      .WillOnce(testing::Return(kAdapterAddress));
334  EXPECT_CALL(*mock_adapter_, GetName())
335      .WillOnce(testing::Return(kName));
336  EXPECT_CALL(*mock_adapter_, IsPresent())
337      .WillOnce(testing::Return(true));
338  EXPECT_CALL(*mock_adapter_, IsPowered())
339      .WillOnce(testing::Return(true));
340  EXPECT_CALL(*mock_adapter_, IsDiscovering())
341      .WillOnce(testing::Return(true));
342  event_router()->AdapterPresentChanged(mock_adapter_, true);
343
344  EXPECT_CALL(*mock_adapter_, GetAddress())
345      .WillOnce(testing::Return(kAdapterAddress));
346  EXPECT_CALL(*mock_adapter_, GetName())
347      .WillOnce(testing::Return(kName));
348  EXPECT_CALL(*mock_adapter_, IsPresent())
349      .WillOnce(testing::Return(true));
350  EXPECT_CALL(*mock_adapter_, IsPowered())
351      .WillOnce(testing::Return(true));
352  EXPECT_CALL(*mock_adapter_, IsDiscovering())
353      .WillOnce(testing::Return(true));
354  event_router()->AdapterDiscoveringChanged(mock_adapter_, true);
355
356  listener.Reply("go");
357
358  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
359}
360
361IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevices) {
362  ResultCatcher catcher;
363  catcher.RestrictToBrowserContext(browser()->profile());
364
365  BluetoothAdapter::ConstDeviceList devices;
366  devices.push_back(device1_.get());
367  devices.push_back(device2_.get());
368
369  EXPECT_CALL(*mock_adapter_, GetDevices())
370      .Times(1)
371      .WillRepeatedly(testing::Return(devices));
372
373  // Load and wait for setup
374  ExtensionTestMessageListener listener("ready", true);
375  ASSERT_TRUE(
376      LoadExtension(test_data_dir_.AppendASCII("bluetooth/get_devices")));
377  EXPECT_TRUE(listener.WaitUntilSatisfied());
378
379  listener.Reply("go");
380
381  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
382}
383
384IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetDevice) {
385  ResultCatcher catcher;
386  catcher.RestrictToBrowserContext(browser()->profile());
387
388  EXPECT_CALL(*mock_adapter_, GetDevice(device1_->GetAddress()))
389      .WillOnce(testing::Return(device1_.get()));
390  EXPECT_CALL(*mock_adapter_, GetDevice(device2_->GetAddress()))
391      .Times(1)
392      .WillRepeatedly(testing::Return(static_cast<BluetoothDevice*>(NULL)));
393
394  // Load and wait for setup
395  ExtensionTestMessageListener listener("ready", true);
396  ASSERT_TRUE(
397      LoadExtension(test_data_dir_.AppendASCII("bluetooth/get_device")));
398  EXPECT_TRUE(listener.WaitUntilSatisfied());
399
400  listener.Reply("go");
401
402  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
403}
404
405IN_PROC_BROWSER_TEST_F(BluetoothApiTest, DeviceInfo) {
406  ResultCatcher catcher;
407  catcher.RestrictToBrowserContext(browser()->profile());
408
409  // Set up the first device object to reflect a real-world device.
410  BluetoothAdapter::ConstDeviceList devices;
411
412  EXPECT_CALL(*device1_.get(), GetAddress())
413    .WillRepeatedly(testing::Return("A4:17:31:00:00:00"));
414  EXPECT_CALL(*device1_.get(), GetDeviceName())
415    .WillRepeatedly(testing::Return("Chromebook Pixel"));
416  EXPECT_CALL(*device1_.get(), GetName())
417    .WillRepeatedly(testing::Return(base::UTF8ToUTF16("Chromebook Pixel")));
418  EXPECT_CALL(*device1_.get(), GetBluetoothClass())
419    .WillRepeatedly(testing::Return(0x080104));
420  EXPECT_CALL(*device1_.get(), GetDeviceType())
421    .WillRepeatedly(testing::Return(BluetoothDevice::DEVICE_COMPUTER));
422  EXPECT_CALL(*device1_.get(), GetVendorIDSource())
423    .WillRepeatedly(testing::Return(BluetoothDevice::VENDOR_ID_BLUETOOTH));
424  EXPECT_CALL(*device1_.get(), GetVendorID())
425    .WillRepeatedly(testing::Return(0x00E0));
426  EXPECT_CALL(*device1_.get(), GetProductID())
427    .WillRepeatedly(testing::Return(0x240A));
428  EXPECT_CALL(*device1_.get(), GetDeviceID())
429    .WillRepeatedly(testing::Return(0x0400));
430  EXPECT_CALL(*device1_, GetRSSI()).WillRepeatedly(testing::Return(-42));
431  EXPECT_CALL(*device1_, GetCurrentHostTransmitPower())
432      .WillRepeatedly(testing::Return(-16));
433  EXPECT_CALL(*device1_, GetMaximumHostTransmitPower())
434      .WillRepeatedly(testing::Return(10));
435
436  BluetoothDevice::UUIDList uuids;
437  uuids.push_back(BluetoothUUID("1105"));
438  uuids.push_back(BluetoothUUID("1106"));
439
440  EXPECT_CALL(*device1_.get(), GetUUIDs())
441      .WillOnce(testing::Return(uuids));
442
443  devices.push_back(device1_.get());
444
445  // Leave the second largely empty so we can check a device without
446  // available information.
447  devices.push_back(device2_.get());
448
449  EXPECT_CALL(*mock_adapter_, GetDevices())
450      .Times(1)
451      .WillRepeatedly(testing::Return(devices));
452
453  // Load and wait for setup
454  ExtensionTestMessageListener listener("ready", true);
455  ASSERT_TRUE(
456      LoadExtension(test_data_dir_.AppendASCII("bluetooth/device_info")));
457  EXPECT_TRUE(listener.WaitUntilSatisfied());
458
459  listener.Reply("go");
460
461  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
462}
463