chromium/chrome/browser/ash/bluetooth/debug_logs_manager_unittest.cc

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

#include "chrome/browser/ash/bluetooth/debug_logs_manager.h"

#include <memory>

#include "ash/constants/ash_features.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/prefs/testing_pref_service.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/dbus/bluez_dbus_manager.h"
#include "device/bluetooth/dbus/fake_bluetooth_debug_manager_client.h"
#include "device/bluetooth/floss/bluetooth_adapter_floss.h"
#include "device/bluetooth/floss/fake_floss_adapter_client.h"
#include "device/bluetooth/floss/fake_floss_logging_client.h"
#include "device/bluetooth/floss/fake_floss_manager_client.h"
#include "device/bluetooth/floss/floss_dbus_manager.h"
#include "device/bluetooth/floss/floss_features.h"
#include "device/bluetooth/floss/floss_manager_client.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace bluetooth {

namespace {

constexpr char kTestGooglerEmail[] = "[email protected]";
constexpr char kTestNonGooglerEmail[] = "[email protected]";

}  // namespace

class DebugLogsManagerTest : public testing::Test {
 public:
  DebugLogsManagerTest() = default;

  DebugLogsManagerTest(const DebugLogsManagerTest&) = delete;
  DebugLogsManagerTest& operator=(const DebugLogsManagerTest&) = delete;

  void SetUp() override {
    DebugLogsManager::RegisterPrefs(prefs_.registry());

    auto fake_bluetooth_debug_manager_client =
        std::make_unique<bluez::FakeBluetoothDebugManagerClient>();
    fake_bluetooth_debug_manager_client_ =
        fake_bluetooth_debug_manager_client.get();

    std::unique_ptr<bluez::BluezDBusManagerSetter> dbus_setter =
        bluez::BluezDBusManager::GetSetterForTesting();
    dbus_setter->SetBluetoothDebugManagerClient(
        std::unique_ptr<bluez::BluetoothDebugManagerClient>(
            std::move(fake_bluetooth_debug_manager_client)));

    is_floss_flag_enabled_ = false;
  }

  void TearDown() override {
    debug_logs_manager_.reset();
    adapter_.reset();
    bluez::BluezDBusManager::Shutdown();
  }

  void InitFlossFakes() {
    std::unique_ptr<floss::FlossDBusManagerSetter> floss_setter =
        floss::FlossDBusManager::GetSetterForTesting();
    floss_setter->SetFlossManagerClient(
        std::make_unique<floss::FakeFlossManagerClient>());
    floss_setter->SetFlossAdapterClient(
        std::make_unique<floss::FakeFlossAdapterClient>());
    floss_setter->SetFlossLoggingClient(
        std::make_unique<floss::FakeFlossLoggingClient>());

    GetFakeManagerClient()->SetDefaultEnabled(true);
  }

  void EnableDebugFlag() { is_debug_toggle_flag_enabled_ = true; }
  void EnableFlossFlag() { is_floss_flag_enabled_ = true; }

  void InitFeatures() {
    std::vector<base::test::FeatureRef> enabled_features;
    std::vector<base::test::FeatureRef> disabled_features;

    if (is_debug_toggle_flag_enabled_)
      enabled_features.push_back(features::kShowBluetoothDebugLogToggle);
    else
      disabled_features.push_back(features::kShowBluetoothDebugLogToggle);

    if (is_floss_flag_enabled_) {
      enabled_features.push_back(floss::features::kFlossEnabled);
    } else {
      disabled_features.push_back(floss::features::kFlossEnabled);
    }

    feature_list_.InitWithFeatures(enabled_features, disabled_features);
  }

  void InstantiateDebugManager(const char* email) {
    debug_logs_manager_ = std::make_unique<DebugLogsManager>(email, &prefs_);
  }

  void DestroyDebugManager() { debug_logs_manager_.reset(); }

  DebugLogsManager* debug_manager() const { return debug_logs_manager_.get(); }

  bluez::FakeBluetoothDebugManagerClient* fake_bluetooth_debug_manager_client()
      const {
    return fake_bluetooth_debug_manager_client_;
  }

  raw_ptr<floss::FakeFlossManagerClient> GetFakeManagerClient() const {
    return static_cast<floss::FakeFlossManagerClient*>(
        floss::FlossDBusManager::Get()->GetManagerClient());
  }

  raw_ptr<floss::FakeFlossLoggingClient> GetFakeFlossLoggingClient() const {
    return static_cast<floss::FakeFlossLoggingClient*>(
        floss::FlossDBusManager::Get()->GetLoggingClient());
  }

  void InitializeAdapter(bool powered) {
    adapter_ = floss::BluetoothAdapterFloss::CreateAdapter();

    base::RunLoop run_loop;
    adapter_->Initialize(run_loop.QuitClosure());
    run_loop.Run();

    ASSERT_TRUE(adapter_);
    ASSERT_TRUE(adapter_->IsInitialized());

    device::BluetoothAdapterFactory::Get()->SetAdapterForTesting(adapter_);
  }

  void SimulatePowered(bool powered) {
    adapter_->NotifyAdapterPoweredChanged(powered);
  }

  bool IsDebugEnabled() {
    return debug_logs_manager_->GetDebugLogsState() ==
           DebugLogsManager::DebugLogsState::kSupportedAndEnabled;
  }

