chromium/chrome/browser/ash/magic_boost/magic_boost_state_ash_unittest.cc

// Copyright 2024 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/magic_boost/magic_boost_state_ash.h"

#include "ash/constants/ash_pref_names.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/types/cxx23_to_underlying.h"
#include "base/values.h"
#include "chrome/browser/ash/magic_boost/mock_editor_panel_manager.h"
#include "chromeos/components/magic_boost/public/cpp/magic_boost_state.h"
#include "chromeos/crosapi/mojom/editor_panel.mojom-shared.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using chromeos::HMRConsentStatus;
using chromeos::MagicBoostState;

namespace ash {

namespace {

class TestMagicBoostStateObserver : public MagicBoostState::Observer {
 public:
  TestMagicBoostStateObserver() = default;

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

  ~TestMagicBoostStateObserver() override = default;

  // MagicBoostStateObserver:
  void OnHMREnabledUpdated(bool enabled) override { hmr_enabled_ = enabled; }

  void OnHMRConsentStatusUpdated(HMRConsentStatus status) override {
    hmr_consent_status_ = status;
  }

  void OnIsDeleting() override {
    // Do nothing as a user of `TestMagicBoostStateObserver` is responsible for
    // managing life cycle, i.e., stop observing before `MagicBoostState`
    // instance gets destructed.
  }

  bool hmr_enabled() const { return hmr_enabled_; }
  HMRConsentStatus hmr_consent_status() const { return hmr_consent_status_; }

 private:
  bool hmr_enabled_ = false;
  HMRConsentStatus hmr_consent_status_ = HMRConsentStatus::kUnset;
};

}  // namespace

class MagicBoostStateAshTest : public AshTestBase {
 protected:
  MagicBoostStateAshTest() = default;
  MagicBoostStateAshTest(const MagicBoostStateAshTest&) = delete;
  MagicBoostStateAshTest& operator=(const MagicBoostStateAshTest&) = delete;
  ~MagicBoostStateAshTest() override = default;

  // ChromeAshTestBase:
  void SetUp() override {
    AshTestBase::SetUp();

    prefs_ = static_cast<TestingPrefServiceSimple*>(
        ash::Shell::Get()->session_controller()->GetPrimaryUserPrefService());

    magic_boost_state_ = std::make_unique<MagicBoostStateAsh>();
    magic_boost_state_->set_editor_panel_manager_for_test(
        &mock_editor_manager_);

    observer_ = std::make_unique<TestMagicBoostStateObserver>();
  }

  void TearDown() override {
    observer_.reset();
    magic_boost_state_.reset();
    prefs_ = nullptr;
    AshTestBase::TearDown();
  }

  TestingPrefServiceSimple* prefs() { return prefs_; }

  TestMagicBoostStateObserver* observer() { return observer_.get(); }

  MagicBoostStateAsh* magic_boost_state() { return magic_boost_state_.get(); }

  MockEditorPanelManager& mock_editor_manager() { return mock_editor_manager_; }

 private:
  raw_ptr<TestingPrefServiceSimple> prefs_;
  std::unique_ptr<TestMagicBoostStateObserver> observer_;
  std::unique_ptr<MagicBoostStateAsh> magic_boost_state_;
  testing::NiceMock<MockEditorPanelManager> mock_editor_manager_;
};

TEST_F(MagicBoostStateAshTest, UpdateMagicBoostEnabledState) {
  MagicBoostState::Get()->AddObserver(observer());

  prefs()->SetBoolean(ash::prefs::kMagicBoostEnabled, false);

  // Both HMR and Orca should be disabled when `kMagicBoostEnabled` is false.
  EXPECT_FALSE(MagicBoostState::Get()->hmr_enabled().value());
  EXPECT_FALSE(prefs()->GetBoolean(ash::prefs::kOrcaEnabled));

  // The observer class should get a notification when the pref value
  // changes.
  MagicBoostState::Get()->AsyncWriteHMREnabled(false);
  EXPECT_FALSE(observer()->hmr_enabled());

  prefs()->SetBoolean(ash::prefs::kMagicBoostEnabled, true);

  // Both HMR and Orca should be enabled when `kMagicBoostEnabled` is true.
  EXPECT_TRUE(MagicBoostState::Get()->hmr_enabled().value());
  EXPECT_TRUE(prefs()->GetBoolean(ash::prefs::kOrcaEnabled));

  // The observer class should get a notification when the pref value
  // changes.
  EXPECT_TRUE(MagicBoostState::Get()->hmr_enabled().value());
  EXPECT_TRUE(observer()->hmr_enabled());

  MagicBoostState::Get()->RemoveObserver(observer());
}

TEST_F(MagicBoostStateAshTest, UpdateHMREnabledState) {
  MagicBoostState::Get()->AddObserver(observer());

  // The observer class should get an notification when the pref value
  // changes.
  MagicBoostState::Get()->AsyncWriteHMREnabled(false);
  EXPECT_FALSE(MagicBoostState::Get()->hmr_enabled().value());
  EXPECT_FALSE(observer()->hmr_enabled());

  // The observer class should get an notification when the pref value
  // changes.
  MagicBoostState::Get()->AsyncWriteHMREnabled(true);
  EXPECT_TRUE(MagicBoostState::Get()->hmr_enabled().value());
  EXPECT_TRUE(observer()->hmr_enabled());

  MagicBoostState::Get()->RemoveObserver(observer());
}

TEST_F(MagicBoostStateAshTest, UpdateHMRConsentStatus) {
  MagicBoostState::Get()->AddObserver(observer());

  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kUnset);
  EXPECT_EQ(observer()->hmr_consent_status(), HMRConsentStatus::kUnset);

