chromium/ash/wallpaper/sea_pen_wallpaper_manager_unittest.cc

// Copyright 2023 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/wallpaper/sea_pen_wallpaper_manager.h"

#include <memory>
#include <string>
#include <string_view>
#include <vector>

#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/test/in_process_data_decoder.h"
#include "ash/public/cpp/wallpaper/sea_pen_image.h"
#include "ash/wallpaper/test_sea_pen_wallpaper_manager_session_delegate.h"
#include "ash/wallpaper/wallpaper_utils/sea_pen_metadata_utils.h"
#include "ash/wallpaper/wallpaper_utils/wallpaper_file_utils.h"
#include "ash/webui/common/mojom/sea_pen.mojom.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/i18n/time_formatting.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "base/time/time_override.h"
#include "components/account_id/account_id.h"
#include "components/prefs/testing_pref_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_unittest_util.h"

namespace ash {
namespace {

const std::string kUser1 = "[email protected]";
constexpr std::string_view kExpectedMigrationFileContents =
    "migration_file_contents";
const AccountId kAccountId1 = AccountId::FromUserEmailGaiaId(kUser1, kUser1);
constexpr SkColor kDefaultImageColor = SkColorSetARGB(255, 31, 63, 127);

SkBitmap CreateBitmap(SkColor color = kDefaultImageColor) {
  return gfx::test::CreateBitmap(1, color);
}

std::string CreateJpgBytes(SkColor color = kDefaultImageColor) {
  SkBitmap bitmap = CreateBitmap(color);
  std::vector<unsigned char> data;
  gfx::JPEGCodec::Encode(bitmap, /*quality=*/100, &data);
  return std::string(data.begin(), data.end());
}

base::subtle::ScopedTimeClockOverrides CreateScopedTimeNowOverride() {
  return base::subtle::ScopedTimeClockOverrides(
      []() -> base::Time {
        base::Time fake_now;
        bool success =
            base::Time::FromString("2023-04-05T01:23:45Z", &fake_now);
        DCHECK(success);
        return fake_now;
      },
      nullptr, nullptr);
}

personalization_app::mojom::SeaPenQueryPtr MakeTemplateQuery() {
  return personalization_app::mojom::SeaPenQuery::NewTemplateQuery(
      personalization_app::mojom::SeaPenTemplateQuery::New(
          personalization_app::mojom::SeaPenTemplateId::kFlower,
          ::base::flat_map<personalization_app::mojom::SeaPenTemplateChip,
                           personalization_app::mojom::SeaPenTemplateOption>(
              {{ash::personalization_app::mojom::SeaPenTemplateChip::
                    kFlowerColor,
                ash::personalization_app::mojom::SeaPenTemplateOption::
                    kFlowerColorBlue},
               {ash::personalization_app::mojom::SeaPenTemplateChip::
                    kFlowerType,
                ash::personalization_app::mojom::SeaPenTemplateOption::
                    kFlowerTypeRose}}),
          personalization_app::mojom::SeaPenUserVisibleQuery::New(
              "test template query", "test template title")));
}

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

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

  ~SeaPenWallpaperManagerTest() override = default;

  void SetUp() override {
    testing::Test::SetUp();
    sea_pen_wallpaper_manager_.SetSessionDelegateForTesting(
        std::make_unique<TestSeaPenWallpaperManagerSessionDelegate>());
  }

  base::FilePath GetFilePathForImageId(const AccountId& account_id,
                                       uint32_t image_id) {
    return sea_pen_wallpaper_manager_session_delegate()
        ->GetStorageDirectory(account_id)
        .Append(base::NumberToString(image_id))
        .AddExtension(".jpg");
  }

  std::vector<base::FilePath> GetJpgFilesForAccountId(
      const AccountId& account_id) {
    const auto target_directory =
        sea_pen_wallpaper_manager_session_delegate()->GetStorageDirectory(
            account_id);
    base::FileEnumerator enumerator(target_directory, /*recursive=*/true,
                                    base::FileEnumerator::FILES, "*.jpg");

    std::vector<base::FilePath> result;
    for (auto path = enumerator.Next(); !path.empty();
         path = enumerator.Next()) {
      result.push_back(path);
    }
    return result;
  }

