QUIC - cleanup changes to sync chromium tree with internal source.
[chromium-blink-merge.git] / device / bluetooth / bluetooth_adapter_mac_unittest.mm
blob2e66cdd1c67541cfdeebec38ca7e442451b69d91
1 // Copyright 2013 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.
5 #include "base/memory/ref_counted.h"
6 #include "base/test/test_simple_task_runner.h"
7 #include "device/bluetooth/bluetooth_adapter.h"
8 #include "device/bluetooth/bluetooth_adapter_mac.h"
9 #include "device/bluetooth/bluetooth_discovery_session.h"
10 #include "device/bluetooth/bluetooth_discovery_session_outcome.h"
11 #include "device/bluetooth/bluetooth_low_energy_device_mac.h"
12 #include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/ocmock/OCMock/OCMock.h"
16 #if defined(OS_IOS)
17 #import <CoreBluetooth/CoreBluetooth.h>
18 #else  // !defined(OS_IOS)
19 #import <IOBluetooth/IOBluetooth.h>
20 #endif  // defined(OS_IOS)
22 #import <Foundation/Foundation.h>
24 namespace {
25 // |kTestHashAddress| is the hash corresponding to identifier |kTestNSUUID|.
26 NSString* const kTestNSUUID = @"00000000-1111-2222-3333-444444444444";
27 const std::string kTestHashAddress = "D1:6F:E3:22:FD:5B";
28 const int kTestRssi = 0;
29 }  // namespace
31 namespace device {
33 class BluetoothAdapterMacTest : public testing::Test {
34  public:
35   BluetoothAdapterMacTest()
36       : ui_task_runner_(new base::TestSimpleTaskRunner()),
37         adapter_(new BluetoothAdapterMac()),
38         adapter_mac_(static_cast<BluetoothAdapterMac*>(adapter_.get())),
39         callback_count_(0),
40         error_callback_count_(0) {
41     adapter_mac_->InitForTest(ui_task_runner_);
42   }
44   // Helper methods for setup and access to BluetoothAdapterMacTest's members.
45   void PollAdapter() { adapter_mac_->PollAdapter(); }
47   void LowEnergyDeviceUpdated(CBPeripheral* peripheral,
48                               NSDictionary* advertisement_data,
49                               int rssi) {
50     adapter_mac_->LowEnergyDeviceUpdated(peripheral, advertisement_data, rssi);
51   }
53   BluetoothDevice* GetDevice(const std::string& address) {
54     return adapter_->GetDevice(address);
55   }
57   CBPeripheral* CreateMockPeripheral(NSString* identifier) {
58     if (!BluetoothAdapterMac::IsLowEnergyAvailable()) {
59       LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
60       return nil;
61     }
62     Class peripheral_class = NSClassFromString(@"CBPeripheral");
63     id mock_peripheral =
64         [[OCMockObject mockForClass:[peripheral_class class]] retain];
65     [static_cast<CBPeripheral*>([[mock_peripheral stub]
66         andReturnValue:@(CBPeripheralStateDisconnected)])
67         performSelector:@selector(state)];
68     [[[mock_peripheral stub] andReturn:[NSString string]] name];
69     Class uuid_class = NSClassFromString(@"NSUUID");
70     [[[mock_peripheral stub]
71         andReturn:[[uuid_class performSelector:@selector(UUID)]
72                       performSelector:@selector(initWithUUIDString:)
73                            withObject:identifier]] identifier];
75     return mock_peripheral;
76   }
78   NSDictionary* CreateAdvertisementData() {
79     NSDictionary* advertisement_data = @{
80       @"CBAdvertisementDataIsConnectable" : @(YES),
81       @"CBAdvertisementDataServiceDataKey" : [NSDictionary dictionary],
82     };
83     [advertisement_data retain];
84     return advertisement_data;
85   }
87   std::string GetHashAddress(CBPeripheral* peripheral) {
88     return BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
89   }
91   void SetDeviceTimeGreaterThanTimeout(BluetoothLowEnergyDeviceMac* device) {
92     device->last_update_time_.reset([[NSDate
93         dateWithTimeInterval:-(BluetoothAdapterMac::kDiscoveryTimeoutSec + 1)
94                    sinceDate:[NSDate date]] retain]);
95   }
97   void AddLowEnergyDevice(BluetoothLowEnergyDeviceMac* device) {
98     adapter_mac_->devices_[device->GetAddress()] = device;
99   }
101   int NumDevices() { return adapter_mac_->devices_.size(); }
103   bool DevicePresent(CBPeripheral* peripheral) {
104     BluetoothDevice* device = adapter_mac_->GetDevice(
105         BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral));
106     return (device != NULL);
107   }
109   void RemoveTimedOutDevices() { adapter_mac_->RemoveTimedOutDevices(); }
111   bool SetMockCentralManager(CBCentralManagerState desired_state) {
112     if (!BluetoothAdapterMac::IsLowEnergyAvailable()) {
113       LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
114       return false;
115     }
116     mock_central_manager_ = [[MockCentralManager alloc] init];
117     [mock_central_manager_ setState:desired_state];
118     adapter_mac_->SetCentralManagerForTesting(mock_central_manager_);
119     return true;
120   }
122   void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter) {
123     adapter_mac_->AddDiscoverySession(
124         discovery_filter,
125         base::Bind(&BluetoothAdapterMacTest::Callback, base::Unretained(this)),
126         base::Bind(&BluetoothAdapterMacTest::DiscoveryErrorCallback,
127                    base::Unretained(this)));
128   }
130   void RemoveDiscoverySession(BluetoothDiscoveryFilter* discovery_filter) {
131     adapter_mac_->RemoveDiscoverySession(
132         discovery_filter,
133         base::Bind(&BluetoothAdapterMacTest::Callback, base::Unretained(this)),
134         base::Bind(&BluetoothAdapterMacTest::DiscoveryErrorCallback,
135                    base::Unretained(this)));
136   }
138   int NumDiscoverySessions() { return adapter_mac_->num_discovery_sessions_; }
140   // Generic callbacks.
141   void Callback() { ++callback_count_; }
142   void ErrorCallback() { ++error_callback_count_; }
143   void DiscoveryErrorCallback(UMABluetoothDiscoverySessionOutcome) {
144     ++error_callback_count_;
145   }
147  protected:
148   scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_;
149   scoped_refptr<BluetoothAdapter> adapter_;
150   BluetoothAdapterMac* adapter_mac_;
152   // Owned by |adapter_mac_|.
153   id mock_central_manager_ = NULL;
155   int callback_count_;
156   int error_callback_count_;
159 TEST_F(BluetoothAdapterMacTest, Poll) {
160   PollAdapter();
161   EXPECT_FALSE(ui_task_runner_->GetPendingTasks().empty());
164 TEST_F(BluetoothAdapterMacTest, AddDiscoverySessionWithLowEnergyFilter) {
165   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
166     return;
167   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
168   EXPECT_EQ(0, NumDiscoverySessions());
170   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
171       new BluetoothDiscoveryFilter(
172           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
173   AddDiscoverySession(discovery_filter.get());
174   EXPECT_EQ(1, callback_count_);
175   EXPECT_EQ(0, error_callback_count_);
176   EXPECT_EQ(1, NumDiscoverySessions());
178   // Check that adding a discovery session resulted in
179   // scanForPeripheralsWithServices being called on the Central Manager.
180   EXPECT_EQ(1, [mock_central_manager_ scanForPeripheralsCallCount]);
183 // TODO(krstnmnlsn): Test changing the filter when adding the second discovery
184 // session (once we have that ability).
185 TEST_F(BluetoothAdapterMacTest, AddSecondDiscoverySessionWithLowEnergyFilter) {
186   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
187     return;
188   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
189       new BluetoothDiscoveryFilter(
190           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
191   AddDiscoverySession(discovery_filter.get());
192   EXPECT_EQ(1, callback_count_);
193   EXPECT_EQ(0, error_callback_count_);
194   EXPECT_EQ(1, NumDiscoverySessions());
196   // We replaced the success callback handed to AddDiscoverySession, so
197   // |adapter_mac_| should remain in a discovering state indefinitely.
198   EXPECT_TRUE(adapter_mac_->IsDiscovering());
200   AddDiscoverySession(discovery_filter.get());
201   EXPECT_EQ(2, [mock_central_manager_ scanForPeripheralsCallCount]);
202   EXPECT_EQ(2, callback_count_);
203   EXPECT_EQ(0, error_callback_count_);
204   EXPECT_EQ(2, NumDiscoverySessions());
207 TEST_F(BluetoothAdapterMacTest, RemoveDiscoverySessionWithLowEnergyFilter) {
208   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
209     return;
210   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
212   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
213       new BluetoothDiscoveryFilter(
214           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
215   AddDiscoverySession(discovery_filter.get());
216   EXPECT_EQ(1, callback_count_);
217   EXPECT_EQ(0, error_callback_count_);
218   EXPECT_EQ(1, NumDiscoverySessions());
220   EXPECT_EQ(0, [mock_central_manager_ stopScanCallCount]);
221   RemoveDiscoverySession(discovery_filter.get());
222   EXPECT_EQ(2, callback_count_);
223   EXPECT_EQ(0, error_callback_count_);
224   EXPECT_EQ(0, NumDiscoverySessions());
226   // Check that removing the discovery session resulted in stopScan being called
227   // on the Central Manager.
228   EXPECT_EQ(1, [mock_central_manager_ stopScanCallCount]);
231 TEST_F(BluetoothAdapterMacTest, RemoveDiscoverySessionWithLowEnergyFilterFail) {
232   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
233     return;
234   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
235   EXPECT_EQ(0, [mock_central_manager_ stopScanCallCount]);
236   EXPECT_EQ(0, NumDiscoverySessions());
238   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
239       new BluetoothDiscoveryFilter(
240           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
241   RemoveDiscoverySession(discovery_filter.get());
242   EXPECT_EQ(0, callback_count_);
243   EXPECT_EQ(1, error_callback_count_);
244   EXPECT_EQ(0, NumDiscoverySessions());
246   // Check that stopScan was not called.
247   EXPECT_EQ(0, [mock_central_manager_ stopScanCallCount]);
250 TEST_F(BluetoothAdapterMacTest, CheckGetPeripheralHashAddress) {
251   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
252   if (mock_peripheral.get() == nil)
253     return;
254   EXPECT_EQ(kTestHashAddress, GetHashAddress(mock_peripheral));
257 TEST_F(BluetoothAdapterMacTest, LowEnergyDeviceUpdatedNewDevice) {
258   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
259   if (mock_peripheral.get() == nil)
260     return;
261   base::scoped_nsobject<NSDictionary> advertisement_data(
262       CreateAdvertisementData());
264   EXPECT_EQ(0, NumDevices());
265   EXPECT_FALSE(DevicePresent(mock_peripheral));
266   LowEnergyDeviceUpdated(mock_peripheral, advertisement_data, kTestRssi);
267   EXPECT_EQ(1, NumDevices());
268   EXPECT_TRUE(DevicePresent(mock_peripheral));
271 TEST_F(BluetoothAdapterMacTest, LowEnergyDeviceUpdatedOldDevice) {
272   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
273   if (mock_peripheral.get() == nil)
274     return;
275   base::scoped_nsobject<NSDictionary> advertisement_data(
276       CreateAdvertisementData());
278   // Update the device for the first time and check it was correctly added to
279   // |devices_|.
280   EXPECT_EQ(0, NumDevices());
281   EXPECT_FALSE(DevicePresent(mock_peripheral));
282   LowEnergyDeviceUpdated(mock_peripheral, advertisement_data, kTestRssi);
283   EXPECT_EQ(1, NumDevices());
284   EXPECT_TRUE(DevicePresent(mock_peripheral));
285   // Search for the device by the address corresponding to |kTestNSUUID|.
286   BluetoothDeviceMac* device =
287       static_cast<BluetoothDeviceMac*>(GetDevice(kTestHashAddress));
288   base::scoped_nsobject<NSDate> first_update_time(
289       [device->GetLastUpdateTime() retain]);
291   // Update the device a second time. The device should be updated in
292   // |devices_| so check the time returned by GetLastUpdateTime() has increased.
293   LowEnergyDeviceUpdated(mock_peripheral, advertisement_data, kTestRssi);
294   EXPECT_EQ(1, NumDevices());
295   EXPECT_TRUE(DevicePresent(mock_peripheral));
296   device = static_cast<BluetoothDeviceMac*>(GetDevice(kTestHashAddress));
297   EXPECT_TRUE([device->GetLastUpdateTime() compare:first_update_time] ==
298               NSOrderedDescending);
301 TEST_F(BluetoothAdapterMacTest, UpdateDevicesRemovesLowEnergyDevice) {
302   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
303   if (mock_peripheral.get() == nil)
304     return;
305   base::scoped_nsobject<NSDictionary> advertisement_data(
306       CreateAdvertisementData());
308   BluetoothLowEnergyDeviceMac* device = new BluetoothLowEnergyDeviceMac(
309       mock_peripheral, advertisement_data, kTestRssi);
310   SetDeviceTimeGreaterThanTimeout(device);
312   EXPECT_EQ(0, NumDevices());
313   AddLowEnergyDevice(device);
314   EXPECT_EQ(1, NumDevices());
315   EXPECT_TRUE(DevicePresent(mock_peripheral));
317   // Check that object pointed to by |device| is deleted by the adapter.
318   RemoveTimedOutDevices();
319   EXPECT_EQ(0, NumDevices());
320   EXPECT_FALSE(DevicePresent(mock_peripheral));
323 }  // namespace device