|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <tuple> | 
|  |  | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/test/gmock_callback_support.h" | 
|  | #include "device/bluetooth/bluetooth_adapter_factory.h" | 
|  | #include "device/bluetooth/public/cpp/bluetooth_uuid.h" | 
|  | #include "device/bluetooth/test/mock_bluetooth_adapter.h" | 
|  | #include "device/bluetooth/test/mock_bluetooth_device.h" | 
|  | #include "device/bluetooth/test/mock_bluetooth_socket.h" | 
|  | #include "extensions/browser/api/bluetooth_socket/bluetooth_socket_api.h" | 
|  | #include "extensions/common/extension_builder.h" | 
|  | #include "extensions/shell/test/shell_apitest.h" | 
|  | #include "extensions/test/extension_test_message_listener.h" | 
|  | #include "extensions/test/result_catcher.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  |  | 
|  | using device::BluetoothAdapter; | 
|  | using device::BluetoothAdapterFactory; | 
|  | using device::BluetoothDevice; | 
|  | using device::BluetoothSocket; | 
|  | using device::BluetoothUUID; | 
|  | using device::MockBluetoothAdapter; | 
|  | using device::MockBluetoothDevice; | 
|  | using device::MockBluetoothSocket; | 
|  | using extensions::Extension; | 
|  | using extensions::ResultCatcher; | 
|  |  | 
|  | namespace api = extensions::api; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class BluetoothSocketApiTest : public extensions::ShellApiTest { | 
|  | public: | 
|  | BluetoothSocketApiTest() {} | 
|  |  | 
|  | void SetUpOnMainThread() override { | 
|  | ShellApiTest::SetUpOnMainThread(); | 
|  | empty_extension_ = extensions::ExtensionBuilder("Test").Build(); | 
|  | SetUpMockAdapter(); | 
|  | } | 
|  |  | 
|  | void SetUpMockAdapter() { | 
|  | // The browser will clean this up when it is torn down. | 
|  | mock_adapter_ = new testing::StrictMock<MockBluetoothAdapter>(); | 
|  | BluetoothAdapterFactory::SetAdapterForTesting(mock_adapter_); | 
|  |  | 
|  | mock_device1_ = std::make_unique<testing::NiceMock<MockBluetoothDevice>>( | 
|  | mock_adapter_.get(), 0, "d1", "11:12:13:14:15:16", true /* paired */, | 
|  | false /* connected */); | 
|  | mock_device2_ = std::make_unique<testing::NiceMock<MockBluetoothDevice>>( | 
|  | mock_adapter_.get(), 0, "d2", "21:22:23:24:25:26", true /* paired */, | 
|  | false /* connected */); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | scoped_refptr<testing::StrictMock<MockBluetoothAdapter> > mock_adapter_; | 
|  | std::unique_ptr<testing::NiceMock<MockBluetoothDevice>> mock_device1_; | 
|  | std::unique_ptr<testing::NiceMock<MockBluetoothDevice>> mock_device2_; | 
|  |  | 
|  | private: | 
|  | scoped_refptr<const Extension> empty_extension_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // TODO(crbug.com/632002): Flaky on many trybot platforms. | 
|  | IN_PROC_BROWSER_TEST_F(BluetoothSocketApiTest, DISABLED_Connect) { | 
|  | ResultCatcher catcher; | 
|  | catcher.RestrictToBrowserContext(browser_context()); | 
|  |  | 
|  | // Return the right mock device object for the address used by the test, | 
|  | // return NULL for the "Device not found" test. | 
|  | EXPECT_CALL(*mock_adapter_, GetDevice(mock_device1_->GetAddress())) | 
|  | .WillRepeatedly(testing::Return(mock_device1_.get())); | 
|  | EXPECT_CALL(*mock_adapter_, GetDevice(std::string("aa:aa:aa:aa:aa:aa"))) | 
|  | .WillOnce(testing::Return(static_cast<BluetoothDevice*>(NULL))); | 
|  |  | 
|  | // Return a mock socket object as a successful result to the connect() call. | 
|  | BluetoothUUID service_uuid("8e3ad063-db38-4289-aa8f-b30e4223cf40"); | 
|  | scoped_refptr<testing::StrictMock<MockBluetoothSocket> > mock_socket | 
|  | = new testing::StrictMock<MockBluetoothSocket>(); | 
|  | EXPECT_CALL(*mock_device1_, | 
|  | ConnectToService(service_uuid, testing::_, testing::_)) | 
|  | .WillOnce(base::test::RunOnceCallback<1>(mock_socket)); | 
|  |  | 
|  | // Since the socket is unpaused, expect a call to Receive() from the socket | 
|  | // dispatcher. Since there is no data, this will not call its callback. | 
|  | EXPECT_CALL(*mock_socket, Receive(testing::_, testing::_, testing::_)); | 
|  |  | 
|  | // The test also cleans up by calling Disconnect. | 
|  | EXPECT_CALL(*mock_socket, Disconnect(testing::_)) | 
|  | .WillOnce(base::test::RunOnceCallback<0>()); | 
|  |  | 
|  | // Run the test. | 
|  | ExtensionTestMessageListener listener("ready", true); | 
|  | scoped_refptr<const Extension> extension( | 
|  | LoadApp("api_test/bluetooth_socket/connect")); | 
|  | ASSERT_TRUE(extension.get()); | 
|  | EXPECT_TRUE(listener.WaitUntilSatisfied()); | 
|  |  | 
|  | listener.Reply("go"); | 
|  | EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); | 
|  | } | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(BluetoothSocketApiTest, Listen) { | 
|  | ResultCatcher catcher; | 
|  | catcher.RestrictToBrowserContext(browser_context()); | 
|  |  | 
|  | // Return a mock socket object as a successful result to the create service | 
|  | // call. | 
|  | BluetoothUUID service_uuid("2de497f9-ab28-49db-b6d2-066ea69f1737"); | 
|  | scoped_refptr<testing::StrictMock<MockBluetoothSocket> > mock_server_socket | 
|  | = new testing::StrictMock<MockBluetoothSocket>(); | 
|  | BluetoothAdapter::ServiceOptions service_options; | 
|  | service_options.name = "MyServiceName"; | 
|  | EXPECT_CALL( | 
|  | *mock_adapter_, | 
|  | CreateRfcommService( | 
|  | service_uuid, | 
|  | testing::Field(&BluetoothAdapter::ServiceOptions::name, | 
|  | testing::Eq("MyServiceName")), | 
|  | testing::_, testing::_)) | 
|  | .WillOnce(base::test::RunOnceCallback<2>(mock_server_socket)); | 
|  |  | 
|  | // Since the socket is unpaused, expect a call to Accept() from the socket | 
|  | // dispatcher. We'll immediately send back another mock socket to represent | 
|  | // the client API. Further calls will return no data and behave as if | 
|  | // pending. | 
|  | scoped_refptr<testing::StrictMock<MockBluetoothSocket> > mock_client_socket | 
|  | = new testing::StrictMock<MockBluetoothSocket>(); | 
|  | EXPECT_CALL(*mock_server_socket, Accept(testing::_, testing::_)) | 
|  | .Times(2) | 
|  | .WillOnce(base::test::RunOnceCallback<0>(mock_device1_.get(), | 
|  | mock_client_socket)) | 
|  | .WillOnce(testing::Return()); | 
|  |  | 
|  | // Run the test, it sends a ready signal once it's ready for us to dispatch | 
|  | // a client connection to it. | 
|  | ExtensionTestMessageListener socket_listening("ready", true); | 
|  | scoped_refptr<const Extension> extension( | 
|  | LoadApp("api_test/bluetooth_socket/listen")); | 
|  | ASSERT_TRUE(extension.get()); | 
|  | EXPECT_TRUE(socket_listening.WaitUntilSatisfied()); | 
|  |  | 
|  | // Connection events are dispatched using a couple of PostTask to the UI | 
|  | // thread. Waiting until idle ensures the event is dispatched to the | 
|  | // receiver(s). | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | ExtensionTestMessageListener listener("ready", true); | 
|  | socket_listening.Reply("go"); | 
|  |  | 
|  | // Second stage of tests checks for error conditions, and will clean up | 
|  | // the existing server and client sockets. | 
|  | EXPECT_CALL(*mock_server_socket, Disconnect(testing::_)) | 
|  | .WillOnce(base::test::RunOnceCallback<0>()); | 
|  |  | 
|  | EXPECT_CALL(*mock_client_socket, Disconnect(testing::_)) | 
|  | .WillOnce(base::test::RunOnceCallback<0>()); | 
|  |  | 
|  | EXPECT_TRUE(listener.WaitUntilSatisfied()); | 
|  | listener.Reply("go"); | 
|  | EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); | 
|  | } | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(BluetoothSocketApiTest, PermissionDenied) { | 
|  | ResultCatcher catcher; | 
|  | catcher.RestrictToBrowserContext(browser_context()); | 
|  |  | 
|  | // Run the test. | 
|  | scoped_refptr<const Extension> extension( | 
|  | LoadApp("api_test/bluetooth_socket/permission_denied")); | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); | 
|  | } |