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.
4
5#include "components/proximity_auth/bluetooth_connection.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/numerics/safe_conversions.h"
9#include "base/run_loop.h"
10#include "components/proximity_auth/remote_device.h"
11#include "components/proximity_auth/wire_message.h"
12#include "device/bluetooth/bluetooth_adapter_factory.h"
13#include "device/bluetooth/bluetooth_uuid.h"
14#include "device/bluetooth/test/mock_bluetooth_adapter.h"
15#include "device/bluetooth/test/mock_bluetooth_device.h"
16#include "device/bluetooth/test/mock_bluetooth_socket.h"
17#include "net/base/io_buffer.h"
18#include "testing/gmock/include/gmock/gmock.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21using testing::_;
22using testing::AnyNumber;
23using testing::NiceMock;
24using testing::Ref;
25using testing::Return;
26using testing::SaveArg;
27using testing::StrictMock;
28
29namespace proximity_auth {
30namespace {
31
32const char kDeviceName[] = "Device name";
33const char kOtherDeviceName[] = "Other device name";
34
35const char kBluetoothAddress[] = "11:22:33:44:55:66";
36const char kOtherBluetoothAddress[] = "AA:BB:CC:DD:EE:FF";
37
38const char kSerializedMessage[] = "Yarrr, this be a serialized message. Yarr!";
39const int kSerializedMessageLength = strlen(kSerializedMessage);
40
41const char kUuid[] = "DEADBEEF-CAFE-FEED-FOOD-D15EA5EBEEF";
42
43const RemoteDevice kRemoteDevice = {kDeviceName, kBluetoothAddress};
44
45const int kReceiveBufferSize = 6;
46const char kReceiveBufferContents[] = "bytes";
47
48// Create a buffer for testing received data.
49scoped_refptr<net::IOBuffer> CreateReceiveBuffer() {
50  scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kReceiveBufferSize);
51  memcpy(buffer->data(), kReceiveBufferContents, kReceiveBufferSize);
52  return buffer;
53}
54
55class MockBluetoothConnection : public BluetoothConnection {
56 public:
57  MockBluetoothConnection()
58      : BluetoothConnection(kRemoteDevice, device::BluetoothUUID(kUuid)) {}
59
60  // Bluetooth dependencies.
61  typedef device::BluetoothDevice::ConnectToServiceCallback
62      ConnectToServiceCallback;
63  typedef device::BluetoothDevice::ConnectToServiceErrorCallback
64      ConnectToServiceErrorCallback;
65  MOCK_METHOD4(ConnectToService,
66               void(device::BluetoothDevice* device,
67                    const device::BluetoothUUID& uuid,
68                    const ConnectToServiceCallback& callback,
69                    const ConnectToServiceErrorCallback& error_callback));
70
71  // Calls back into the parent Connection class.
72  MOCK_METHOD1(SetStatusProxy, void(Status status));
73  MOCK_METHOD1(OnBytesReceived, void(const std::string& bytes));
74  MOCK_METHOD2(OnDidSendMessage,
75               void(const WireMessage& message, bool success));
76
77  virtual void SetStatus(Status status) OVERRIDE {
78    SetStatusProxy(status);
79    BluetoothConnection::SetStatus(status);
80  }
81
82  using BluetoothConnection::status;
83  using BluetoothConnection::Connect;
84  using BluetoothConnection::DeviceRemoved;
85  using BluetoothConnection::Disconnect;
86
87 private:
88  DISALLOW_COPY_AND_ASSIGN(MockBluetoothConnection);
89};
90
91class TestWireMessage : public WireMessage {
92 public:
93  TestWireMessage() : WireMessage("permit id", "payload") {}
94  virtual ~TestWireMessage() {}
95
96  virtual std::string Serialize() const OVERRIDE { return kSerializedMessage; }
97
98 private:
99  DISALLOW_COPY_AND_ASSIGN(TestWireMessage);
100};
101
102}  // namespace
103
104class ProximityAuthBluetoothConnectionTest : public testing::Test {
105 public:
106  ProximityAuthBluetoothConnectionTest()
107      : adapter_(new device::MockBluetoothAdapter),
108        device_(adapter_.get(), 0, kDeviceName, kBluetoothAddress, true, true),
109        socket_(new StrictMock<device::MockBluetoothSocket>),
110        uuid_(kUuid) {
111    device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_);
112
113    // Suppress uninteresting Gmock call warnings.
114    EXPECT_CALL(*adapter_, GetDevice(_)).Times(AnyNumber());
115  }
116
117  // Transition the connection into an in-progress state.
118  void BeginConnecting(MockBluetoothConnection* connection) {
119    EXPECT_EQ(Connection::DISCONNECTED, connection->status());
120
121    ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
122    EXPECT_CALL(*connection, SetStatusProxy(Connection::IN_PROGRESS));
123    EXPECT_CALL(*adapter_, AddObserver(connection));
124    EXPECT_CALL(*connection, ConnectToService(&device_, uuid_, _, _));
125    connection->Connect();
126
127    EXPECT_EQ(Connection::IN_PROGRESS, connection->status());
128  }
129
130  // Transition the connection into a connected state.
131  // Saves the success and error callbacks passed into OnReceive(), which can be
132  // accessed via receive_callback() and receive_success_callback().
133  void Connect(MockBluetoothConnection* connection) {
134    EXPECT_EQ(Connection::DISCONNECTED, connection->status());
135
136    device::BluetoothDevice::ConnectToServiceCallback callback;
137    ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
138    EXPECT_CALL(*connection, SetStatusProxy(Connection::IN_PROGRESS));
139    EXPECT_CALL(*adapter_, AddObserver(connection));
140    EXPECT_CALL(*connection, ConnectToService(_, _, _, _))
141        .WillOnce(SaveArg<2>(&callback));
142    connection->Connect();
143    ASSERT_FALSE(callback.is_null());
144
145    EXPECT_CALL(*connection, SetStatusProxy(Connection::CONNECTED));
146    EXPECT_CALL(*socket_, Receive(_, _, _))
147        .WillOnce(DoAll(SaveArg<1>(&receive_callback_),
148                        SaveArg<2>(&receive_error_callback_)));
149    callback.Run(socket_);
150
151    EXPECT_EQ(Connection::CONNECTED, connection->status());
152  }
153
154  device::BluetoothSocket::ReceiveCompletionCallback* receive_callback() {
155    return &receive_callback_;
156  }
157  device::BluetoothSocket::ReceiveErrorCompletionCallback*
158  receive_error_callback() {
159    return &receive_error_callback_;
160  }
161
162 protected:
163  // Mocks used for verifying interactions with the Bluetooth subsystem.
164  scoped_refptr<device::MockBluetoothAdapter> adapter_;
165  NiceMock<device::MockBluetoothDevice> device_;
166  scoped_refptr<StrictMock<device::MockBluetoothSocket>> socket_;
167
168  device::BluetoothUUID uuid_;
169
170 private:
171  base::MessageLoop message_loop_;
172
173  device::BluetoothSocket::ReceiveCompletionCallback receive_callback_;
174  device::BluetoothSocket::ReceiveErrorCompletionCallback
175      receive_error_callback_;
176};
177
178TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionWasInProgress) {
179  // Create an in-progress connection.
180  StrictMock<MockBluetoothConnection> connection;
181  BeginConnecting(&connection);
182
183  // A second call to Connect() should be ignored.
184  EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
185  connection.Connect();
186
187  // The connection cleans up after itself upon destruction.
188  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
189}
190
191TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionWasConnected) {
192  // Create a connected connection.
193  StrictMock<MockBluetoothConnection> connection;
194  Connect(&connection);
195
196  // A second call to Connect() should be ignored.
197  EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
198  connection.Connect();
199
200  // The connection disconnects and unregisters as an observer upon destruction.
201  EXPECT_CALL(*socket_, Disconnect(_));
202  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
203}
204
205TEST_F(ProximityAuthBluetoothConnectionTest, Connect_NoBluetoothAdapter) {
206  // Some platforms do not support Bluetooth. This test is only meaningful on
207  // those platforms.
208  adapter_ = NULL;
209  if (device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable())
210    return;
211
212  StrictMock<MockBluetoothConnection> connection;
213  EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
214  connection.Connect();
215}
216
217TEST_F(ProximityAuthBluetoothConnectionTest, Connect_DeviceMissing) {
218  StrictMock<MockBluetoothConnection> connection;
219
220  ON_CALL(*adapter_, GetDevice(_))
221      .WillByDefault(Return(static_cast<device::BluetoothDevice*>(NULL)));
222  EXPECT_CALL(connection, SetStatusProxy(Connection::IN_PROGRESS));
223  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
224  connection.Connect();
225}
226
227TEST_F(ProximityAuthBluetoothConnectionTest,
228       Connect_DeviceRemovedWhileConnecting) {
229  // Create an in-progress connection.
230  StrictMock<MockBluetoothConnection> connection;
231  BeginConnecting(&connection);
232
233  // Remove the device while the connection is in-progress. This should cause
234  // the connection to disconnect.
235  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
236  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
237  connection.DeviceRemoved(adapter_.get(), &device_);
238}
239
240TEST_F(ProximityAuthBluetoothConnectionTest,
241       Connect_OtherDeviceRemovedWhileConnecting) {
242  // Create an in-progress connection.
243  StrictMock<MockBluetoothConnection> connection;
244  BeginConnecting(&connection);
245
246  // Remove a device other than the one that is being connected to. This should
247  // not have any effect on the connection.
248  NiceMock<device::MockBluetoothDevice> other_device(
249      adapter_.get(), 0, kOtherDeviceName, kOtherBluetoothAddress, true, true);
250  EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
251  connection.DeviceRemoved(adapter_.get(), &other_device);
252
253  // The connection removes itself as an observer when it is destroyed.
254  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
255}
256
257TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionFails) {
258  StrictMock<MockBluetoothConnection> connection;
259
260  device::BluetoothDevice::ConnectToServiceErrorCallback error_callback;
261  ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
262  EXPECT_CALL(connection, SetStatusProxy(Connection::IN_PROGRESS));
263  EXPECT_CALL(*adapter_, AddObserver(&connection));
264  EXPECT_CALL(connection, ConnectToService(&device_, uuid_, _, _))
265      .WillOnce(SaveArg<3>(&error_callback));
266  connection.Connect();
267  ASSERT_FALSE(error_callback.is_null());
268
269  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
270  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
271  error_callback.Run("super descriptive error message");
272}
273
274TEST_F(ProximityAuthBluetoothConnectionTest, Connect_ConnectionSucceeds) {
275  StrictMock<MockBluetoothConnection> connection;
276  Connect(&connection);
277
278  // The connection disconnects and unregisters as an observer upon destruction.
279  EXPECT_CALL(*socket_, Disconnect(_));
280  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
281}
282
283TEST_F(ProximityAuthBluetoothConnectionTest,
284       Connect_ConnectionSucceeds_ThenDeviceRemoved) {
285  StrictMock<MockBluetoothConnection> connection;
286  Connect(&connection);
287
288  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
289  EXPECT_CALL(*socket_, Disconnect(_));
290  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
291  connection.DeviceRemoved(adapter_.get(), &device_);
292}
293
294TEST_F(ProximityAuthBluetoothConnectionTest,
295       Connect_ConnectionSucceeds_ReceiveData) {
296  StrictMock<MockBluetoothConnection> connection;
297  Connect(&connection);
298  ASSERT_TRUE(receive_callback() && !receive_callback()->is_null());
299
300  // Receive some data. Once complete, the connection should re-register to be
301  // ready receive more data.
302  scoped_refptr<net::IOBuffer> buffer = CreateReceiveBuffer();
303  EXPECT_CALL(
304      connection,
305      OnBytesReceived(std::string(kReceiveBufferContents, kReceiveBufferSize)));
306  EXPECT_CALL(*socket_, Receive(_, _, _));
307  receive_callback()->Run(kReceiveBufferSize, buffer);
308  base::RunLoop run_loop;
309  run_loop.RunUntilIdle();
310
311  // The connection disconnects and unregisters as an observer upon destruction.
312  EXPECT_CALL(*socket_, Disconnect(_));
313  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
314}
315
316TEST_F(ProximityAuthBluetoothConnectionTest,
317       Connect_ConnectionSucceeds_ReceiveDataAfterReceiveError) {
318  StrictMock<MockBluetoothConnection> connection;
319  Connect(&connection);
320  ASSERT_TRUE(receive_error_callback() && !receive_error_callback()->is_null());
321
322  // Simulate an error while receiving data. The connection should re-register
323  // to be ready receive more data despite the error.
324  device::BluetoothSocket::ReceiveCompletionCallback receive_callback;
325  EXPECT_CALL(*socket_, Receive(_, _, _))
326      .WillOnce(SaveArg<1>(&receive_callback));
327  receive_error_callback()->Run(device::BluetoothSocket::kSystemError,
328                                "The system is down. They're taking over!");
329  base::RunLoop run_loop;
330  run_loop.RunUntilIdle();
331  ASSERT_FALSE(receive_callback.is_null());
332
333  // Receive some data.
334  scoped_refptr<net::IOBuffer> buffer = CreateReceiveBuffer();
335  EXPECT_CALL(
336      connection,
337      OnBytesReceived(std::string(kReceiveBufferContents, kReceiveBufferSize)));
338  EXPECT_CALL(*socket_, Receive(_, _, _));
339  receive_callback.Run(kReceiveBufferSize, buffer);
340  base::RunLoop run_loop2;
341  run_loop2.RunUntilIdle();
342
343  // The connection disconnects and unregisters as an observer upon destruction.
344  EXPECT_CALL(*socket_, Disconnect(_));
345  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
346}
347
348TEST_F(ProximityAuthBluetoothConnectionTest,
349       Disconnect_ConnectionWasAlreadyDisconnected) {
350  StrictMock<MockBluetoothConnection> connection;
351  EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
352  connection.Disconnect();
353}
354
355TEST_F(ProximityAuthBluetoothConnectionTest,
356       Disconnect_ConnectionWasInProgress) {
357  // Create an in-progress connection.
358  StrictMock<MockBluetoothConnection> connection;
359  BeginConnecting(&connection);
360
361  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
362  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
363  connection.Disconnect();
364}
365
366TEST_F(ProximityAuthBluetoothConnectionTest,
367       Disconnect_ConnectionWasConnected) {
368  // Create a connected connection.
369  StrictMock<MockBluetoothConnection> connection;
370  Connect(&connection);
371
372  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
373  EXPECT_CALL(*socket_, Disconnect(_));
374  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
375  connection.Disconnect();
376}
377
378TEST_F(ProximityAuthBluetoothConnectionTest,
379       Connect_ThenDisconnectWhileInProgress_ThenBackingConnectionSucceeds) {
380  StrictMock<MockBluetoothConnection> connection;
381  device::BluetoothDevice::ConnectToServiceCallback callback;
382  ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(&device_));
383  EXPECT_CALL(connection, SetStatusProxy(Connection::IN_PROGRESS));
384  EXPECT_CALL(*adapter_, AddObserver(&connection));
385  EXPECT_CALL(connection, ConnectToService(&device_, uuid_, _, _))
386      .WillOnce(SaveArg<2>(&callback));
387  connection.Connect();
388  ASSERT_FALSE(callback.is_null());
389
390  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
391  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
392  connection.Disconnect();
393
394  EXPECT_CALL(connection, SetStatusProxy(_)).Times(0);
395  EXPECT_CALL(*socket_, Receive(_, _, _)).Times(0);
396  callback.Run(socket_);
397}
398
399TEST_F(ProximityAuthBluetoothConnectionTest,
400       SendMessage_SendsExpectedDataOverTheWire) {
401  // Create a connected connection.
402  StrictMock<MockBluetoothConnection> connection;
403  Connect(&connection);
404
405  scoped_refptr<net::IOBuffer> buffer;
406  scoped_ptr<TestWireMessage> wire_message(new TestWireMessage);
407  EXPECT_CALL(*socket_, Send(_, kSerializedMessageLength, _, _))
408      .WillOnce(SaveArg<0>(&buffer));
409  connection.SendMessage(wire_message.PassAs<WireMessage>());
410  ASSERT_TRUE(buffer.get());
411  EXPECT_EQ(kSerializedMessage,
412            std::string(buffer->data(), kSerializedMessageLength));
413
414  // The connection disconnects and unregisters as an observer upon destruction.
415  EXPECT_CALL(*socket_, Disconnect(_));
416  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
417}
418
419TEST_F(ProximityAuthBluetoothConnectionTest, SendMessage_Success) {
420  // Create a connected connection.
421  StrictMock<MockBluetoothConnection> connection;
422  Connect(&connection);
423
424  scoped_ptr<TestWireMessage> wire_message(new TestWireMessage);
425  // Ownership will be transfered below, so grab a reference here.
426  TestWireMessage* expected_wire_message = wire_message.get();
427
428  device::BluetoothSocket::SendCompletionCallback callback;
429  EXPECT_CALL(*socket_, Send(_, _, _, _)).WillOnce(SaveArg<2>(&callback));
430  connection.SendMessage(wire_message.PassAs<WireMessage>());
431  ASSERT_FALSE(callback.is_null());
432
433  EXPECT_CALL(connection, OnDidSendMessage(Ref(*expected_wire_message), true));
434  callback.Run(kSerializedMessageLength);
435
436  // The connection disconnects and unregisters as an observer upon destruction.
437  EXPECT_CALL(*socket_, Disconnect(_));
438  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
439}
440
441TEST_F(ProximityAuthBluetoothConnectionTest, SendMessage_Failure) {
442  // Create a connected connection.
443  StrictMock<MockBluetoothConnection> connection;
444  Connect(&connection);
445
446  scoped_ptr<TestWireMessage> wire_message(new TestWireMessage);
447  // Ownership will be transfered below, so grab a reference here.
448  TestWireMessage* expected_wire_message = wire_message.get();
449
450  device::BluetoothSocket::ErrorCompletionCallback error_callback;
451  EXPECT_CALL(*socket_, Send(_, _, _, _)).WillOnce(SaveArg<3>(&error_callback));
452  connection.SendMessage(wire_message.PassAs<WireMessage>());
453
454  ASSERT_FALSE(error_callback.is_null());
455  EXPECT_CALL(connection, OnDidSendMessage(Ref(*expected_wire_message), false));
456  EXPECT_CALL(connection, SetStatusProxy(Connection::DISCONNECTED));
457  EXPECT_CALL(*socket_, Disconnect(_));
458  EXPECT_CALL(*adapter_, RemoveObserver(&connection));
459  error_callback.Run("The most helpful of error messages");
460}
461
462}  // namespace proximity_auth
463