| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromecast/browser/bluetooth/cast_bluetooth_chooser.h" |
| |
| #include "base/test/mock_callback.h" |
| #include "base/test/task_environment.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace chromecast { |
| namespace { |
| |
| class SimpleDeviceAccessProvider : public mojom::BluetoothDeviceAccessProvider { |
| public: |
| SimpleDeviceAccessProvider() = default; |
| |
| SimpleDeviceAccessProvider(const SimpleDeviceAccessProvider&) = delete; |
| SimpleDeviceAccessProvider& operator=(const SimpleDeviceAccessProvider&) = |
| delete; |
| |
| ~SimpleDeviceAccessProvider() override = default; |
| |
| // mojom::BluetoothDeviceAccessProvider implementation: |
| void RequestDeviceAccess( |
| mojo::PendingRemote<mojom::BluetoothDeviceAccessProviderClient> client) |
| override { |
| DCHECK(!client_); |
| client_.Bind(std::move(client)); |
| client_.set_disconnect_handler(connection_closed_.Get()); |
| for (const auto& address : approved_devices_) |
| client_->GrantAccess(address); |
| } |
| |
| mojom::BluetoothDeviceAccessProviderClient* client() { |
| return client_ ? client_.get() : nullptr; |
| } |
| void reset_client() { return client_.reset(); } |
| base::MockCallback<base::OnceClosure>& connection_closed() { |
| return connection_closed_; |
| } |
| std::vector<std::string>& approved_devices() { return approved_devices_; } |
| |
| private: |
| mojo::Remote<mojom::BluetoothDeviceAccessProviderClient> client_; |
| base::MockCallback<base::OnceClosure> connection_closed_; |
| std::vector<std::string> approved_devices_; |
| }; |
| |
| } // namespace |
| |
| using testing::AnyOf; |
| |
| class CastBluetoothChooserTest : public testing::Test { |
| public: |
| CastBluetoothChooserTest() : provider_receiver_(&provider_) { |
| cast_bluetooth_chooser_ = std::make_unique<CastBluetoothChooser>( |
| handler_.Get(), provider_receiver_.BindNewPipeAndPassRemote()); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| CastBluetoothChooserTest(const CastBluetoothChooserTest&) = delete; |
| CastBluetoothChooserTest& operator=(const CastBluetoothChooserTest&) = delete; |
| |
| ~CastBluetoothChooserTest() override = default; |
| |
| void AddDeviceToChooser(const std::string& address) { |
| chooser().AddOrUpdateDevice(address, false, std::u16string(), false, false, |
| 0); |
| } |
| |
| SimpleDeviceAccessProvider& provider() { return provider_; } |
| content::BluetoothChooser& chooser() { return *cast_bluetooth_chooser_; } |
| |
| protected: |
| base::test::TaskEnvironment task_environment_; |
| base::MockCallback<content::BluetoothChooser::EventHandler> handler_; |
| |
| private: |
| SimpleDeviceAccessProvider provider_; |
| mojo::Receiver<mojom::BluetoothDeviceAccessProvider> provider_receiver_; |
| std::unique_ptr<CastBluetoothChooser> cast_bluetooth_chooser_; |
| }; |
| |
| TEST_F(CastBluetoothChooserTest, GrantAccessBeforeDeviceAvailable) { |
| // No devices have been made available to |chooser| yet. Grant it access to a |
| // device. |handler| should not run yet. |
| EXPECT_TRUE(provider().client()); |
| provider().client()->GrantAccess("aa:bb:cc:dd:ee:ff"); |
| task_environment_.RunUntilIdle(); |
| |
| // Make some unapproved devices available. |handler| should not run yet. |
| AddDeviceToChooser("11:22:33:44:55:66"); |
| AddDeviceToChooser("99:88:77:66:55:44"); |
| |
| // Now make the approved device available. |handler| should be called. |
| EXPECT_CALL(handler_, Run(content::BluetoothChooserEvent::SELECTED, |
| "aa:bb:cc:dd:ee:ff")); |
| EXPECT_CALL(provider().connection_closed(), Run()); |
| AddDeviceToChooser("aa:bb:cc:dd:ee:ff"); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(CastBluetoothChooserTest, DiscoverDeviceBeforeAccessGranted) { |
| // Make some devices available before access is granted. |handler| should not |
| // run yet. |
| AddDeviceToChooser("11:22:33:44:55:66"); |
| AddDeviceToChooser("aa:bb:cc:dd:ee:ff"); |
| AddDeviceToChooser("00:00:00:11:00:00"); |
| AddDeviceToChooser("99:88:77:66:55:44"); |
| |
| // Now approve one of those devices. |handler| should run. |
| EXPECT_CALL(handler_, Run(content::BluetoothChooserEvent::SELECTED, |
| "00:00:00:11:00:00")); |
| EXPECT_CALL(provider().connection_closed(), Run()); |
| provider().client()->GrantAccess("00:00:00:11:00:00"); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(CastBluetoothChooserTest, GrantAccessToAllDevicesBeforeDiscovery) { |
| // Grant access to all devices. |handler| should not run until the first |
| // device is made available. |
| provider().client()->GrantAccessToAllDevices(); |
| task_environment_.RunUntilIdle(); |
| |
| // Now make the some device available. |handler| should be called. |
| EXPECT_CALL(handler_, Run(content::BluetoothChooserEvent::SELECTED, |
| "aa:bb:cc:dd:ee:ff")); |
| EXPECT_CALL(provider().connection_closed(), Run()); |
| AddDeviceToChooser("aa:bb:cc:dd:ee:ff"); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(CastBluetoothChooserTest, GrantAccessToAllDevicesAfterDiscovery) { |
| // Make some devices available before access is granted. |handler| should not |
| // run yet. |
| AddDeviceToChooser("11:22:33:44:55:66"); |
| AddDeviceToChooser("aa:bb:cc:dd:ee:ff"); |
| AddDeviceToChooser("00:00:00:11:00:00"); |
| |
| // Now grant access to all devices. |handler| should be called with one of the |
| // available devices. |
| EXPECT_CALL(handler_, Run(content::BluetoothChooserEvent::SELECTED, |
| AnyOf("11:22:33:44:55:66", "aa:bb:cc:dd:ee:ff", |
| "00:00:00:11:00:00"))); |
| EXPECT_CALL(provider().connection_closed(), Run()); |
| provider().client()->GrantAccessToAllDevices(); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(CastBluetoothChooserTest, TearDownClientAfterAllAccessGranted) { |
| // Grant access to all devices. |handler| should not run until the first |
| // device is made available. |
| provider().client()->GrantAccessToAllDevices(); |
| task_environment_.RunUntilIdle(); |
| |
| // Tear down the client. Now that it has granted access to the client, it does |
| // not need to keep a reference to it. However, the chooser should stay alive |
| // and wait for devices to be made available. |
| provider().reset_client(); |
| EXPECT_FALSE(provider().client()); |
| task_environment_.RunUntilIdle(); |
| |
| // As soon as a device is available, run the handler. |
| EXPECT_CALL(handler_, Run(content::BluetoothChooserEvent::SELECTED, |
| "aa:bb:cc:dd:ee:ff")); |
| AddDeviceToChooser("aa:bb:cc:dd:ee:ff"); |
| } |
| |
| TEST_F(CastBluetoothChooserTest, TearDownClientBeforeApprovedDeviceDiscovered) { |
| // Make some devices available before access is granted. |handler| should not |
| // run yet. |
| AddDeviceToChooser("11:22:33:44:55:66"); |
| AddDeviceToChooser("aa:bb:cc:dd:ee:ff"); |
| |
| // Tear the client down before any access is granted. |handler| should run, |
| // but with content::BluetoothChooserEvent::CANCELLED. |
| EXPECT_CALL(handler_, Run(content::BluetoothChooserEvent::CANCELLED, "")); |
| provider().reset_client(); |
| EXPECT_FALSE(provider().client()); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| } // namespace chromecast |