  // The observer class should get an notification when the pref value
  // changes.
  prefs()->SetInteger(ash::prefs::kHMRConsentStatus,
                      base::to_underlying(HMRConsentStatus::kDeclined));

  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kDeclined);
  EXPECT_EQ(observer()->hmr_consent_status(), HMRConsentStatus::kDeclined);

  prefs()->SetInteger(ash::prefs::kHMRConsentStatus,
                      base::to_underlying(HMRConsentStatus::kApproved));
  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kApproved);
  EXPECT_EQ(observer()->hmr_consent_status(), HMRConsentStatus::kApproved);

  MagicBoostState::Get()->RemoveObserver(observer());
}

TEST_F(MagicBoostStateAshTest, UpdateHMRConsentStatusWhenEnableStateChanged) {
  MagicBoostState::Get()->AsyncWriteHMREnabled(false);
  MagicBoostState::Get()->AsyncWriteConsentStatus(HMRConsentStatus::kDeclined);

  // When consent status is `kDeclined` and enable state flip from false to
  // true (this can happen when flipping the toggle in Settings app), consent
  // status should be flip to `kPending` so that disclaimer UI can be shown when
  // accessing the feature.
  MagicBoostState::Get()->AsyncWriteHMREnabled(true);

  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kPendingDisclaimer);

  // Flipping back enable state from true to false should retain the consent
  // status value.
  MagicBoostState::Get()->AsyncWriteHMREnabled(false);

  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kPendingDisclaimer);

  MagicBoostState::Get()->AsyncWriteConsentStatus(HMRConsentStatus::kUnset);

  // When consent status is `kUnset` and enable state flip from false to
  // true (this can happen when flipping the toggle in Settings app), consent
  // status should be flip to `kPendingDisclaimer` so that disclaimer UI can be
  // shown when accessing the feature.
  MagicBoostState::Get()->AsyncWriteHMREnabled(true);

  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kPendingDisclaimer);

  // When consent status is `kApproved`, flipping enable state should not change
  // consent status state.
  MagicBoostState::Get()->AsyncWriteConsentStatus(HMRConsentStatus::kApproved);

  MagicBoostState::Get()->AsyncWriteHMREnabled(false);
  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kApproved);

  MagicBoostState::Get()->AsyncWriteHMREnabled(true);
  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_status(),
            HMRConsentStatus::kApproved);
}

TEST_F(MagicBoostStateAshTest, UpdateHMRConsentWindowDismissCount) {
  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_window_dismiss_count(), 0);

  prefs()->SetInteger(ash::prefs::kHMRConsentWindowDismissCount, 1);
  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_window_dismiss_count(), 1);

  prefs()->SetInteger(ash::prefs::kHMRConsentWindowDismissCount, 2);
  EXPECT_EQ(MagicBoostState::Get()->hmr_consent_window_dismiss_count(), 2);
}

TEST_F(MagicBoostStateAshTest, DisableOrcaFeature) {
  // `DisableOrcaFeature` should trigger the correct functions from
  // `EditorPanelManager`.
  EXPECT_CALL(mock_editor_manager(), OnConsentRejected);
  EXPECT_CALL(mock_editor_manager(), OnPromoCardDeclined);

  magic_boost_state()->DisableOrcaFeature();
  testing::Mock::VerifyAndClearExpectations(&mock_editor_manager());
}

TEST_F(MagicBoostStateAshTest, EnableOrcaFeature) {
  // `EnableOrcaFeature` should trigger the correct functions from
  // `EditorPanelManager`.
  EXPECT_CALL(mock_editor_manager(), OnConsentApproved);

  magic_boost_state()->EnableOrcaFeature();
  testing::Mock::VerifyAndClearExpectations(&mock_editor_manager());
}

}  // namespace ash