chromium/ash/components/arc/system_ui/arc_system_ui_bridge_unittest.cc

// 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 "ash/components/arc/system_ui/arc_system_ui_bridge.h"

#include <memory>

#include "ash/components/arc/session/arc_bridge_service.h"
#include "ash/components/arc/session/arc_service_manager.h"
#include "ash/components/arc/test/connection_holder_util.h"
#include "ash/components/arc/test/fake_system_ui_instance.h"
#include "ash/style/mojom/color_scheme.mojom-shared.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/test/mock_log.h"
#include "base/test/scoped_feature_list.h"
#include "components/user_prefs/test/test_browser_context_with_prefs.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/color_palette.h"

using ::testing::_;
namespace arc {

#define EXPECT_ERROR_LOG(matcher)                                    \
  if (DLOG_IS_ON(ERROR)) {                                           \
    EXPECT_CALL(log_, Log(logging::LOGGING_ERROR, _, _, _, matcher)) \
        .WillOnce(testing::Return(true)); /* suppress logging */     \
  }

class TestColorPaletteController : public ash::ColorPaletteController {
 public:
  TestColorPaletteController() = default;
  ~TestColorPaletteController() override = default;

  void SetSeed(ash::ColorPaletteSeed seed) { seed_ = seed; }

  void AddObserver(Observer* observer) override {}
  void RemoveObserver(Observer* observer) override {}
  void SetColorScheme(ash::style::mojom::ColorScheme scheme,
                      const AccountId& account_id,
                      base::OnceClosure on_complete) override {}
  void SetStaticColor(SkColor seed_color,
                      const AccountId& account_id,
                      base::OnceClosure on_complete) override {}
  void SelectLocalAccount(const AccountId& account_id) override {}
  SkColor GetUserWallpaperColorOrDefault(SkColor default_color) const override {
    return SK_ColorGREEN;
  }
  std::optional<ash::ColorPaletteSeed> GetColorPaletteSeed(
      const AccountId& account_id) const override {
    return seed_;
  }
  std::optional<ash::ColorPaletteSeed> GetCurrentSeed() const override {
    return seed_;
  }
  bool UsesWallpaperSeedColor(const AccountId& account_id) const override {
    return true;
  }
  ash::style::mojom::ColorScheme GetColorScheme(
      const AccountId& account_id) const override {
    return seed_.scheme;
  }
  std::optional<SkColor> GetStaticColor(
      const AccountId& account_id) const override {
    return seed_.seed_color;
  }
  bool GetUseKMeansPref(const AccountId& account_id) const override {
    return false;
  }
  void GenerateSampleColorSchemes(
      base::span<const ash::style::mojom::ColorScheme> color_scheme_buttons,
      ash::ColorPaletteController::SampleColorSchemeCallback callback)
      const override {}

 private:
  ash::ColorPaletteSeed seed_;
};

class ArcSystemUIBridgeTest : public testing::Test {
 protected:
  ArcSystemUIBridgeTest()
      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
        bridge_(ArcSystemUIBridge::GetForBrowserContextForTesting(&context_)) {
    // ARC has VLOG(1) enabled. Ignore and suppress these logs if the test
    // will verify log output. Note the "if" must match the "if" in
    // `EXPECT_ERROR_LOG`.
    if (DLOG_IS_ON(ERROR)) {
      EXPECT_CALL(log_, Log(-1, _, _, _, _))
          .WillRepeatedly(testing::Return(true));
    }
  }
  ~ArcSystemUIBridgeTest() override = default;

  void SetUp() override {
    test_palette_ = std::make_unique<TestColorPaletteController>();
    bridge_->SetColorPaletteControllerForTesting(test_palette_.get());
    ArcServiceManager::Get()->arc_bridge_service()->system_ui()->SetInstance(
        &system_ui_instance_);
    WaitForInstanceReady(
        ArcServiceManager::Get()->arc_bridge_service()->system_ui());
  }

  void TearDown() override {
    ArcServiceManager::Get()->arc_bridge_service()->system_ui()->CloseInstance(
        &system_ui_instance_);
    bridge_->Shutdown();
    if (test_palette_) {
      // Only run cleanup if simulating that Shell still exists.
      bridge_->SetColorPaletteControllerForTesting(nullptr);
      test_palette_.reset();
    }
  }

