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 <string>
6
7#include "base/memory/ref_counted.h"
8#include "base/memory/scoped_ptr.h"
9#include "chrome/browser/extensions/extension_apitest.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/test/base/ui_test_utils.h"
13#include "device/bluetooth/bluetooth_adapter_factory.h"
14#include "device/bluetooth/bluetooth_uuid.h"
15#include "device/bluetooth/test/mock_bluetooth_adapter.h"
16#include "device/bluetooth/test/mock_bluetooth_device.h"
17#include "device/bluetooth/test/mock_bluetooth_socket.h"
18#include "extensions/browser/api/bluetooth_socket/bluetooth_socket_api.h"
19#include "extensions/common/test_util.h"
20#include "extensions/test/extension_test_message_listener.h"
21#include "extensions/test/result_catcher.h"
22#include "testing/gmock/include/gmock/gmock.h"
23
24using device::BluetoothAdapter;
25using device::BluetoothAdapterFactory;
26using device::BluetoothDevice;
27using device::BluetoothSocket;
28using device::BluetoothUUID;
29using device::MockBluetoothAdapter;
30using device::MockBluetoothDevice;
31using device::MockBluetoothSocket;
32using extensions::Extension;
33using extensions::ResultCatcher;
34
35namespace api = extensions::core_api;
36
37namespace {
38
39class BluetoothSocketApiTest : public ExtensionApiTest {
40 public:
41  BluetoothSocketApiTest() {}
42
43  virtual void SetUpOnMainThread() OVERRIDE {
44    ExtensionApiTest::SetUpOnMainThread();
45    empty_extension_ = extensions::test_util::CreateEmptyExtension();
46    SetUpMockAdapter();
47  }
48
49  void SetUpMockAdapter() {
50    // The browser will clean this up when it is torn down.
51    mock_adapter_ = new testing::StrictMock<MockBluetoothAdapter>();
52    BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_);
53
54    mock_device1_.reset(
55        new testing::NiceMock<MockBluetoothDevice>(mock_adapter_.get(),
56                                                   0,
57                                                   "d1",
58                                                   "11:12:13:14:15:16",
59                                                   true /* paired */,
60                                                   false /* connected */));
61    mock_device2_.reset(
62        new testing::NiceMock<MockBluetoothDevice>(mock_adapter_.get(),
63                                                   0,
64                                                   "d2",
65                                                   "21:22:23:24:25:26",
66                                                   true /* paired */,
67                                                   false /* connected */));
68  }
69
70 protected:
71  scoped_refptr<testing::StrictMock<MockBluetoothAdapter> > mock_adapter_;
72  scoped_ptr<testing::NiceMock<MockBluetoothDevice> > mock_device1_;
73  scoped_ptr<testing::NiceMock<MockBluetoothDevice> > mock_device2_;
74
75 private:
76  scoped_refptr<Extension> empty_extension_;
77};
78
79// testing::InvokeArgument<N> does not work with base::Callback, fortunately
80// gmock makes it simple to create action templates that do for the various
81// possible numbers of arguments.
82ACTION_TEMPLATE(InvokeCallbackArgument,
83                HAS_1_TEMPLATE_PARAMS(int, k),
84                AND_0_VALUE_PARAMS()) {
85  ::std::tr1::get<k>(args).Run();
86}
87
88ACTION_TEMPLATE(InvokeCallbackArgument,
89                HAS_1_TEMPLATE_PARAMS(int, k),
90                AND_1_VALUE_PARAMS(p0)) {
91  ::std::tr1::get<k>(args).Run(p0);
92}
93
94ACTION_TEMPLATE(InvokeCallbackArgument,
95                HAS_1_TEMPLATE_PARAMS(int, k),
96                AND_2_VALUE_PARAMS(p0, p1)) {
97  ::std::tr1::get<k>(args).Run(p0, p1);
98}
99
100}  // namespace
101
102IN_PROC_BROWSER_TEST_F(BluetoothSocketApiTest, Connect) {
103  ResultCatcher catcher;
104  catcher.RestrictToBrowserContext(browser()->profile());
105
106  // Return the right mock device object for the address used by the test,
107  // return NULL for the "Device not found" test.
108  EXPECT_CALL(*mock_adapter_.get(), GetDevice(mock_device1_->GetAddress()))
109      .WillRepeatedly(testing::Return(mock_device1_.get()));
110  EXPECT_CALL(*mock_adapter_.get(), GetDevice(std::string("aa:aa:aa:aa:aa:aa")))
111      .WillOnce(testing::Return(static_cast<BluetoothDevice*>(NULL)));
112
113  // Return a mock socket object as a successful result to the connect() call.
114  BluetoothUUID service_uuid("8e3ad063-db38-4289-aa8f-b30e4223cf40");
115  scoped_refptr<testing::StrictMock<MockBluetoothSocket> > mock_socket
116      = new testing::StrictMock<MockBluetoothSocket>();
117  EXPECT_CALL(*mock_device1_,
118              ConnectToService(service_uuid, testing::_, testing::_))
119      .WillOnce(InvokeCallbackArgument<1>(mock_socket));
120
121  // Since the socket is unpaused, expect a call to Receive() from the socket
122  // dispatcher. Since there is no data, this will not call its callback.
123  EXPECT_CALL(*mock_socket.get(), Receive(testing::_, testing::_, testing::_));
124
125  // The test also cleans up by calling Disconnect and Close.
126  EXPECT_CALL(*mock_socket.get(), Disconnect(testing::_))
127      .WillOnce(InvokeCallbackArgument<0>());
128  EXPECT_CALL(*mock_socket.get(), Close());
129
130  // Run the test.
131  ExtensionTestMessageListener listener("ready", true);
132  scoped_refptr<const Extension> extension(
133      LoadExtension(test_data_dir_.AppendASCII("bluetooth_socket/connect")));
134  ASSERT_TRUE(extension.get());
135  EXPECT_TRUE(listener.WaitUntilSatisfied());
136
137  listener.Reply("go");
138  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
139}
140
141#if defined(_LIBCPP_VERSION)
142// This test fails in libc++ builds, see http://crbug.com/392205.
143#define MAYBE_Listen DISABLED_Listen
144#else
145#define MAYBE_Listen Listen
146#endif
147IN_PROC_BROWSER_TEST_F(BluetoothSocketApiTest, MAYBE_Listen) {
148  ResultCatcher catcher;
149  catcher.RestrictToBrowserContext(browser()->profile());
150
151  // Return a mock socket object as a successful result to the create service
152  // call.
153  BluetoothUUID service_uuid("2de497f9-ab28-49db-b6d2-066ea69f1737");
154  scoped_refptr<testing::StrictMock<MockBluetoothSocket> > mock_server_socket
155      = new testing::StrictMock<MockBluetoothSocket>();
156  BluetoothAdapter::ServiceOptions service_options;
157  service_options.name.reset(new std::string("MyServiceName"));
158  EXPECT_CALL(
159      *mock_adapter_.get(),
160      CreateRfcommService(
161          service_uuid,
162          testing::Field(&BluetoothAdapter::ServiceOptions::name,
163                         testing::Pointee(testing::Eq("MyServiceName"))),
164          testing::_,
165          testing::_)).WillOnce(InvokeCallbackArgument<2>(mock_server_socket));
166
167  // Since the socket is unpaused, expect a call to Accept() from the socket
168  // dispatcher. We'll immediately send back another mock socket to represent
169  // the client API. Further calls will return no data and behave as if
170  // pending.
171  scoped_refptr<testing::StrictMock<MockBluetoothSocket> > mock_client_socket
172      = new testing::StrictMock<MockBluetoothSocket>();
173  EXPECT_CALL(*mock_server_socket.get(), Accept(testing::_, testing::_))
174      .Times(2)
175      .WillOnce(
176           InvokeCallbackArgument<0>(mock_device1_.get(), mock_client_socket))
177      .WillOnce(testing::Return());
178
179  // Run the test, it sends a ready signal once it's ready for us to dispatch
180  // a client connection to it.
181  ExtensionTestMessageListener socket_listening("ready", true);
182  scoped_refptr<const Extension> extension(
183      LoadExtension(test_data_dir_.AppendASCII("bluetooth_socket/listen")));
184  ASSERT_TRUE(extension.get());
185  EXPECT_TRUE(socket_listening.WaitUntilSatisfied());
186
187  // Connection events are dispatched using a couple of PostTask to the UI
188  // thread. Waiting until idle ensures the event is dispatched to the
189  // receiver(s).
190  base::RunLoop().RunUntilIdle();
191  ExtensionTestMessageListener listener("ready", true);
192  socket_listening.Reply("go");
193
194  // Second stage of tests checks for error conditions, and will clean up
195  // the existing server and client sockets.
196  EXPECT_CALL(*mock_server_socket.get(), Disconnect(testing::_))
197      .WillOnce(InvokeCallbackArgument<0>());
198  EXPECT_CALL(*mock_server_socket.get(), Close());
199
200  EXPECT_CALL(*mock_client_socket.get(), Disconnect(testing::_))
201      .WillOnce(InvokeCallbackArgument<0>());
202  EXPECT_CALL(*mock_client_socket.get(), Close());
203
204  EXPECT_TRUE(listener.WaitUntilSatisfied());
205  listener.Reply("go");
206  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
207}
208
209IN_PROC_BROWSER_TEST_F(BluetoothSocketApiTest, PermissionDenied) {
210  ResultCatcher catcher;
211  catcher.RestrictToBrowserContext(browser()->profile());
212
213  // Run the test.
214  scoped_refptr<const Extension> extension(
215      LoadExtension(test_data_dir_.AppendASCII(
216          "bluetooth_socket/permission_denied")));
217  ASSERT_TRUE(extension.get());
218
219  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
220}
221