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 24 using device::BluetoothAdapter; 25 using device::BluetoothAdapterFactory; 26 using device::BluetoothDevice; 27 using device::BluetoothSocket; 28 using device::BluetoothUUID; 29 using device::MockBluetoothAdapter; 30 using device::MockBluetoothDevice; 31 using device::MockBluetoothSocket; 32 using extensions::Extension; 33 using extensions::ResultCatcher; 34 35 namespace api = extensions::core_api; 36 37 namespace { 38 39 class 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. 82 ACTION_TEMPLATE(InvokeCallbackArgument, 83 HAS_1_TEMPLATE_PARAMS(int, k), 84 AND_0_VALUE_PARAMS()) { 85 ::std::tr1::get<k>(args).Run(); 86 } 87 88 ACTION_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 94 ACTION_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 102 IN_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 147 IN_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 209 IN_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