  SeaPenWallpaperManager* sea_pen_wallpaper_manager() {
    return &sea_pen_wallpaper_manager_;
  }

  TestSeaPenWallpaperManagerSessionDelegate*
  sea_pen_wallpaper_manager_session_delegate() {
    return static_cast<TestSeaPenWallpaperManagerSessionDelegate*>(
        sea_pen_wallpaper_manager()->session_delegate_for_testing());
  }

  void SetUpMigrationSourceDir(const AccountId& account_id) {
    ASSERT_TRUE(migration_source_dir_.CreateUniqueTempDir());

    const base::FilePath source_subdir =
        migration_source_dir_.GetPath().Append(account_id.GetAccountIdKey());
    ASSERT_TRUE(base::CreateDirectory(source_subdir));

    const base::FilePath source_file =
        source_subdir.Append("12345").AddExtension(".jpg");
    ASSERT_TRUE(base::WriteFile(source_file, kExpectedMigrationFileContents));
  }

  base::FilePath GetMigrationSourceDir(const AccountId& account_id) {
    return migration_source_dir_.GetPath().Append(account_id.GetAccountIdKey());
  }

 private:
  base::test::TaskEnvironment task_environment_;
  base::ScopedTempDir migration_source_dir_;
  InProcessDataDecoder in_process_data_decoder_;
  TestingPrefServiceSimple profile_prefs_;
  SeaPenWallpaperManager sea_pen_wallpaper_manager_;
};

TEST_F(SeaPenWallpaperManagerTest, DecodesImageAndReturnsId) {
  constexpr uint32_t image_id = 111;
  const base::FilePath file_path = GetFilePathForImageId(kAccountId1, image_id);
  ASSERT_FALSE(base::PathExists(file_path));

  base::test::TestFuture<bool> save_sea_pen_image_future;
  sea_pen_wallpaper_manager()->SaveSeaPenImage(
      kAccountId1, {CreateJpgBytes(), image_id},
      personalization_app::mojom::SeaPenQuery::NewTextQuery("search query"),
      save_sea_pen_image_future.GetCallback());
  ASSERT_TRUE(save_sea_pen_image_future.Get());

  // Use `AreBitmapsClose` because JPG encoding/decoding can alter the color
  // slightly.
  base::test::TestFuture<const gfx::ImageSkia&> get_image_future;
  sea_pen_wallpaper_manager()->GetImage(kAccountId1, image_id,
                                        get_image_future.GetCallback());
  EXPECT_TRUE(gfx::test::AreBitmapsClose(
      CreateBitmap(), *get_image_future.Get<gfx::ImageSkia>().bitmap(),
      /*max_deviation=*/1));
  EXPECT_TRUE(base::PathExists(file_path));
}

TEST_F(SeaPenWallpaperManagerTest, StoresTwelveImages) {
  // Create 12 images in the temp directory.
  for (uint32_t i = 1; i <= 12; i++) {
    base::test::TestFuture<bool> save_sea_pen_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), i},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_sea_pen_image_future.GetCallback());
    ASSERT_TRUE(save_sea_pen_image_future.Get());

    const auto file_path = GetFilePathForImageId(kAccountId1, i);
    EXPECT_TRUE(base::PathExists(file_path));
  }

  EXPECT_THAT(GetIdsFromFilePaths(GetJpgFilesForAccountId(kAccountId1)),
              testing::UnorderedElementsAre(1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u, 9u,
                                            10u, 11u, 12u));
}

