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