  explicit ArcSystemUIBridgeTest(const ArcSystemUIBridge&) = delete;
  ArcSystemUIBridgeTest& operator=(const ArcSystemUIBridge&) = delete;

  content::BrowserTaskEnvironment task_environment_;
  ArcServiceManager arc_service_manager_;
  user_prefs::TestBrowserContextWithPrefs context_;
  FakeSystemUiInstance system_ui_instance_;
  std::unique_ptr<TestColorPaletteController> test_palette_;
  const raw_ptr<ArcSystemUIBridge> bridge_;
  base::test::MockLog log_;
};

TEST_F(ArcSystemUIBridgeTest, ConstructDestruct) {}

TEST_F(ArcSystemUIBridgeTest, DestroyColorPaletteControllerFirst) {
  // Simulate Shell destruction.
  bridge_->OnShellDestroying();
  // Delete the ColorPaletteController like Shell would.
  test_palette_.reset();
  // This would crash in `TearDown()` before https://crbug.com/1431544 was
  // fixed.
}

TEST_F(ArcSystemUIBridgeTest, OnColorModeChanged) {
  EXPECT_FALSE(system_ui_instance_.dark_theme_status());
  ash::ColorPaletteSeed seed;
  seed.color_mode = ui::ColorProviderKey::ColorMode::kDark;
  bridge_->OnColorPaletteChanging(seed);
  EXPECT_TRUE(system_ui_instance_.dark_theme_status());
  ArcServiceManager::Get()->arc_bridge_service()->system_ui()->CloseInstance(
      &system_ui_instance_);
  EXPECT_ERROR_LOG(testing::HasSubstr("Failed to send theme status"));
  log_.StartCapturingLogs();
  seed.color_mode = ui::ColorProviderKey::ColorMode::kLight;
  bridge_->OnColorPaletteChanging(seed);
}

TEST_F(ArcSystemUIBridgeTest, OnConnectionReady) {
  EXPECT_FALSE(system_ui_instance_.dark_theme_status());
  ash::ColorPaletteSeed seed;
  seed.color_mode = ui::ColorProviderKey::ColorMode::kDark;
  seed.scheme = ash::style::mojom::ColorScheme::kVibrant;
  seed.seed_color = SK_ColorMAGENTA;
  test_palette_->SetSeed(seed);

  // When the connection is ready, bridge will read the current seed from the
  // ColorPaletteController.
  bridge_->OnConnectionReady();
  EXPECT_TRUE(system_ui_instance_.dark_theme_status());
  EXPECT_EQ(static_cast<uint32_t>(SK_ColorMAGENTA),
            system_ui_instance_.source_color());
  EXPECT_EQ(mojom::ThemeStyleType::VIBRANT, system_ui_instance_.theme_style());
}

TEST_F(ArcSystemUIBridgeTest, SendOverlayColor) {
  // Verify that the test data is not the default
  ASSERT_NE((uint32_t)50, system_ui_instance_.source_color());
  ASSERT_NE(mojom::ThemeStyleType::EXPRESSIVE,
            system_ui_instance_.theme_style());

  bridge_->SendOverlayColor(50, mojom::ThemeStyleType::EXPRESSIVE);
  EXPECT_EQ((uint32_t)50, system_ui_instance_.source_color());
  EXPECT_EQ(mojom::ThemeStyleType::EXPRESSIVE,
            system_ui_instance_.theme_style());
}

TEST_F(ArcSystemUIBridgeTest, OnConnectionReady_NeutralToSpritzConversion) {
  EXPECT_FALSE(system_ui_instance_.dark_theme_status());
  ash::ColorPaletteSeed seed;
  seed.color_mode = ui::ColorProviderKey::ColorMode::kLight;
  seed.scheme = ash::style::mojom::ColorScheme::kNeutral;
  seed.seed_color = SK_ColorCYAN;
  test_palette_->SetSeed(seed);

  // When the connection is ready, bridge will read the current seed from the
  // ColorPaletteController.
  bridge_->OnConnectionReady();
  EXPECT_FALSE(system_ui_instance_.dark_theme_status());
  EXPECT_EQ(static_cast<uint32_t>(SK_ColorCYAN),
            system_ui_instance_.source_color());
  EXPECT_EQ(mojom::ThemeStyleType::SPRITZ, system_ui_instance_.theme_style());
}

}  // namespace arc