chromium/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_info_browsertest.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 <stdint.h>

#include <algorithm>
#include <memory>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/wallpaper/online_wallpaper_variant.h"
#include "ash/public/cpp/wallpaper/wallpaper_controller_observer.h"
#include "ash/public/cpp/wallpaper/wallpaper_drivefs_delegate.h"
#include "ash/public/cpp/wallpaper/wallpaper_info.h"
#include "ash/public/cpp/wallpaper/wallpaper_types.h"
#include "ash/shell.h"
#include "ash/wallpaper/test_wallpaper_drivefs_delegate.h"
#include "ash/wallpaper/test_wallpaper_image_downloader.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "ash/wallpaper/wallpaper_controller_test_api.h"
#include "ash/wallpaper/wallpaper_pref_manager.h"
#include "ash/webui/personalization_app/personalization_app_url_constants.h"
#include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_utils.h"
#include "chrome/browser/ash/system_web_apps/apps/personalization_app/test_personalization_app_webui_provider.h"
#include "chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.h"
#include "chrome/browser/ash/wallpaper_handlers/mock_wallpaper_handlers.h"
#include "chrome/browser/ash/wallpaper_handlers/test_wallpaper_fetcher_delegate.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/wallpaper/wallpaper_controller_client_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chromeos/constants/chromeos_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/scoped_web_ui_controller_factory_registration.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "url/gurl.h"

namespace ash::personalization_app {

namespace {

constexpr char kDummyUrl[] = "data:image/png;base64,20";
constexpr char kDummyCollectionId[] = "fake_collection_id_0";

const uint64_t kAssetId = 20;

void PutWallpaperInfoInPrefs(AccountId account_id,
                             WallpaperInfo info,
                             PrefService* pref_service,
                             const std::string& pref_name) {
  DCHECK(pref_service);
  ScopedDictPrefUpdate wallpaper_update(pref_service, pref_name);
  base::Value::Dict wallpaper_info_dict = info.ToDict();
  wallpaper_update->Set(account_id.GetUserEmail(),
                        std::move(wallpaper_info_dict));
}

// Helper class to block until wallpaper colors have updated.
class WallpaperChangedWaiter : public WallpaperControllerObserver {
 public:
  explicit WallpaperChangedWaiter(base::OnceClosure on_wallpaper_changed)
      : on_wallpaper_changed_(std::move(on_wallpaper_changed)) {
    wallpaper_controller_observation_.Observe(WallpaperController::Get());
  }

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

  ~WallpaperChangedWaiter() override = default;

  void OnWallpaperChanged() override {
    DCHECK(on_wallpaper_changed_);
    std::move(on_wallpaper_changed_).Run();
  }