 private:
  base::test::ScopedFeatureList feature_list_;
  bool is_debug_toggle_flag_enabled_ = false;
  bool is_floss_flag_enabled_ = false;
  raw_ptr<bluez::FakeBluetoothDebugManagerClient, DanglingUntriaged>
      fake_bluetooth_debug_manager_client_;
  std::unique_ptr<DebugLogsManager> debug_logs_manager_;
  TestingPrefServiceSimple prefs_;
  scoped_refptr<device::BluetoothAdapter> adapter_;
};

TEST_F(DebugLogsManagerTest, FlagNotEnabled) {
  base::test::SingleThreadTaskEnvironment task_environment;

  /* debug flag disabled */
  InitFeatures();
  InstantiateDebugManager(kTestGooglerEmail);
  EXPECT_EQ(debug_manager()->GetDebugLogsState(),
            DebugLogsManager::DebugLogsState::kNotSupported);
}

TEST_F(DebugLogsManagerTest, NonGoogler) {
  base::test::SingleThreadTaskEnvironment task_environment;

  EnableDebugFlag();
  InitFeatures();
  InstantiateDebugManager(kTestNonGooglerEmail);
  EXPECT_EQ(debug_manager()->GetDebugLogsState(),
            DebugLogsManager::DebugLogsState::kNotSupported);
}

TEST_F(DebugLogsManagerTest, GooglerDefaultPref) {
  base::test::SingleThreadTaskEnvironment task_environment;

  EnableDebugFlag();
  InitFeatures();
  InstantiateDebugManager(kTestGooglerEmail);
  EXPECT_EQ(debug_manager()->GetDebugLogsState(),
            DebugLogsManager::DebugLogsState::kSupportedAndEnabled);
}

TEST_F(DebugLogsManagerTest, ChangeDebugLogsState) {
  base::test::SingleThreadTaskEnvironment task_environment;

  EnableDebugFlag();
  InitFeatures();
  InstantiateDebugManager(kTestGooglerEmail);
  EXPECT_EQ(debug_manager()->GetDebugLogsState(),
            DebugLogsManager::DebugLogsState::kSupportedAndEnabled);

  debug_manager()->ChangeDebugLogsState(
      false /* should_debug_logs_be_enabled */);
  EXPECT_EQ(debug_manager()->GetDebugLogsState(),
            DebugLogsManager::DebugLogsState::kSupportedButDisabled);

  DestroyDebugManager();
  InstantiateDebugManager(kTestGooglerEmail);
  EXPECT_EQ(debug_manager()->GetDebugLogsState(),
            DebugLogsManager::DebugLogsState::kSupportedButDisabled);
}

TEST_F(DebugLogsManagerTest, SendVerboseLogsRequestUponLoginAndLogout) {
  base::test::SingleThreadTaskEnvironment task_environment;

  EnableDebugFlag();
  InitFeatures();
  InstantiateDebugManager(kTestGooglerEmail);
  // After login, bluez level should change
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 1);

  DestroyDebugManager();
  // After logout, bluez level should reset to 0
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 0);

  InstantiateDebugManager(kTestGooglerEmail);
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 1);

  debug_manager()->ChangeDebugLogsState(
      false /* should_debug_logs_be_enabled */);
  // BlueZ level is updated only on login/logout, so now it stays the same.
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 1);
  DestroyDebugManager();
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 0);

  InstantiateDebugManager(kTestGooglerEmail);
  // bluez level should still be 0 because logging is disabled
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 0);
}

TEST_F(DebugLogsManagerTest, RetryUponSetVerboseLogsFailure) {
  base::test::TaskEnvironment task_environment{
      base::test::TaskEnvironment::TimeSource::MOCK_TIME};

  EnableDebugFlag();
  InitFeatures();
  InstantiateDebugManager(kTestGooglerEmail);
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 1);
  debug_manager()->ChangeDebugLogsState(
      false /* should_debug_logs_be_enabled */);

  DestroyDebugManager();
  fake_bluetooth_debug_manager_client()->MakeNextSetLogLevelsFail();
  InstantiateDebugManager(kTestGooglerEmail);
  task_environment.FastForwardUntilNoTasksRemain();

  EXPECT_EQ(fake_bluetooth_debug_manager_client()->set_log_levels_fail_count(),
            1);
  // Message is re-sent upon failing, eventually bluez level should change.
  EXPECT_EQ(fake_bluetooth_debug_manager_client()->bluez_level(), 0);
}

TEST_F(DebugLogsManagerTest, CheckFlossUpdatesOnPowerOn) {
  base::test::SingleThreadTaskEnvironment task_environment;

  InitFlossFakes();
  InitializeAdapter(/*powered=*/false);

  EnableDebugFlag();
  EnableFlossFlag();
  InitFeatures();

  // Until we're powered, setting debug logging should fail but the default
  // state should be persisted.
  EXPECT_EQ(GetFakeFlossLoggingClient()->GetDebugEnabledForTesting(), false);
  InstantiateDebugManager(kTestGooglerEmail);
  EXPECT_EQ(GetFakeFlossLoggingClient()->GetDebugEnabledForTesting(), false);
  EXPECT_EQ(IsDebugEnabled(), true);

  // Powering on should enable the flag.
  SimulatePowered(/*powered=*/true);
  EXPECT_EQ(GetFakeFlossLoggingClient()->GetDebugEnabledForTesting(), true);
  SimulatePowered(/*powered=*/false);
}

}  // namespace bluetooth

}  // namespace ash