TEST_F(SeaPenWallpaperManagerTest, ThirteenthImageReplacesOldest) {
  // Create 12 images in the temp directory.
  for (uint32_t i = 1; i <= 12; i++) {
    base::test::TestFuture<bool> save_sea_pen_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), i},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_sea_pen_image_future.GetCallback());
    ASSERT_TRUE(save_sea_pen_image_future.Get());
  }

  constexpr uint32_t oldest_image_id = 5;
  // Mark image 5 as the oldest by last modified time.
  ASSERT_TRUE(
      base::TouchFile(GetFilePathForImageId(kAccountId1, oldest_image_id),
                      /*last_accessed=*/base::Time::Now(),
                      /*last_modified=*/base::Time::Now() - base::Minutes(30)));

  constexpr uint32_t new_image_id = 13;

  ASSERT_FALSE(
      base::PathExists(GetFilePathForImageId(kAccountId1, new_image_id)));

  // Decode and save the 13th sea pen image.
  base::test::TestFuture<bool> save_sea_pen_image_future;
  sea_pen_wallpaper_manager()->SaveSeaPenImage(
      kAccountId1, {CreateJpgBytes(SK_ColorBLUE), new_image_id},
      personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
      save_sea_pen_image_future.GetCallback());
  ASSERT_TRUE(save_sea_pen_image_future.Get());

  // Use `AreBitmapsClose` because JPG encoding/decoding can alter the color
  // slightly.
  base::test::TestFuture<const gfx::ImageSkia&> get_image_future;
  sea_pen_wallpaper_manager()->GetImage(kAccountId1, new_image_id,
                                        get_image_future.GetCallback());
  EXPECT_TRUE(gfx::test::AreBitmapsClose(CreateBitmap(SK_ColorBLUE),
                                         *get_image_future.Get().bitmap(),
                                         /*max_deviation=*/1));

  // The last modified image should be deleted when the 13th image is added.
  EXPECT_THAT(GetIdsFromFilePaths(GetJpgFilesForAccountId(kAccountId1)),
              testing::UnorderedElementsAre(1u, 2u, 3u, 4u, 6u, 7u, 8u, 9u, 10u,
                                            11u, 12u, new_image_id));
  EXPECT_FALSE(
      base::PathExists(GetFilePathForImageId(kAccountId1, oldest_image_id)));
  EXPECT_TRUE(
      base::PathExists(GetFilePathForImageId(kAccountId1, new_image_id)));
}

TEST_F(SeaPenWallpaperManagerTest, GetImageIds) {
  // Create images in the temp directory.
  for (uint32_t i = 1; i <= 5; i++) {
    base::test::TestFuture<bool> save_sea_pen_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), i * i},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_sea_pen_image_future.GetCallback());
    ASSERT_TRUE(save_sea_pen_image_future.Get());
  }

  {
    base::test::TestFuture<const std::vector<uint32_t>&> get_image_ids_future;
    sea_pen_wallpaper_manager()->GetImageIds(
        kAccountId1, get_image_ids_future.GetCallback());
    EXPECT_THAT(get_image_ids_future.Take(),
                testing::UnorderedElementsAre(1, 4, 9, 16, 25));
  }

  {
    base::test::TestFuture<bool> delete_recent_sea_pen_image_future;
    sea_pen_wallpaper_manager()->DeleteSeaPenImage(
        kAccountId1, 16u, delete_recent_sea_pen_image_future.GetCallback());
    ASSERT_TRUE(delete_recent_sea_pen_image_future.Take());
  }

  {
    base::test::TestFuture<const std::vector<uint32_t>&> get_image_ids_future;
    sea_pen_wallpaper_manager()->GetImageIds(
        kAccountId1, get_image_ids_future.GetCallback());
    EXPECT_THAT(get_image_ids_future.Take(),
                testing::UnorderedElementsAre(1, 4, 9, 25));
  }
}

