| // Copyright 2014 The Chromium Authors |
| // 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/41266338): 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*>(nullptr))); |
| |
| // 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", ReplyBehavior::kWillReply); |
| 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", |
| ReplyBehavior::kWillReply); |
| 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", ReplyBehavior::kWillReply); |
| 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(); |
| } |