 private:
  base::OnceClosure on_wallpaper_changed_;
  base::ScopedObservation<WallpaperController, WallpaperControllerObserver>
      wallpaper_controller_observation_{this};
};

class PersonalizationAppWallpaperInfoBrowserTest
    : public SystemWebAppBrowserTestBase {
 public:
  PersonalizationAppWallpaperInfoBrowserTest() = default;

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

  ~PersonalizationAppWallpaperInfoBrowserTest() override = default;

  // BrowserTestBase:
  void SetUpInProcessBrowserTestFixture() override {
    WallpaperControllerImpl::SetWallpaperImageDownloaderForTesting(
        std::make_unique<TestWallpaperImageDownloader>());
    SystemWebAppBrowserTestBase::SetUpInProcessBrowserTestFixture();
  }

  void SetUpOnMainThread() override {
    SystemWebAppBrowserTestBase::SetUpOnMainThread();

    browser()->window()->Minimize();

    wallpaper_controller()->OverrideDriveFsDelegateForTesting(
        std::make_unique<TestWallpaperDriveFsDelegate>());
    WallpaperControllerClientImpl::Get()->SetWallpaperFetcherDelegateForTesting(
        std::make_unique<wallpaper_handlers::TestWallpaperFetcherDelegate>());

    auto wallpaper_controller_test_api =
        std::make_unique<WallpaperControllerTestApi>(wallpaper_controller());
    wallpaper_controller_test_api->SetDefaultWallpaper(
        GetAccountId(browser()->profile()));

    test_chrome_webui_controller_factory_.AddFactoryOverride(
        kChromeUIPersonalizationAppHost, &test_webui_provider_);

    WaitForTestSystemAppInstall();
  }

  void TearDownOnMainThread() override {
    SystemWebAppBrowserTestBase::TearDownOnMainThread();
  }

  content::WebContents* LaunchAppAtWallpaperSubpage(Browser** browser) {
    apps::AppLaunchParams launch_params =
        LaunchParamsForApp(ash::SystemWebAppType::PERSONALIZATION);
    launch_params.override_url =
        GURL(std::string(kChromeUIPersonalizationAppURL) +
             kWallpaperSubpageRelativeUrl);
    return LaunchApp(std::move(launch_params), browser);
  }

  WallpaperControllerImpl* wallpaper_controller() {
    return Shell::Get()->wallpaper_controller();
  }

 private:
  TestChromeWebUIControllerFactory test_chrome_webui_controller_factory_;
  TestPersonalizationAppWebUIProvider test_webui_provider_;
  content::ScopedWebUIControllerFactoryRegistration
      scoped_controller_factory_registration_{
          &test_chrome_webui_controller_factory_};
};

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       BadWallpaperLayoutIgnoredDuringSyncIn) {
  WallpaperInfo info =
      WallpaperInfo(std::string(), static_cast<WallpaperLayout>(1000),
                    WallpaperType::kOnline, base::Time::Now());
  info.collection_id = kDummyCollectionId;
  info.location = kDummyUrl;

  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  base::RunLoop().RunUntilIdle();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  EXPECT_EQ(new_info.type, WallpaperType::kDefault);

  // Expects constructing synced info to fail.
  EXPECT_FALSE(wallpaper_controller()
                   ->pref_manager_for_testing()
                   ->GetSyncedWallpaperInfo(account_id, &info));
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       BadWallpaperTypeIgnoredDuringSyncIn) {
  WallpaperInfo info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    static_cast<WallpaperType>(10000), base::Time::Now());
  info.collection_id = kDummyCollectionId;
  info.location = kDummyUrl;

  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  base::RunLoop().RunUntilIdle();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  EXPECT_EQ(new_info.type, WallpaperType::kDefault);

  // Expects constructing synced info to fail.
  EXPECT_FALSE(wallpaper_controller()
                   ->pref_manager_for_testing()
                   ->GetSyncedWallpaperInfo(account_id, &info));
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       OnlineWallpaperSyncInSuccessfully) {
  WallpaperInfo info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kOnline, base::Time::Now());
  info.collection_id = kDummyCollectionId;
  info.location = kDummyUrl;
  info.asset_id = kAssetId;
  info.unit_id = kAssetId;
  info.variants.emplace_back(kAssetId, GURL(kDummyUrl),
                             backdrop::Image::IMAGE_TYPE_UNKNOWN);

  base::RunLoop loop;
  WallpaperChangedWaiter waiter(loop.QuitClosure());
  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  loop.Run();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  // Expects set wallpaper info to match synced info.
  EXPECT_TRUE(new_info.MatchesAsset(info));
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       OnlineWallpaperBadLocationShouldNotCrash) {
  WallpaperInfo info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kOnline, base::Time::Now());
  info.collection_id = kDummyCollectionId;
  info.location = "http://_none_matching_location_from_server";
  info.asset_id = kAssetId;
  info.unit_id = kAssetId;

  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  base::RunLoop().RunUntilIdle();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  EXPECT_EQ(new_info.type, WallpaperType::kDefault);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       OnlineWallpaperFetchMissingAssetIdSuccessfully) {
  WallpaperInfo info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kOnline, base::Time::Now());
  info.collection_id = kDummyCollectionId;
  info.location = kDummyUrl;

  base::RunLoop loop;
  WallpaperChangedWaiter waiter(loop.QuitClosure());
  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  loop.Run();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  // Expects asset_id, unit_id, and variants to be set.
  if (!features::IsVersionWallpaperInfoEnabled()) {
    EXPECT_TRUE(new_info.asset_id.has_value());
  }
  EXPECT_TRUE(new_info.unit_id.has_value());
  EXPECT_EQ(new_info.variants.size(), 1u);
  EXPECT_EQ(new_info.collection_id, kDummyCollectionId);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       OnlineWallpaperFetchMissingVariantsSuccessfully) {
  WallpaperInfo info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kOnline, base::Time::Now());
  info.collection_id = kDummyCollectionId;
  info.location = kDummyUrl;
  info.asset_id = kAssetId;
  info.unit_id = kAssetId;

  base::RunLoop loop;
  WallpaperChangedWaiter waiter(loop.QuitClosure());
  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  loop.Run();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  // Expects asset_id, unit_id, and variants to be set.
  if (!features::IsVersionWallpaperInfoEnabled()) {
    EXPECT_TRUE(new_info.asset_id.has_value());
  }
  EXPECT_TRUE(new_info.unit_id.has_value());
  EXPECT_EQ(new_info.variants.size(), 1u);
  EXPECT_EQ(new_info.collection_id, kDummyCollectionId);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       OnceGooglePhotosWallpaperSyncInSuccessfully) {
  WallpaperInfo synced_info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kOnceGooglePhotos, base::Time::Now());
  synced_info.location = "_some_google_photos_id";

  base::RunLoop loop;
  WallpaperChangedWaiter waiter(loop.QuitClosure());
  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, synced_info,
                          browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  loop.Run();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  EXPECT_TRUE(new_info.MatchesAsset(synced_info));
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       DailyGooglePhotosWallpaper) {
  WallpaperInfo synced_info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kDailyGooglePhotos, base::Time::Now());
  synced_info.collection_id = "_some_google_photos_collection_id";

  base::RunLoop loop;
  WallpaperChangedWaiter waiter(loop.QuitClosure());
  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, synced_info,
                          browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  loop.Run();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  EXPECT_EQ(new_info.collection_id, synced_info.collection_id);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppWallpaperInfoBrowserTest,
                       CustomWallpaperSyncInSuccessfully) {
  WallpaperInfo synced_info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kCustomized, base::Time::Now());
  synced_info.user_file_path = "_some_user_file_path";

  base::RunLoop loop;
  WallpaperChangedWaiter waiter(loop.QuitClosure());
  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, synced_info,
                          browser()->profile()->GetPrefs(),
                          WallpaperPrefManager::GetSyncPrefName());
  loop.Run();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();
  EXPECT_EQ(new_info.user_file_path, synced_info.user_file_path);
}