TEST_F(SeaPenWallpaperManagerTest, GetImageIdsMultipleAccounts) {
  {
    // Create an image for account 1.
    base::test::TestFuture<bool> save_sea_pen_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), 77},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_sea_pen_image_future.GetCallback());
    ASSERT_TRUE(save_sea_pen_image_future.Get());
  }

  const std::string kUser2 = "[email protected]";
  const AccountId kAccountId2 = AccountId::FromUserEmailGaiaId(kUser2, kUser2);
  ASSERT_NE(kAccountId1.GetAccountIdKey(), kAccountId2.GetAccountIdKey());

  {
    // Create an image for account 2.
    base::test::TestFuture<bool> save_sea_pen_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId2, {CreateJpgBytes(), 987654321},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_sea_pen_image_future.GetCallback());
    ASSERT_TRUE(save_sea_pen_image_future.Get());
  }

  {
    // Retrieve images for account 1.
    base::test::TestFuture<const std::vector<uint32_t>&> get_image_ids_future;
    sea_pen_wallpaper_manager()->GetImageIds(
        kAccountId1, get_image_ids_future.GetCallback());
    EXPECT_THAT(get_image_ids_future.Take(), testing::UnorderedElementsAre(77));
  }

  {
    // Retrieve images for account 2.
    base::test::TestFuture<const std::vector<uint32_t>&> get_image_ids_future;
    sea_pen_wallpaper_manager()->GetImageIds(
        kAccountId2, get_image_ids_future.GetCallback());
    EXPECT_THAT(get_image_ids_future.Take(),
                testing::UnorderedElementsAre(987654321));
  }
}

TEST_F(SeaPenWallpaperManagerTest, GetImageIdsSortedByLastModifiedTime) {
  // Create images in the temp directory.
  base::Time fake_last_modified_time;
  ASSERT_TRUE(
      base::Time::FromString("2018-03-05T16:16:16Z", &fake_last_modified_time));

  for (uint32_t i = 1; i <= 5; i++) {
    base::test::TestFuture<bool> save_sea_pen_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), i * i},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_sea_pen_image_future.GetCallback());
    ASSERT_TRUE(save_sea_pen_image_future.Get());

    // File save does not respect base::Time::Now override as it will use actual
    // file system stat calls, so set it manually.
    const base::FilePath image_path =
        sea_pen_wallpaper_manager_session_delegate()
            ->GetStorageDirectory(kAccountId1)
            .Append(base::NumberToString(i * i))
            .AddExtension(".jpg");
    ASSERT_TRUE(base::TouchFile(image_path, fake_last_modified_time,
                                fake_last_modified_time));
    fake_last_modified_time += base::Seconds(1);
  }

  {
    base::test::TestFuture<const std::vector<uint32_t>&> get_image_ids_future;
    sea_pen_wallpaper_manager()->GetImageIds(
        kAccountId1, get_image_ids_future.GetCallback());
    EXPECT_THAT(get_image_ids_future.Get(),
                testing::ElementsAre(25, 16, 9, 4, 1));
  }

  {
    // Mark 4 as newest.
    base::test::TestFuture<bool> touch_file_future;
    sea_pen_wallpaper_manager()->TouchFile(kAccountId1, 4);
  }

  {
    base::test::TestFuture<const std::vector<uint32_t>&> get_image_ids_future;
    sea_pen_wallpaper_manager()->GetImageIds(
        kAccountId1, get_image_ids_future.GetCallback());
    EXPECT_THAT(get_image_ids_future.Get(),
                testing::ElementsAre(4, 25, 16, 9, 1));
  }
}

TEST_F(SeaPenWallpaperManagerTest, GetImageAndMetadataSuccess) {
  constexpr uint32_t image_id = 88888888;
  const auto time_override = CreateScopedTimeNowOverride();

  {
    base::test::TestFuture<bool> save_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), image_id}, MakeTemplateQuery(),
        save_image_future.GetCallback());
    ASSERT_TRUE(save_image_future.Get());
  }

  {
    base::test::TestFuture<const gfx::ImageSkia&,
                           personalization_app::mojom::RecentSeaPenImageInfoPtr>
        get_image_and_metadata_future;
    sea_pen_wallpaper_manager()->GetImageAndMetadata(
        kAccountId1, image_id, get_image_and_metadata_future.GetCallback());

    EXPECT_TRUE(gfx::test::AreBitmapsClose(
        CreateBitmap(),
        *get_image_and_metadata_future.Get<gfx::ImageSkia>().bitmap(),
        /*max_deviation=*/1));
    EXPECT_TRUE(MakeTemplateQuery().Equals(
        get_image_and_metadata_future
            .Get<personalization_app::mojom::RecentSeaPenImageInfoPtr>()
            ->query));
    // base::Time::Now is overridden to return a fixed date.
    EXPECT_EQ(base::TimeFormatShortDate(base::Time::Now()),
              get_image_and_metadata_future
                  .Get<personalization_app::mojom::RecentSeaPenImageInfoPtr>()
                  ->creation_time.value());
  }
}

