chromium/chromeos/ash/services/bluetooth_config/cros_bluetooth_config_unittest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/services/bluetooth_config/cros_bluetooth_config.h"

#include <memory>

#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/services/bluetooth_config/bluetooth_power_controller_impl.h"
#include "chromeos/ash/services/bluetooth_config/device_name_manager_impl.h"
#include "chromeos/ash/services/bluetooth_config/fake_bluetooth_device_status_observer.h"
#include "chromeos/ash/services/bluetooth_config/fake_bluetooth_discovery_delegate.h"
#include "chromeos/ash/services/bluetooth_config/fake_discovery_session_status_observer.h"
#include "chromeos/ash/services/bluetooth_config/fake_fast_pair_delegate.h"
#include "chromeos/ash/services/bluetooth_config/fake_system_properties_observer.h"
#include "chromeos/ash/services/bluetooth_config/initializer_impl.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "components/session_manager/core/session_manager.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::bluetooth_config {

// Tests initialization and bindings in for CrosBluetoothConfig. Note that this
// test is not meant to be exhaustive of the API, since these details are tested
// within the unit tests for the other classes owned by CrosBluetoothConfig.
class CrosBluetoothConfigTest : public testing::Test {
 protected:
  CrosBluetoothConfigTest() = default;
  CrosBluetoothConfigTest(const CrosBluetoothConfigTest&) = delete;
  CrosBluetoothConfigTest& operator=(const CrosBluetoothConfigTest&) = delete;
  ~CrosBluetoothConfigTest() override = default;

  // testing::Test:
  void SetUp() override {
    DeviceNameManagerImpl::RegisterLocalStatePrefs(
        test_pref_service_.registry());
    BluetoothPowerControllerImpl::RegisterLocalStatePrefs(
        test_pref_service_.registry());

    auto fake_user_manager = std::make_unique<user_manager::FakeUserManager>();
    fake_user_manager_ = fake_user_manager.get();
    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
        std::move(fake_user_manager));

    chromeos::PowerManagerClient::InitializeFake();

    mock_adapter_ =
        base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>();
    fake_fast_pair_delegate_ = std::make_unique<FakeFastPairDelegate>();
    InitializerImpl initializer;
    cros_bluetooth_config_ = std::make_unique<CrosBluetoothConfig>(
        initializer, mock_adapter_, fake_fast_pair_delegate_.get());
    cros_bluetooth_config_->SetPrefs(/*logged_in_profile_prefs=*/nullptr,
                                     &test_pref_service_);

    // CrosBluetoothConfig ctor should set the device name manager for the
    // delegate.
    EXPECT_TRUE(fake_fast_pair_delegate_->device_name_manager() != nullptr);
  }

  void TearDown() override {
    // Destroy |cros_bluetooth_config_| before the fake power manager client in
    // order to remove observers correctly.
    cros_bluetooth_config_.reset();
    chromeos::PowerManagerClient::Shutdown();
  }

  mojo::Remote<mojom::CrosBluetoothConfig> BindToInterface() {
    mojo::Remote<mojom::CrosBluetoothConfig> remote;
    cros_bluetooth_config_->BindPendingReceiver(
        remote.BindNewPipeAndPassReceiver());
    return remote;
  }

 private:
  base::test::TaskEnvironment task_environment_;
  raw_ptr<user_manager::FakeUserManager, DanglingUntriaged> fake_user_manager_;
  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
  session_manager::SessionManager session_manager_;
  scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
  std::unique_ptr<FakeFastPairDelegate> fake_fast_pair_delegate_;
  sync_preferences::TestingPrefServiceSyncable test_pref_service_;

  std::unique_ptr<CrosBluetoothConfig> cros_bluetooth_config_;
};

TEST_F(CrosBluetoothConfigTest, BindMultipleClients) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote1 = BindToInterface();
  mojo::Remote<mojom::CrosBluetoothConfig> remote2 = BindToInterface();
  base::RunLoop().RunUntilIdle();
}

TEST_F(CrosBluetoothConfigTest, CallApiFunction) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote = BindToInterface();
  FakeSystemPropertiesObserver fake_observer;
  remote->ObserveSystemProperties(fake_observer.GeneratePendingRemote());
  base::RunLoop().RunUntilIdle();
}

TEST_F(CrosBluetoothConfigTest, CallPowerFunctions) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote = BindToInterface();
  remote->SetBluetoothEnabledState(true);
  base::RunLoop().RunUntilIdle();

  remote->SetBluetoothEnabledWithoutPersistence();
  base::RunLoop().RunUntilIdle();

  remote->SetBluetoothHidDetectionInactive(/*is_using_bluetooth=*/false);
  base::RunLoop().RunUntilIdle();
}

TEST_F(CrosBluetoothConfigTest, CallPairingFunction) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote = BindToInterface();
  auto delegate = std::make_unique<FakeBluetoothDiscoveryDelegate>();
  remote->StartDiscovery(delegate->GeneratePendingRemote());
  base::RunLoop().RunUntilIdle();
}

TEST_F(CrosBluetoothConfigTest, CallDeviceManagementFunctions) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote = BindToInterface();
  const std::string device_id = "device_id";
  std::optional<bool> result;

  remote->Connect(device_id,
                  base::BindLambdaForTesting(
                      [&result](bool success) { result = success; }));
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(result.value());
  result.reset();

  remote->Disconnect(device_id,
                     base::BindLambdaForTesting(
                         [&result](bool success) { result = success; }));
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(result.value());
  result.reset();

  remote->Forget(device_id, base::BindLambdaForTesting(
                                [&result](bool success) { result = success; }));
  base::RunLoop().RunUntilIdle();
  EXPECT_FALSE(result.value());
}

TEST_F(CrosBluetoothConfigTest, CallNamingFunction) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote = BindToInterface();
  remote->SetDeviceNickname("device_id", "nickname");
  base::RunLoop().RunUntilIdle();
}

TEST_F(CrosBluetoothConfigTest, CallBluetoothDeviceStatusApiFunction) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote = BindToInterface();
  FakeBluetoothDeviceStatusObserver fake_observer;
  remote->ObserveDeviceStatusChanges(fake_observer.GeneratePendingRemote());
  base::RunLoop().RunUntilIdle();
}

TEST_F(CrosBluetoothConfigTest, CallDiscoverySessionStatusApiFunction) {
  mojo::Remote<mojom::CrosBluetoothConfig> remote = BindToInterface();
  FakeDiscoverySessionStatusObserver fake_observer;
  remote->ObserveDiscoverySessionStatusChanges(
      fake_observer.GeneratePendingRemote());
  base::RunLoop().RunUntilIdle();
}

}  // namespace ash::bluetooth_config