class PersonalizationAppVersionedWallpaperInfoBrowserTest
    : public PersonalizationAppWallpaperInfoBrowserTest {
 public:
  PersonalizationAppVersionedWallpaperInfoBrowserTest() {
    scoped_feature_list_.InitAndEnableFeature(
        features::kVersionedWallpaperInfo);
  }

 protected:
  void TestMigratingLocalWallpaperInfo(const WallpaperInfo& unmigrated_info) {
    const AccountId account_id = GetAccountId(browser()->profile());
    PutWallpaperInfoInPrefs(account_id, unmigrated_info,
                            g_browser_process->local_state(),
                            prefs::kUserWallpaperInfo);
    // Migration is triggered by `OnActiveUserPrefServiceChanged`.
    wallpaper_controller()->OnActiveUserPrefServiceChanged(
        browser()->profile()->GetPrefs());
    base::RunLoop().RunUntilIdle();

    WallpaperInfo new_info =
        *wallpaper_controller()->GetActiveUserWallpaperInfo();

    EXPECT_TRUE(new_info.version.IsValid());
    EXPECT_EQ(new_info.version, GetSupportedVersion(new_info.type));
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(
    PersonalizationAppVersionedWallpaperInfoBrowserTest,
    OnlineWallpaperFetchMissingUnitIdSuccessfullyForSyncedPref) {
  WallpaperInfo info =
      WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED,
                    WallpaperType::kOnline, base::Time::Now());
  info.collection_id = kDummyCollectionId;
  info.location = kDummyUrl;

  base::RunLoop loop;
  WallpaperChangedWaiter waiter(loop.QuitClosure());
  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, info, browser()->profile()->GetPrefs(),
                          prefs::kSyncableVersionedWallpaperInfo);
  loop.Run();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();

  // Expects asset_id to be empty.
  EXPECT_FALSE(new_info.asset_id.has_value());
  // Expects unit_id, and variants to be set.
  EXPECT_TRUE(new_info.unit_id.has_value());
  EXPECT_EQ(new_info.variants.size(), 1u);
  EXPECT_EQ(new_info.collection_id, kDummyCollectionId);
  EXPECT_TRUE(new_info.version.IsValid());
  EXPECT_EQ(new_info.version, GetSupportedVersion(new_info.type));
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppVersionedWallpaperInfoBrowserTest,
                       LocalPrefIsMigratedSuccessfullyForOnlineWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.collection_id = kDummyCollectionId;
  unmigrated_info.location = kDummyUrl;
  unmigrated_info.type = WallpaperType::kOnline;
  unmigrated_info.version = base::Version();

  const AccountId account_id = GetAccountId(browser()->profile());
  PutWallpaperInfoInPrefs(account_id, unmigrated_info,
                          g_browser_process->local_state(),
                          prefs::kUserWallpaperInfo);
  // Migration is triggered by `OnActiveUserPrefServiceChanged`.
  wallpaper_controller()->OnActiveUserPrefServiceChanged(
      browser()->profile()->GetPrefs());
  base::RunLoop().RunUntilIdle();

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();

  // Expects asset_id to be empty.
  EXPECT_FALSE(new_info.asset_id.has_value());
  // Expects unit_id, and variants to be set.
  EXPECT_TRUE(new_info.unit_id.has_value());
  EXPECT_EQ(new_info.variants.size(), 1u);
  EXPECT_EQ(new_info.collection_id, kDummyCollectionId);
  EXPECT_TRUE(new_info.version.IsValid());
  EXPECT_EQ(new_info.version, GetSupportedVersion(new_info.type));
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppVersionedWallpaperInfoBrowserTest,
                       LocalPrefIsMigratedSuccessfullyForDailyWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.collection_id = kDummyCollectionId;
  unmigrated_info.location = kDummyUrl;
  unmigrated_info.type = WallpaperType::kDaily;
  unmigrated_info.version = base::Version();
  TestMigratingLocalWallpaperInfo(unmigrated_info);

  WallpaperInfo new_info =
      *wallpaper_controller()->GetActiveUserWallpaperInfo();

  // Expects asset_id to be empty.
  EXPECT_FALSE(new_info.asset_id.has_value());
  // Expects unit_id, and variants to be set.
  EXPECT_TRUE(new_info.unit_id.has_value());
  EXPECT_EQ(new_info.variants.size(), 1u);
  EXPECT_EQ(new_info.collection_id, kDummyCollectionId);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppVersionedWallpaperInfoBrowserTest,
                       LocalPrefIsMigratedSuccessfullyForCustomWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.type = WallpaperType::kCustomized;
  unmigrated_info.user_file_path = "_some_user_file_path";
  unmigrated_info.version = base::Version();
  TestMigratingLocalWallpaperInfo(unmigrated_info);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppVersionedWallpaperInfoBrowserTest,
                       LocalPrefIsMigratedSuccessfullyForDefaultWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.type = WallpaperType::kDefault;
  unmigrated_info.version = base::Version();
  TestMigratingLocalWallpaperInfo(unmigrated_info);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppVersionedWallpaperInfoBrowserTest,
                       LocalPrefIsMigratedSuccessfullyForPolicyWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.type = WallpaperType::kPolicy;
  unmigrated_info.location =
      "547ff840b5bc4aa50d7b57823c47d06cb79c7666/policy-controlled.jpeg";
  unmigrated_info.version = base::Version();
  TestMigratingLocalWallpaperInfo(unmigrated_info);
}