TEST_F(SeaPenWallpaperManagerTest, GetImageAndMetadataInvalidJson) {
  constexpr uint32_t image_id = 918273645;
  const auto time_override = CreateScopedTimeNowOverride();

  {
    const auto template_query = MakeTemplateQuery();
    // Create valid metadata dict.
    base::Value::Dict query_dict = SeaPenQueryToDict(MakeTemplateQuery());

    // Rename a necessary field to cause parsing failure.
    ASSERT_TRUE(query_dict.contains("user_visible_query_text"));
    query_dict.Set("user_visible_query_text_bad",
                   query_dict.Extract("user_visible_query_text").value());

    // Write the jpg with invalid metadata.
    const base::FilePath target_file_path =
        GetFilePathForImageId(kAccountId1, image_id);
    ASSERT_TRUE(base::CreateDirectory(target_file_path.DirName()));
    gfx::ImageSkia test_image =
        gfx::ImageSkia::CreateFrom1xBitmap(CreateBitmap());
    ASSERT_TRUE(ResizeAndSaveWallpaper(
        test_image, target_file_path,
        WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED, test_image.size(),
        QueryDictToXmpString(query_dict)));
  }

  base::test::TestFuture<const gfx::ImageSkia&,
                         personalization_app::mojom::RecentSeaPenImageInfoPtr>
      get_image_and_metadata_future;
  sea_pen_wallpaper_manager()->GetImageAndMetadata(
      kAccountId1, image_id, get_image_and_metadata_future.GetCallback());

  // Image loading still succeeds.
  EXPECT_TRUE(gfx::test::AreBitmapsClose(
      CreateBitmap(),
      *get_image_and_metadata_future.Get<gfx::ImageSkia>().bitmap(),
      /*max_deviation=*/1));

  // No metadata loaded.
  EXPECT_TRUE(get_image_and_metadata_future
                  .Get<personalization_app::mojom::RecentSeaPenImageInfoPtr>()
                  .is_null());
}

TEST_F(SeaPenWallpaperManagerTest, GetImageAndMetadataNonExistentId) {
  constexpr uint32_t image_id = 88888888;

  ASSERT_FALSE(base::PathExists(GetFilePathForImageId(kAccountId1, image_id)));

  base::test::TestFuture<const gfx::ImageSkia&,
                         personalization_app::mojom::RecentSeaPenImageInfoPtr>
      get_image_and_metadata_future;
  sea_pen_wallpaper_manager()->GetImageAndMetadata(
      kAccountId1, image_id, get_image_and_metadata_future.GetCallback());

  EXPECT_TRUE(get_image_and_metadata_future.Get<gfx::ImageSkia>().isNull());
  EXPECT_TRUE(get_image_and_metadata_future
                  .Get<personalization_app::mojom::RecentSeaPenImageInfoPtr>()
                  .is_null());
}

TEST_F(SeaPenWallpaperManagerTest, GetImageAndMetadataOtherAccount) {
  constexpr uint32_t image_id = 8888;
  {
    // Write an image for first account.
    base::test::TestFuture<bool> save_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), image_id},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_image_future.GetCallback());
    ASSERT_TRUE(save_image_future.Get());
  }

  {
    // Try to retrieve the image with another account.
    const AccountId other_account_id = AccountId::FromUserEmailGaiaId(
        "[email protected]", "[email protected]");

    base::test::TestFuture<const gfx::ImageSkia&,
                           personalization_app::mojom::RecentSeaPenImageInfoPtr>
        get_image_and_metadata_future;
    sea_pen_wallpaper_manager()->GetImageAndMetadata(
        other_account_id, image_id,
        get_image_and_metadata_future.GetCallback());

    EXPECT_TRUE(get_image_and_metadata_future.Get<gfx::ImageSkia>().isNull());
    EXPECT_TRUE(get_image_and_metadata_future
                    .Get<personalization_app::mojom::RecentSeaPenImageInfoPtr>()
                    .is_null());
  }
}

