910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/native_theme/native_theme.h"

#include <optional>
#include <utility>

#include "base/scoped_observation.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/color/color_provider_key.h"
#include "ui/native_theme/mock_os_settings_provider.h"
#include "ui/native_theme/native_theme.h"
#include "ui/native_theme/native_theme_observer.h"

namespace ui {
namespace {

class NativeThemeTest : public ::testing::Test {
 protected:
  NativeThemeTest() = default;
  ~NativeThemeTest() override = default;

  MockOsSettingsProvider& os_settings_provider() {
    return os_settings_provider_;
  }

 private:
  MockOsSettingsProvider os_settings_provider_;
};

TEST_F(NativeThemeTest, PreferredColorScheme) {
  using enum NativeTheme::PreferredColorScheme;
  const auto* const native_theme = NativeTheme::GetInstanceForNativeUi();

  EXPECT_EQ(native_theme->preferred_color_scheme(), kLight);

  os_settings_provider().SetPreferredColorScheme(kDark);
  EXPECT_EQ(native_theme->preferred_color_scheme(), kDark);

  os_settings_provider().SetPreferredColorScheme(kNoPreference);
  EXPECT_EQ(native_theme->preferred_color_scheme(), kNoPreference);
}

TEST_F(NativeThemeTest, PreferredContrast) {
  using enum NativeTheme::PreferredContrast;
  const auto* const native_theme = NativeTheme::GetInstanceForNativeUi();

  EXPECT_EQ(native_theme->preferred_contrast(), kNoPreference);

  os_settings_provider().SetPreferredContrast(kMore);
  EXPECT_EQ(native_theme->preferred_contrast(), kMore);

  os_settings_provider().SetPreferredContrast(kCustom);
  EXPECT_EQ(native_theme->preferred_contrast(), kCustom);

  os_settings_provider().SetPreferredContrast(kLess);
  EXPECT_EQ(native_theme->preferred_contrast(), kLess);
}

TEST_F(NativeThemeTest, UserColor) {
  static constexpr auto kAccentColor = SkColorSetRGB(135, 115, 10);
  os_settings_provider().SetAccentColor(kAccentColor);
  EXPECT_EQ(kAccentColor, NativeTheme::GetInstanceForNativeUi()->user_color());
}

TEST_F(NativeThemeTest, CaretBlinkInterval) {
  auto* const native_theme = NativeTheme::GetInstanceForNativeUi();
  static constexpr auto kNewInterval = base::Milliseconds(42);
  native_theme->set_caret_blink_interval(kNewInterval);
  EXPECT_EQ(native_theme->caret_blink_interval(), kNewInterval);

  native_theme->set_caret_blink_interval(base::TimeDelta());
  EXPECT_EQ(native_theme->caret_blink_interval(), base::TimeDelta());
}

TEST_F(NativeThemeTest, ColorMode) {
  using enum NativeTheme::PreferredColorScheme;
  const auto* const native_theme = NativeTheme::GetInstanceForNativeUi();

  os_settings_provider().SetPreferredColorScheme(kDark);
  EXPECT_EQ(native_theme->GetColorProviderKey(nullptr).color_mode,
            ColorProviderKey::ColorMode::kDark);

  os_settings_provider().SetPreferredColorScheme(kLight);
  EXPECT_EQ(native_theme->GetColorProviderKey(nullptr).color_mode,
            ColorProviderKey::ColorMode::kLight);

  os_settings_provider().SetForcedColorsActive(true);
  os_settings_provider().SetPreferredColorScheme(kDark);
  EXPECT_EQ(native_theme->GetColorProviderKey(nullptr).color_mode,
            ColorProviderKey::ColorMode::kDark);

  os_settings_provider().SetPreferredColorScheme(kLight);
  EXPECT_EQ(native_theme->GetColorProviderKey(nullptr).color_mode,
            ColorProviderKey::ColorMode::kLight);
}

TEST_F(NativeThemeTest, MetricsEmitted) {
  auto* const native_theme = NativeTheme::GetInstanceForNativeUi();
  base::HistogramTester histogram_tester;

  native_theme->NotifyOnNativeThemeUpdated();
  histogram_tester.ExpectTotalCount(
      "Views.Browser.TimeSpentProcessingOnNativeThemeUpdatedEvent", 1);
  histogram_tester.ExpectUniqueSample(
      "Views.Browser.NumColorProvidersInitializedDuringOnNativeThemeUpdated", 0,
      1);

  native_theme->NotifyOnNativeThemeUpdated();
  histogram_tester.ExpectTotalCount(
      "Views.Browser.TimeSpentProcessingOnNativeThemeUpdatedEvent", 2);
  histogram_tester.ExpectUniqueSample(
      "Views.Browser.NumColorProvidersInitializedDuringOnNativeThemeUpdated", 0,
      2);
}

TEST_F(NativeThemeTest, DelayScoper) {
  // Monitor calls to `OnNativeThemeUpdated()`.
  struct MockObserver : NativeThemeObserver {
    void OnNativeThemeUpdated(NativeTheme* observed_theme) override {
      ++call_count;
    }

    int call_count = 0;
  } observer;
  base::ScopedObservation<NativeTheme, NativeThemeObserver> observation(
      &observer);
  observation.Observe(NativeTheme::GetInstanceForNativeUi());

  const auto expect_notification_count = [&](int n) {
    EXPECT_EQ(std::exchange(observer.call_count, 0), n);
  };

  // Sanity check: setting the color should normally notify.
  os_settings_provider().SetAccentColor(SK_ColorRED);
  expect_notification_count(1);

  // When there are scopers alive, there should be no notifications.
  std::optional<NativeTheme::UpdateNotificationDelayScoper> scoper_1, scoper_2;
  scoper_1.emplace();
  scoper_2.emplace();
  os_settings_provider().SetAccentColor(SK_ColorGREEN);
  expect_notification_count(0);

  // Destroying some, but not all scopers should still not notify.
  scoper_2.reset();
  expect_notification_count(0);

  // Since there are still scopers, further changes should still not notify.
  os_settings_provider().SetAccentColor(SK_ColorBLUE);
  expect_notification_count(0);

  // When the last scoper is destroyed, there should only be one notification,
  // even though there were multiple changes above.
  scoper_1.reset();
  expect_notification_count(1);
}

}  // namespace
}  // namespace ui