Roll src/third_party/WebKit 787a07c:716df21 (svn 201034:201036)
[chromium-blink-merge.git] / device / bluetooth / bluetooth_adapter_mac_unittest.mm
blob81b3a6cae82dbad313521e08f42707e67f07ed5c
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_low_energy_device_mac.h"
11 #include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "third_party/ocmock/OCMock/OCMock.h"
15 #if defined(OS_IOS)
16 #import <CoreBluetooth/CoreBluetooth.h>
17 #else  // !defined(OS_IOS)
18 #import <IOBluetooth/IOBluetooth.h>
19 #endif  // defined(OS_IOS)
21 #import <Foundation/Foundation.h>
23 namespace {
24 // |kTestHashAddress| is the hash corresponding to identifier |kTestNSUUID|.
25 NSString* const kTestNSUUID = @"00000000-1111-2222-3333-444444444444";
26 const std::string kTestHashAddress = "D1:6F:E3:22:FD:5B";
27 const int kTestRssi = 0;
28 }  // namespace
30 namespace device {
32 class BluetoothAdapterMacTest : public testing::Test {
33  public:
34   BluetoothAdapterMacTest()
35       : ui_task_runner_(new base::TestSimpleTaskRunner()),
36         adapter_(new BluetoothAdapterMac()),
37         adapter_mac_(static_cast<BluetoothAdapterMac*>(adapter_.get())),
38         callback_count_(0),
39         error_callback_count_(0) {
40     adapter_mac_->InitForTest(ui_task_runner_);
41   }
43   // Helper methods for setup and access to BluetoothAdapterMacTest's members.
44   void PollAdapter() { adapter_mac_->PollAdapter(); }
46   void LowEnergyDeviceUpdated(CBPeripheral* peripheral,
47                               NSDictionary* advertisement_data,
48                               int rssi) {
49     adapter_mac_->LowEnergyDeviceUpdated(peripheral, advertisement_data, rssi);
50   }
52   BluetoothDevice* GetDevice(const std::string& address) {
53     return adapter_->GetDevice(address);
54   }
56   CBPeripheral* CreateMockPeripheral(NSString* identifier) {
57     if (!BluetoothAdapterMac::IsLowEnergyAvailable()) {
58       LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
59       return nil;
60     }
61     Class peripheral_class = NSClassFromString(@"CBPeripheral");
62     id mock_peripheral =
63         [[OCMockObject mockForClass:[peripheral_class class]] retain];
64     [static_cast<CBPeripheral*>([[mock_peripheral stub]
65         andReturnValue:@(CBPeripheralStateDisconnected)])
66         performSelector:@selector(state)];
67     [[[mock_peripheral stub] andReturn:[NSString string]] name];
68     Class uuid_class = NSClassFromString(@"NSUUID");
69     [[[mock_peripheral stub]
70         andReturn:[[uuid_class performSelector:@selector(UUID)]
71                       performSelector:@selector(initWithUUIDString:)
72                            withObject:identifier]] identifier];
74     return mock_peripheral;
75   }
77   NSDictionary* CreateAdvertisementData() {
78     NSDictionary* advertisement_data = @{
79       @"CBAdvertisementDataIsConnectable" : @(YES),
80       @"CBAdvertisementDataServiceDataKey" : [NSDictionary dictionary],
81     };
82     [advertisement_data retain];
83     return advertisement_data;
84   }
86   std::string GetHashAddress(CBPeripheral* peripheral) {
87     return BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral);
88   }
90   void SetDeviceTimeGreaterThanTimeout(BluetoothLowEnergyDeviceMac* device) {
91     device->last_update_time_.reset([[NSDate
92         dateWithTimeInterval:-(BluetoothAdapterMac::kDiscoveryTimeoutSec + 1)
93                    sinceDate:[NSDate date]] retain]);
94   }
96   void AddLowEnergyDevice(BluetoothLowEnergyDeviceMac* device) {
97     adapter_mac_->devices_[device->GetAddress()] = device;
98   }
100   int NumDevices() { return adapter_mac_->devices_.size(); }
102   bool DevicePresent(CBPeripheral* peripheral) {
103     BluetoothDevice* device = adapter_mac_->GetDevice(
104         BluetoothLowEnergyDeviceMac::GetPeripheralHashAddress(peripheral));
105     return (device != NULL);
106   }
108   void RemoveTimedOutDevices() { adapter_mac_->RemoveTimedOutDevices(); }
110   bool SetMockCentralManager(CBCentralManagerState desired_state) {
111     if (!BluetoothAdapterMac::IsLowEnergyAvailable()) {
112       LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
113       return false;
114     }
115     mock_central_manager_ = [[MockCentralManager alloc] init];
116     [mock_central_manager_ setState:desired_state];
117     adapter_mac_->SetCentralManagerForTesting(mock_central_manager_);
118     return true;
119   }
121   void AddDiscoverySession(BluetoothDiscoveryFilter* discovery_filter) {
122     adapter_mac_->AddDiscoverySession(
123         discovery_filter,
124         base::Bind(&BluetoothAdapterMacTest::Callback, base::Unretained(this)),
125         base::Bind(&BluetoothAdapterMacTest::ErrorCallback,
126                    base::Unretained(this)));
127   }
129   void RemoveDiscoverySession(BluetoothDiscoveryFilter* discovery_filter) {
130     adapter_mac_->RemoveDiscoverySession(
131         discovery_filter,
132         base::Bind(&BluetoothAdapterMacTest::Callback, base::Unretained(this)),
133         base::Bind(&BluetoothAdapterMacTest::ErrorCallback,
134                    base::Unretained(this)));
135   }
137   int NumDiscoverySessions() { return adapter_mac_->num_discovery_sessions_; }
139   // Generic callbacks.
140   void Callback() { ++callback_count_; }
141   void ErrorCallback() { ++error_callback_count_; }
143  protected:
144   scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_;
145   scoped_refptr<BluetoothAdapter> adapter_;
146   BluetoothAdapterMac* adapter_mac_;
148   // Owned by |adapter_mac_|.
149   id mock_central_manager_ = NULL;
151   int callback_count_;
152   int error_callback_count_;
155 TEST_F(BluetoothAdapterMacTest, Poll) {
156   PollAdapter();
157   EXPECT_FALSE(ui_task_runner_->GetPendingTasks().empty());
160 TEST_F(BluetoothAdapterMacTest, AddDiscoverySessionWithLowEnergyFilter) {
161   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
162     return;
163   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
164   EXPECT_EQ(0, NumDiscoverySessions());
166   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
167       new BluetoothDiscoveryFilter(
168           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
169   AddDiscoverySession(discovery_filter.get());
170   EXPECT_EQ(1, callback_count_);
171   EXPECT_EQ(0, error_callback_count_);
172   EXPECT_EQ(1, NumDiscoverySessions());
174   // Check that adding a discovery session resulted in
175   // scanForPeripheralsWithServices being called on the Central Manager.
176   EXPECT_EQ(1, [mock_central_manager_ scanForPeripheralsCallCount]);
179 // TODO(krstnmnlsn): Test changing the filter when adding the second discovery
180 // session (once we have that ability).
181 TEST_F(BluetoothAdapterMacTest, AddSecondDiscoverySessionWithLowEnergyFilter) {
182   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
183     return;
184   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
185       new BluetoothDiscoveryFilter(
186           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
187   AddDiscoverySession(discovery_filter.get());
188   EXPECT_EQ(1, callback_count_);
189   EXPECT_EQ(0, error_callback_count_);
190   EXPECT_EQ(1, NumDiscoverySessions());
192   // We replaced the success callback handed to AddDiscoverySession, so
193   // |adapter_mac_| should remain in a discovering state indefinitely.
194   EXPECT_TRUE(adapter_mac_->IsDiscovering());
196   AddDiscoverySession(discovery_filter.get());
197   EXPECT_EQ(2, [mock_central_manager_ scanForPeripheralsCallCount]);
198   EXPECT_EQ(2, callback_count_);
199   EXPECT_EQ(0, error_callback_count_);
200   EXPECT_EQ(2, NumDiscoverySessions());
203 TEST_F(BluetoothAdapterMacTest, RemoveDiscoverySessionWithLowEnergyFilter) {
204   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
205     return;
206   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
208   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
209       new BluetoothDiscoveryFilter(
210           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
211   AddDiscoverySession(discovery_filter.get());
212   EXPECT_EQ(1, callback_count_);
213   EXPECT_EQ(0, error_callback_count_);
214   EXPECT_EQ(1, NumDiscoverySessions());
216   EXPECT_EQ(0, [mock_central_manager_ stopScanCallCount]);
217   RemoveDiscoverySession(discovery_filter.get());
218   EXPECT_EQ(2, callback_count_);
219   EXPECT_EQ(0, error_callback_count_);
220   EXPECT_EQ(0, NumDiscoverySessions());
222   // Check that removing the discovery session resulted in stopScan being called
223   // on the Central Manager.
224   EXPECT_EQ(1, [mock_central_manager_ stopScanCallCount]);
227 TEST_F(BluetoothAdapterMacTest, RemoveDiscoverySessionWithLowEnergyFilterFail) {
228   if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
229     return;
230   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
231   EXPECT_EQ(0, [mock_central_manager_ stopScanCallCount]);
232   EXPECT_EQ(0, NumDiscoverySessions());
234   scoped_ptr<BluetoothDiscoveryFilter> discovery_filter(
235       new BluetoothDiscoveryFilter(
236           BluetoothDiscoveryFilter::Transport::TRANSPORT_LE));
237   RemoveDiscoverySession(discovery_filter.get());
238   EXPECT_EQ(0, callback_count_);
239   EXPECT_EQ(1, error_callback_count_);
240   EXPECT_EQ(0, NumDiscoverySessions());
242   // Check that stopScan was not called.
243   EXPECT_EQ(0, [mock_central_manager_ stopScanCallCount]);
246 TEST_F(BluetoothAdapterMacTest, CheckGetPeripheralHashAddress) {
247   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
248   if (mock_peripheral.get() == nil)
249     return;
250   EXPECT_EQ(kTestHashAddress, GetHashAddress(mock_peripheral));
253 TEST_F(BluetoothAdapterMacTest, LowEnergyDeviceUpdatedNewDevice) {
254   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
255   if (mock_peripheral.get() == nil)
256     return;
257   base::scoped_nsobject<NSDictionary> advertisement_data(
258       CreateAdvertisementData());
260   EXPECT_EQ(0, NumDevices());
261   EXPECT_FALSE(DevicePresent(mock_peripheral));
262   LowEnergyDeviceUpdated(mock_peripheral, advertisement_data, kTestRssi);
263   EXPECT_EQ(1, NumDevices());
264   EXPECT_TRUE(DevicePresent(mock_peripheral));
267 TEST_F(BluetoothAdapterMacTest, LowEnergyDeviceUpdatedOldDevice) {
268   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
269   if (mock_peripheral.get() == nil)
270     return;
271   base::scoped_nsobject<NSDictionary> advertisement_data(
272       CreateAdvertisementData());
274   // Update the device for the first time and check it was correctly added to
275   // |devices_|.
276   EXPECT_EQ(0, NumDevices());
277   EXPECT_FALSE(DevicePresent(mock_peripheral));
278   LowEnergyDeviceUpdated(mock_peripheral, advertisement_data, kTestRssi);
279   EXPECT_EQ(1, NumDevices());
280   EXPECT_TRUE(DevicePresent(mock_peripheral));
281   // Search for the device by the address corresponding to |kTestNSUUID|.
282   BluetoothDeviceMac* device =
283       static_cast<BluetoothDeviceMac*>(GetDevice(kTestHashAddress));
284   base::scoped_nsobject<NSDate> first_update_time(
285       [device->GetLastUpdateTime() retain]);
287   // Update the device a second time. The device should be updated in
288   // |devices_| so check the time returned by GetLastUpdateTime() has increased.
289   LowEnergyDeviceUpdated(mock_peripheral, advertisement_data, kTestRssi);
290   EXPECT_EQ(1, NumDevices());
291   EXPECT_TRUE(DevicePresent(mock_peripheral));
292   device = static_cast<BluetoothDeviceMac*>(GetDevice(kTestHashAddress));
293   EXPECT_TRUE([device->GetLastUpdateTime() compare:first_update_time] ==
294               NSOrderedDescending);
297 TEST_F(BluetoothAdapterMacTest, UpdateDevicesRemovesLowEnergyDevice) {
298   base::scoped_nsobject<id> mock_peripheral(CreateMockPeripheral(kTestNSUUID));
299   if (mock_peripheral.get() == nil)
300     return;
301   base::scoped_nsobject<NSDictionary> advertisement_data(
302       CreateAdvertisementData());
304   BluetoothLowEnergyDeviceMac* device = new BluetoothLowEnergyDeviceMac(
305       mock_peripheral, advertisement_data, kTestRssi);
306   SetDeviceTimeGreaterThanTimeout(device);
308   EXPECT_EQ(0, NumDevices());
309   AddLowEnergyDevice(device);
310   EXPECT_EQ(1, NumDevices());
311   EXPECT_TRUE(DevicePresent(mock_peripheral));
313   // Check that object pointed to by |device| is deleted by the adapter.
314   RemoveTimedOutDevices();
315   EXPECT_EQ(0, NumDevices());
316   EXPECT_FALSE(DevicePresent(mock_peripheral));
319 }  // namespace device