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 "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
22 using testing::NiceMock
;
23 using testing::Return
;
24 using testing::StrictMock
;
26 namespace proximity_auth
{
29 const char kDeviceName
[] = "Device name";
30 const char kBluetoothAddress
[] = "11:22:33:44:55:66";
31 const RemoteDevice kRemoteDevice
= {kDeviceName
, kBluetoothAddress
};
33 const char kUuid
[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEF";
35 class MockConnection
: public Connection
{
37 MockConnection() : Connection(kRemoteDevice
) {}
38 virtual ~MockConnection() {}
40 MOCK_METHOD0(Connect
, void());
42 using Connection::SetStatus
;
46 void SendMessageImpl(scoped_ptr
<WireMessage
> message
) {}
48 DISALLOW_COPY_AND_ASSIGN(MockConnection
);
51 class MockBluetoothConnectionFinder
: public BluetoothConnectionFinder
{
53 MockBluetoothConnectionFinder()
54 : BluetoothConnectionFinder(kRemoteDevice
,
55 device::BluetoothUUID(kUuid
),
57 virtual ~MockBluetoothConnectionFinder() {}
59 MOCK_METHOD0(CreateConnectionProxy
, Connection
*());
61 // Creates a mock connection and sets an expectation that the mock connection
62 // finder's CreateConnection() method will be called and will return the
63 // created connection. Returns a reference to the created connection.
64 // NOTE: The returned connection's lifetime is managed by the connection
66 MockConnection
* ExpectCreateConnection() {
67 scoped_ptr
<MockConnection
> connection(new NiceMock
<MockConnection
>());
68 MockConnection
* connection_alias
= connection
.get();
69 EXPECT_CALL(*this, CreateConnectionProxy())
70 .WillOnce(Return(connection
.release()));
71 return connection_alias
;
74 using BluetoothConnectionFinder::AdapterPresentChanged
;
75 using BluetoothConnectionFinder::AdapterPoweredChanged
;
78 virtual scoped_ptr
<Connection
> CreateConnection() override
{
79 return make_scoped_ptr(CreateConnectionProxy());
83 DISALLOW_COPY_AND_ASSIGN(MockBluetoothConnectionFinder
);
88 class ProximityAuthBluetoothConnectionFinderTest
: public testing::Test
{
90 ProximityAuthBluetoothConnectionFinderTest()
91 : adapter_(new NiceMock
<device::MockBluetoothAdapter
>),
92 connection_callback_(base::Bind(
93 &ProximityAuthBluetoothConnectionFinderTest::OnConnectionFound
,
94 base::Unretained(this))) {
95 device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_
);
97 // By default, configure the environment to allow polling. Individual tests
98 // can override this as needed.
99 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
100 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
103 MOCK_METHOD1(OnConnectionFoundProxy
, void(Connection
* connection
));
104 void OnConnectionFound(scoped_ptr
<Connection
> connection
) {
105 OnConnectionFoundProxy(connection
.get());
106 last_found_connection_
= connection
.Pass();
109 scoped_refptr
<device::MockBluetoothAdapter
> adapter_
;
110 ConnectionFinder::ConnectionCallback connection_callback_
;
113 // Save a pointer to the last found connection, to extend its lifetime.
114 scoped_ptr
<Connection
> last_found_connection_
;
116 base::MessageLoop message_loop_
;
119 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
120 ConstructAndDestroyDoesntCrash
) {
121 // Destroying a BluetoothConnectionFinder for which Find() has not been called
123 BluetoothConnectionFinder
connection_finder(
125 device::BluetoothUUID(kUuid
),
126 base::TimeDelta::FromMilliseconds(1));
129 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_NoBluetoothAdapter
) {
130 // Some platforms do not support Bluetooth. This test is only meaningful on
133 if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
136 // The StrictMock will verify that no connection is created.
137 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
138 connection_finder
.Find(connection_callback_
);
141 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
142 Find_BluetoothAdapterNotPresent
) {
143 // The StrictMock will verify that no connection is created.
144 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
145 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
146 connection_finder
.Find(connection_callback_
);
149 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
150 Find_BluetoothAdapterNotPowered
) {
151 // The StrictMock will verify that no connection is created.
152 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
153 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(false));
154 connection_finder
.Find(connection_callback_
);
157 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_ConnectionSucceeds
) {
158 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
160 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
161 connection_finder
.Find(connection_callback_
);
163 connection
->SetStatus(Connection::IN_PROGRESS
);
165 EXPECT_CALL(*this, OnConnectionFoundProxy(_
));
166 connection
->SetStatus(Connection::CONNECTED
);
169 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
170 Find_ConnectionSucceeds_UnregistersAsObserver
) {
171 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
173 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
174 connection_finder
.Find(connection_callback_
);
176 connection
->SetStatus(Connection::IN_PROGRESS
);
178 EXPECT_CALL(*this, OnConnectionFoundProxy(_
));
179 EXPECT_CALL(*adapter_
, RemoveObserver(&connection_finder
));
180 connection
->SetStatus(Connection::CONNECTED
);
182 // If for some reason the connection sends more status updates, they should be
184 EXPECT_CALL(*this, OnConnectionFoundProxy(_
)).Times(0);
185 connection
->SetStatus(Connection::IN_PROGRESS
);
186 connection
->SetStatus(Connection::CONNECTED
);
189 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
190 Find_ConnectionFails_PostsTaskToPollAgain
) {
191 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
193 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
194 connection_finder
.Find(connection_callback_
);
196 // Simulate a connection that fails to connect.
197 connection
->SetStatus(Connection::IN_PROGRESS
);
198 connection
->SetStatus(Connection::DISCONNECTED
);
200 // A task should have been posted to poll again.
201 base::RunLoop run_loop
;
202 connection_finder
.ExpectCreateConnection();
203 run_loop
.RunUntilIdle();
206 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_PollsOnAdapterPresent
) {
207 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
209 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
210 EXPECT_CALL(connection_finder
, CreateConnectionProxy()).Times(0);
211 connection_finder
.Find(connection_callback_
);
213 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
214 connection_finder
.ExpectCreateConnection();
215 connection_finder
.AdapterPresentChanged(adapter_
.get(), true);
218 TEST_F(ProximityAuthBluetoothConnectionFinderTest
, Find_PollsOnAdapterPowered
) {
219 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
221 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(false));
222 EXPECT_CALL(connection_finder
, CreateConnectionProxy()).Times(0);
223 connection_finder
.Find(connection_callback_
);
225 ON_CALL(*adapter_
, IsPowered()).WillByDefault(Return(true));
226 connection_finder
.ExpectCreateConnection();
227 connection_finder
.AdapterPoweredChanged(adapter_
.get(), true);
230 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
231 Find_DoesNotPollIfConnectionPending
) {
232 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
234 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
235 connection_finder
.Find(connection_callback_
);
237 connection
->SetStatus(Connection::IN_PROGRESS
);
239 // At this point, there is a pending connection in progress. Hence, an event
240 // that would normally trigger a new polling iteration should not do so now,
241 // because the delay interval between successive polling attempts has not yet
243 EXPECT_CALL(connection_finder
, CreateConnectionProxy()).Times(0);
244 connection_finder
.AdapterPresentChanged(adapter_
.get(), true);
247 TEST_F(ProximityAuthBluetoothConnectionFinderTest
,
248 Find_ConnectionFails_PostsTaskToPollAgain_PollWaitsForTask
) {
249 StrictMock
<MockBluetoothConnectionFinder
> connection_finder
;
251 MockConnection
* connection
= connection_finder
.ExpectCreateConnection();
252 connection_finder
.Find(connection_callback_
);
254 connection
->SetStatus(Connection::IN_PROGRESS
);
255 connection
->SetStatus(Connection::DISCONNECTED
);
257 // At this point, there is a pending poll scheduled. Hence, an event that
258 // would normally trigger a new polling iteration should not do so now,
259 // because the delay interval between successive polling attempts has not yet
261 EXPECT_CALL(connection_finder
, CreateConnectionProxy()).Times(0);
262 connection_finder
.AdapterPresentChanged(adapter_
.get(), true);
264 // Now, allow the pending task to run, but fail early, so that no new task is
266 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(false));
267 base::RunLoop run_loop
;
268 run_loop
.RunUntilIdle();
269 ON_CALL(*adapter_
, IsPresent()).WillByDefault(Return(true));
271 // Now that there is no pending task, events should once again trigger new
272 // polling iterations.
273 connection_finder
.ExpectCreateConnection();
274 connection_finder
.AdapterPresentChanged(adapter_
.get(), true);
277 } // namespace proximity_auth