1 // Copyright 2014 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 "components/proximity_auth/bluetooth_connection_finder.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/time/time.h"
13 #include "components/proximity_auth/remote_device.h"
14 #include "components/proximity_auth/wire_message.h"
15 #include "device/bluetooth/bluetooth_adapter_factory.h"
16 #include "device/bluetooth/bluetooth_uuid.h"
17 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
18 #include "device/bluetooth/test/mock_bluetooth_device.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
23 using testing::NiceMock
;
24 using testing::Return
;
25 using testing::StrictMock
;
27 namespace proximity_auth
{
30 const char kDeviceName
[] = "Device name";
31 const char kPublicKey
[] = "Public key";
32 const char kBluetoothAddress
[] = "11:22:33:44:55:66";
33 const char kPersistentSymmetricKey
[] = "PSK";
35 const char kUuid
[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEF";
37 RemoteDevice
CreateRemoteDevice() {
38 return RemoteDevice(kDeviceName
, kPublicKey
, kBluetoothAddress
,
39 kPersistentSymmetricKey
);
42 class MockConnection
: public Connection
{
44 MockConnection() : Connection(CreateRemoteDevice()), do_not_destroy_(false) {}
45 ~MockConnection() override
{ EXPECT_FALSE(do_not_destroy_
); }
47 MOCK_METHOD0(Connect
, void());
49 void SetStatus(Connection::Status status
) {
50 // This object should not be destroyed after setting the status and calling
52 do_not_destroy_
= true;
53 Connection::SetStatus(status
);
54 do_not_destroy_
= false;
58 void Disconnect() override
{}
59 void SendMessageImpl(scoped_ptr
<WireMessage
> message
) override
{}
61 // If true, we do not expect |this| object to be destroyed until this value is
62 // toggled back to false.
65 DISALLOW_COPY_AND_ASSIGN(MockConnection
);
68 class MockBluetoothConnectionFinder
: public BluetoothConnectionFinder
{
70 MockBluetoothConnectionFinder()
71 : BluetoothConnectionFinder(CreateRemoteDevice(),
72 device::BluetoothUUID(kUuid
),
74 ~MockBluetoothConnectionFinder() override
{}
76 MOCK_METHOD0(CreateConnectionProxy
, Connection
*());
78 // Creates a mock connection and sets an expectation that the mock connection
79 // finder's CreateConnection() method will be called and will return the
80 // created connection. Returns a reference to the created connection.
81 // NOTE: The returned connection's lifetime is managed by the connection
83 MockConnection
* ExpectCreateConnection() {
84 scoped_ptr
<MockConnection
> connection(new NiceMock
<MockConnection
>());
85 MockConnection
* connection_alias
= connection
.get();
86 EXPECT_CALL(*this, CreateConnectionProxy())
87 .WillOnce(Return(connection
.release()));
88 return connection_alias
;
91 using BluetoothConnectionFinder::AdapterPresentChanged
;
92 using BluetoothConnectionFinder::AdapterPoweredChanged
;
94 void ClearSeekCallbacks() {
95 seek_callback_
= base::Closure();
96 seek_error_callback_
= bluetooth_util::ErrorCallback();
99 const base::Closure
& seek_callback() { return seek_callback_
; }
100 const bluetooth_util::ErrorCallback
& seek_error_callback() {
101 return seek_error_callback_
;
105 // BluetoothConnectionFinder:
106 scoped_ptr
<Connection
> CreateConnection() override
{
107 return make_scoped_ptr(CreateConnectionProxy());
110 void SeekDeviceByAddress(
111 const std::string
& bluetooth_address
,
112 const base::Closure
& callback
,
113 const bluetooth_util::ErrorCallback
& error_callback
) override
{
114 EXPECT_EQ(kBluetoothAddress
, bluetooth_address
);
115 seek_callback_
= callback
;
116 seek_error_callback_
= error_callback
;
120 base::Closure seek_callback_
;
121 bluetooth_util::ErrorCallback seek_error_callback_
;
123 DISALLOW_COPY_AND_ASSIGN(MockBluetoothConnectionFinder
);
128 class ProximityAuthBluetoothConnectionFinderTest
: public testing::Test
{
130 ProximityAuthBluetoothConnectionFinderTest()
131 : adapter_(new NiceMock
<device::MockBluetoothAdapter
>),
132 bluetooth_device_(new NiceMock
<device::MockBluetoothDevice
>(
134 device::BluetoothDevice::DEVICE_PHONE
,
139 connection_callback_(base::Bind(
140 &ProximityAuthBluetoothConnectionFinderTest::OnConnectionFound
,
141 base::Unretained(this))) {
142 device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_
);
144 // By default, configure the environment to allow polling. Individual tests
145 // can override this as needed.
146 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
147 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
149 // By default, the remote device is known to |adapter_| so
150 // |SeekDeviceByAddress()| will not be called.
151 ON_CALL(*adapter_
, GetDevice(kBluetoothAddress
))
152 .WillByDefault(Return(bluetooth_device_
.get()));
155 MOCK_METHOD1(OnConnectionFoundProxy
, void(Connection
* connection
));
156 void OnConnectionFound(scoped_ptr
<Connection
> connection
) {
157 OnConnectionFoundProxy(connection
.get());
158 last_found_connection_
= connection
.Pass();
161 // Starts |connection_finder_|. If |expect_connection| is true, then we set an
162 // expectation that an in-progress connection will be created and returned.
163 MockConnection
* StartConnectionFinder(bool expect_connection
) {
164 MockConnection
* connection
= nullptr;
165 if (expect_connection
)
166 connection
= connection_finder_
.ExpectCreateConnection();
167 connection_finder_
.Find(connection_callback_
);
171 // Given an in-progress |connection| returned by |StartConnectionFinder()|,
172 // simulate it transitioning to the CONNECTED state.
173 void SimulateDeviceConnection(MockConnection
* connection
) {
174 connection
->SetStatus(Connection::IN_PROGRESS
);
175 base::RunLoop run_loop
;
176 EXPECT_CALL(*this, OnConnectionFoundProxy(_
));
177 connection
->SetStatus(Connection::CONNECTED
);
178 run_loop
.RunUntilIdle();
181 scoped_refptr
<device::MockBluetoothAdapter
> adapter_
;
182 StrictMock
<MockBluetoothConnectionFinder
> connection_finder_
;
183 scoped_ptr
<device::MockBluetoothDevice
> bluetooth_device_
;
184 ConnectionFinder::ConnectionCallback connection_callback_
;
187 // Save a pointer to the last found connection, to extend its lifetime.
188 scoped_ptr
<Connection
> last_found_connection_
;
190 base::MessageLoop message_loop_
;
193 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
194 ConstructAndDestroyDoesntCrash
) {
195 // Destroying a BluetoothConnectionFinder for which Find() has not been called
197 BluetoothConnectionFinder
connection_finder(
198 CreateRemoteDevice(), device::BluetoothUUID(kUuid
),
199 base::TimeDelta::FromMilliseconds(1));
202 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_NoBluetoothAdapter
) {
203 // Some platforms do not support Bluetooth. This test is only meaningful on
206 if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
209 // The StrictMock will verify that no connection is created.
210 StartConnectionFinder(false);
213 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
214 Find_BluetoothAdapterNotPresent
) {
215 // The StrictMock will verify that no connection is created.
216 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
217 StartConnectionFinder(false);
220 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
221 Find_BluetoothAdapterNotPowered
) {
222 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(false));
223 // The StrictMock will verify that no connection is created.
224 StartConnectionFinder(false);
227 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_ConnectionSucceeds
) {
228 MockConnection
* connection
= StartConnectionFinder(true);
229 SimulateDeviceConnection(connection
);
232 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
233 Find_ConnectionSucceeds_UnregistersAsObserver
) {
234 MockConnection
* connection
= StartConnectionFinder(true);
235 SimulateDeviceConnection(connection
);
237 // If for some reason the connection sends more status updates, they should
239 base::RunLoop run_loop
;
240 EXPECT_CALL(*this, OnConnectionFoundProxy(_
)).Times(0);
241 connection
->SetStatus(Connection::IN_PROGRESS
);
242 connection
->SetStatus(Connection::CONNECTED
);
243 run_loop
.RunUntilIdle();
246 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
247 Find_ConnectionFails_PostsTaskToPollAgain
) {
248 MockConnection
* connection
= StartConnectionFinder(true);
250 // Simulate a connection that fails to connect.
251 connection
->SetStatus(Connection::IN_PROGRESS
);
252 connection
->SetStatus(Connection::DISCONNECTED
);
254 // A task should have been posted to poll again.
255 base::RunLoop run_loop
;
256 connection_finder_
.ExpectCreateConnection();
257 run_loop
.RunUntilIdle();
260 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_PollsOnAdapterPresent
) {
261 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
262 EXPECT_CALL(connection_finder_
, CreateConnectionProxy()).Times(0);
263 connection_finder_
.Find(connection_callback_
);
265 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
266 connection_finder_
.ExpectCreateConnection();
267 connection_finder_
.AdapterPresentChanged(adapter_
.get(), true);
270 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_PollsOnAdapterPowered
) {
271 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(false));
272 EXPECT_CALL(connection_finder_
, CreateConnectionProxy()).Times(0);
273 connection_finder_
.Find(connection_callback_
);
275 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
276 connection_finder_
.ExpectCreateConnection();
277 connection_finder_
.AdapterPoweredChanged(adapter_
.get(), true);
280 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
281 Find_DoesNotPollIfConnectionPending
) {
282 MockConnection
* connection
= StartConnectionFinder(true);
284 connection
->SetStatus(Connection::IN_PROGRESS
);
286 // At this point, there is a pending connection in progress. Hence, an event
287 // that would normally trigger a new polling iteration should not do so now,
288 // because the delay interval between successive polling attempts has not yet
290 EXPECT_CALL(connection_finder_
, CreateConnectionProxy()).Times(0);
291 connection_finder_
.AdapterPresentChanged(adapter_
.get(), true);
294 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
295 Find_ConnectionFails_PostsTaskToPollAgain_PollWaitsForTask
) {
296 MockConnection
* connection
= StartConnectionFinder(true);
298 connection
->SetStatus(Connection::IN_PROGRESS
);
299 connection
->SetStatus(Connection::DISCONNECTED
);
301 // At this point, there is a pending poll scheduled. Hence, an event that
302 // would normally trigger a new polling iteration should not do so now,
303 // because the delay interval between successive polling attempts has not yet
305 EXPECT_CALL(connection_finder_
, CreateConnectionProxy()).Times(0);
306 connection_finder_
.AdapterPresentChanged(adapter_
.get(), true);
308 // Now, allow the pending task to run, but fail early, so that no new task is
310 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
311 base::RunLoop run_loop
;
312 run_loop
.RunUntilIdle();
313 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
315 // Now that there is no pending task, events should once again trigger new
316 // polling iterations.
317 connection_finder_
.ExpectCreateConnection();
318 connection_finder_
.AdapterPresentChanged(adapter_
.get(), true);
321 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
322 Find_DeviceNotKnown_SeekDeviceSucceeds
) {
323 // If the BluetoothDevice is not known by the adapter, |connection_finder|
324 // will call SeekDeviceByAddress() first to make it known.
325 ON_CALL(*adapter_
, GetDevice(kBluetoothAddress
))
326 .WillByDefault(Return(nullptr));
327 connection_finder_
.Find(connection_callback_
);
328 ASSERT_FALSE(connection_finder_
.seek_callback().is_null());
329 EXPECT_FALSE(connection_finder_
.seek_error_callback().is_null());
331 // After seeking is successful, the normal flow should resume.
332 ON_CALL(*adapter_
, GetDevice(kBluetoothAddress
))
333 .WillByDefault(Return(bluetooth_device_
.get()));
334 MockConnection
* connection
= connection_finder_
.ExpectCreateConnection();
335 connection_finder_
.seek_callback().Run();
336 SimulateDeviceConnection(connection
);
339 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
340 Find_DeviceNotKnown_SeekDeviceFailThenSucceeds
) {
341 // If the BluetoothDevice is not known by the adapter, |connection_finder|
342 // will call SeekDeviceByAddress() first to make it known.
343 ON_CALL(*adapter_
, GetDevice(kBluetoothAddress
))
344 .WillByDefault(Return(nullptr));
345 connection_finder_
.Find(connection_callback_
);
346 EXPECT_FALSE(connection_finder_
.seek_callback().is_null());
347 ASSERT_FALSE(connection_finder_
.seek_error_callback().is_null());
349 // If the seek fails, then |connection_finder| will post a delayed poll to
350 // reattempt the seek.
351 connection_finder_
.seek_error_callback().Run("Seek failed for test.");
352 connection_finder_
.ClearSeekCallbacks();
353 EXPECT_TRUE(connection_finder_
.seek_callback().is_null());
354 EXPECT_TRUE(connection_finder_
.seek_error_callback().is_null());
356 // Check that seek is reattempted.
357 base::RunLoop run_loop
;
358 run_loop
.RunUntilIdle();
359 ASSERT_FALSE(connection_finder_
.seek_callback().is_null());
360 EXPECT_FALSE(connection_finder_
.seek_error_callback().is_null());
362 // Successfully connect to the Bluetooth device.
363 ON_CALL(*adapter_
, GetDevice(kBluetoothAddress
))
364 .WillByDefault(Return(bluetooth_device_
.get()));
365 MockConnection
* connection
= connection_finder_
.ExpectCreateConnection();
366 connection_finder_
.seek_callback().Run();
367 SimulateDeviceConnection(connection
);
370 } // namespace proximity_auth