TEST_F(SeaPenWallpaperManagerTest, GetTemplateIdFromFileSuccess) {
  const uint32_t image_id = 88888888;

  {
    base::test::TestFuture<bool> save_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), image_id}, MakeTemplateQuery(),
        save_image_future.GetCallback());
    ASSERT_TRUE(save_image_future.Get());
  }

  {
    base::test::TestFuture<std::optional<int>> get_template_id_future;
    sea_pen_wallpaper_manager()->GetTemplateIdFromFile(
        kAccountId1, image_id, get_template_id_future.GetCallback());
    EXPECT_EQ(static_cast<int>(
                  ash::personalization_app::mojom::SeaPenTemplateId::kFlower),
              *get_template_id_future.Get());
  }
}

TEST_F(SeaPenWallpaperManagerTest, GetTemplateIdFromFileFailure) {
  const uint32_t image_id = 88888888;

  {
    base::test::TestFuture<bool> save_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), image_id},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_image_future.GetCallback());
    ASSERT_TRUE(save_image_future.Get());
  }

  {
    base::test::TestFuture<std::optional<int>> get_template_id_future;
    sea_pen_wallpaper_manager()->GetTemplateIdFromFile(
        kAccountId1, image_id, get_template_id_future.GetCallback());
    EXPECT_FALSE(get_template_id_future.Get());
  }
}

TEST_F(SeaPenWallpaperManagerTest, DeleteNonExistentImage) {
  // File does not exist yet. Deleting it should fail.
  base::test::TestFuture<bool> delete_sea_pen_image_future;
  sea_pen_wallpaper_manager()->DeleteSeaPenImage(
      kAccountId1, 111u, delete_sea_pen_image_future.GetCallback());
  EXPECT_FALSE(delete_sea_pen_image_future.Get());
}

TEST_F(SeaPenWallpaperManagerTest, DeleteImageRemovesFromDisk) {
  constexpr uint32_t image_id = 1234u;

  {
    // Save a test image.
    base::test::TestFuture<bool> save_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        kAccountId1, {CreateJpgBytes(), image_id},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_image_future.GetCallback());

    ASSERT_TRUE(save_image_future.Get());
    ASSERT_TRUE(base::PathExists(GetFilePathForImageId(kAccountId1, image_id)));
  }

  {
    // Delete the test image.
    base::test::TestFuture<bool> delete_image_future;
    sea_pen_wallpaper_manager()->DeleteSeaPenImage(
        kAccountId1, image_id, delete_image_future.GetCallback());

    EXPECT_TRUE(delete_image_future.Get());
    EXPECT_FALSE(
        base::PathExists(GetFilePathForImageId(kAccountId1, image_id)));
  }
}

TEST_F(SeaPenWallpaperManagerTest, DeleteImageForOtherUserFails) {
  constexpr uint32_t image_id = 999u;
  const AccountId other_account_id = AccountId::FromUserEmailGaiaId(
      "[email protected]", "[email protected]");

  // Save a test image with the same id for both users.
  for (const auto& account_id : {kAccountId1, other_account_id}) {
    base::test::TestFuture<bool> save_image_future;
    sea_pen_wallpaper_manager()->SaveSeaPenImage(
        account_id, {CreateJpgBytes(), image_id},
        personalization_app::mojom::SeaPenQuery::NewTextQuery("test query"),
        save_image_future.GetCallback());

    ASSERT_TRUE(save_image_future.Get());
    ASSERT_TRUE(base::PathExists(GetFilePathForImageId(kAccountId1, image_id)));
  }

  {
    // Delete the image for first account id.
    base::test::TestFuture<bool> delete_image_future;
    sea_pen_wallpaper_manager()->DeleteSeaPenImage(
        kAccountId1, image_id, delete_image_future.GetCallback());

    EXPECT_TRUE(delete_image_future.Get());
    EXPECT_FALSE(
        base::PathExists(GetFilePathForImageId(kAccountId1, image_id)));
  }

  // Image still exists for other account id.
  ASSERT_TRUE(
      base::PathExists(GetFilePathForImageId(other_account_id, image_id)));

  {
    // Try delete the image for first account id again, should fail.
    base::test::TestFuture<bool> delete_image_future;
    sea_pen_wallpaper_manager()->DeleteSeaPenImage(
        kAccountId1, image_id, delete_image_future.GetCallback());

    EXPECT_FALSE(delete_image_future.Get());
    EXPECT_FALSE(
        base::PathExists(GetFilePathForImageId(kAccountId1, image_id)));
  }

  // Image still exists for other account id.
  ASSERT_TRUE(
      base::PathExists(GetFilePathForImageId(other_account_id, image_id)));
}