IN_PROC_BROWSER_TEST_F(
    PersonalizationAppVersionedWallpaperInfoBrowserTest,
    LocalPrefIsMigratedSuccessfullyForDailyGooglePhotosWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.type = WallpaperType::kDailyGooglePhotos;
  unmigrated_info.collection_id = "_some_google_photos_collection_id";
  unmigrated_info.version = base::Version();
  TestMigratingLocalWallpaperInfo(unmigrated_info);
}

IN_PROC_BROWSER_TEST_F(
    PersonalizationAppVersionedWallpaperInfoBrowserTest,
    LocalPrefIsMigratedSuccessfullyForOnceGooglePhotosWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.type = WallpaperType::kOnceGooglePhotos;
  unmigrated_info.location = "_some_google_photos_id";
  unmigrated_info.version = base::Version();
  TestMigratingLocalWallpaperInfo(unmigrated_info);
}

IN_PROC_BROWSER_TEST_F(PersonalizationAppVersionedWallpaperInfoBrowserTest,
                       LocalPrefIsMigratedSuccessfullyForSeaPenWallpaper) {
  WallpaperInfo unmigrated_info;
  unmigrated_info.type = WallpaperType::kSeaPen;
  unmigrated_info.location = "1864724739";
  unmigrated_info.version = base::Version();
  TestMigratingLocalWallpaperInfo(unmigrated_info);
}

}  // namespace
}  // namespace ash::personalization_app