TEST_F(SeaPenWallpaperManagerTest, MigrateMovesFiles) {
  SetUpMigrationSourceDir(kAccountId1);
  ASSERT_TRUE(base::PathExists(GetMigrationSourceDir(kAccountId1)));

  base::test::TestFuture<bool> migrate_sea_pen_files_if_necessary_future;
  sea_pen_wallpaper_manager()->Migrate(
      kAccountId1, GetMigrationSourceDir(kAccountId1),
      migrate_sea_pen_files_if_necessary_future.GetCallback());
  EXPECT_TRUE(migrate_sea_pen_files_if_necessary_future.Get());

  std::string migrated_file_contents;
  EXPECT_TRUE(
      base::ReadFileToString(sea_pen_wallpaper_manager_session_delegate()
                                 ->GetStorageDirectory(kAccountId1)
                                 .Append("12345.jpg"),
                             &migrated_file_contents));
  EXPECT_EQ(kExpectedMigrationFileContents, migrated_file_contents);

  EXPECT_FALSE(base::PathExists(GetMigrationSourceDir(kAccountId1)));

  base::test::TestFuture<const std::vector<uint32_t>&> get_image_ids_future;
  sea_pen_wallpaper_manager()->GetImageIds(kAccountId1,
                                           get_image_ids_future.GetCallback());

  EXPECT_EQ(std::vector<uint32_t>{12345}, get_image_ids_future.Get());
}

TEST_F(SeaPenWallpaperManagerTest, MigrateWritesPrefs) {
  EXPECT_EQ(
      SeaPenWallpaperManager::MigrationStatus::kNotStarted,
      static_cast<SeaPenWallpaperManager::MigrationStatus>(
          sea_pen_wallpaper_manager_session_delegate()
              ->GetPrefService(kAccountId1)
              ->GetInteger(::ash::prefs::kWallpaperSeaPenMigrationStatus)));

  base::ScopedTempDir source_dir;
  ASSERT_TRUE(source_dir.CreateUniqueTempDir());

  const base::FilePath source_subdir =
      source_dir.GetPath().Append(kAccountId1.GetAccountIdKey());
  ASSERT_TRUE(base::CreateDirectory(source_subdir));

  base::test::TestFuture<bool> migrate_sea_pen_files_if_necessary_future;

  sea_pen_wallpaper_manager()->Migrate(
      kAccountId1, source_subdir,
      migrate_sea_pen_files_if_necessary_future.GetCallback());

  EXPECT_EQ(
      SeaPenWallpaperManager::MigrationStatus::kCrashed,
      static_cast<SeaPenWallpaperManager::MigrationStatus>(
          sea_pen_wallpaper_manager_session_delegate()
              ->GetPrefService(kAccountId1)
              ->GetInteger(::ash::prefs::kWallpaperSeaPenMigrationStatus)))
      << "kCrashed should have been written as migration started";

  EXPECT_TRUE(migrate_sea_pen_files_if_necessary_future.Get());

  EXPECT_EQ(
      SeaPenWallpaperManager::MigrationStatus::kSuccess,
      static_cast<SeaPenWallpaperManager::MigrationStatus>(
          sea_pen_wallpaper_manager_session_delegate()
              ->GetPrefService(kAccountId1)
              ->GetInteger(::ash::prefs::kWallpaperSeaPenMigrationStatus)));
}

}  // namespace
}  // namespace ash