// Copyright 2018 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/web_applications/web_app_icon_manager.h"

#include <algorithm>
#include <memory>
#include <optional>
#include <string>
#include <utility>

#include "base/files/file_enumerator.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "chrome/browser/web_applications/test/fake_web_app_database_factory.h"
#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
#include "chrome/browser/web_applications/test/test_file_utils.h"
#include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_test.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_icon_generator.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_install_manager.h"
#include "chrome/browser/web_applications/web_app_registrar.h"
#include "chrome/browser/web_applications/web_app_registry_update.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chrome/browser/web_applications/web_app_utils.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/testing_profile.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/base/resource/resource_scale_factor.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/gfx/test/sk_gmock_support.h"

namespace web_app {

namespace {

using IconSizeAndPurpose = WebAppIconManager::IconSizeAndPurpose;

// Returns a vector of item infos for the shortcuts menu. Each item is empty
// except `downloaded_icon_sizes` is set to `icon_sizes`.
std::vector<WebAppShortcutsMenuItemInfo> CreateShortcutsMenuItemInfos(
    int num_menu_items,
    const IconSizes& icon_sizes) {
  std::vector<WebAppShortcutsMenuItemInfo> result;

  for (int i = 0; i < num_menu_items; ++i) {
    result.emplace_back();
    result.back().downloaded_icon_sizes = icon_sizes;
  }

  return result;
}

}  // namespace

class WebAppIconManagerTest : public WebAppTest {
 public:
  void SetUp() override {
    WebAppTest::SetUp();
    test::AwaitStartWebAppProviderAndSubsystems(profile());
  }

 protected:
  void WriteGeneratedShortcutsMenuIcons(
      const webapps::AppId& app_id,
      const std::vector<GeneratedIconsInfo>& icons_info,
      int num_menu_items) {
    ShortcutsMenuIconBitmaps shortcuts_menu_icons;

    for (int i = 0; i < num_menu_items; ++i) {
      IconBitmaps menu_item_icon_map;

      for (const GeneratedIconsInfo& info : icons_info) {
        DCHECK_EQ(info.sizes_px.size(), info.colors.size());

        OrderedSizeToBitmap generated_bitmaps;

        for (size_t j = 0; j < info.sizes_px.size(); ++j) {
          AddGeneratedIcon(&generated_bitmaps, info.sizes_px[j],
                           info.colors[j]);
        }

        menu_item_icon_map.SetBitmapsForPurpose(info.purpose,
                                                std::move(generated_bitmaps));
      }

      shortcuts_menu_icons.push_back(std::move(menu_item_icon_map));
    }

    base::RunLoop run_loop;
    icon_manager().WriteData(app_id, {}, {}, std::move(shortcuts_menu_icons),
                             {}, base::BindLambdaForTesting([&](bool success) {
                               EXPECT_TRUE(success);
                               run_loop.Quit();
                             }));
    run_loop.Run();
  }

  ShortcutsMenuIconBitmaps ReadAllShortcutsMenuIcons(
      const webapps::AppId& app_id) {
    ShortcutsMenuIconBitmaps result;
    base::RunLoop run_loop;
    icon_manager().ReadAllShortcutsMenuIcons(
        app_id, base::BindLambdaForTesting(
                    [&](ShortcutsMenuIconBitmaps shortcuts_menu_icons_map) {
                      result = std::move(shortcuts_menu_icons_map);
                      run_loop.Quit();
                    }));
    run_loop.Run();
    return result;
  }

  struct PurposeAndBitmap {
    IconPurpose purpose;
    SkBitmap bitmap;
  };
  PurposeAndBitmap ReadSmallestIcon(const webapps::AppId& app_id,
                                    const std::vector<IconPurpose>& purposes,
                                    SquareSizePx min_icon_size) {
    PurposeAndBitmap result;
    base::RunLoop run_loop;
    icon_manager().ReadSmallestIcon(
        app_id, purposes, min_icon_size,
        base::BindLambdaForTesting([&](IconPurpose purpose, SkBitmap bitmap) {
          result.purpose = purpose;
          result.bitmap = std::move(bitmap);
          run_loop.Quit();
        }));
    run_loop.Run();
    return result;
  }

  struct PurposeAndData {
    IconPurpose purpose;
    std::vector<uint8_t> data;
  };
  PurposeAndData ReadSmallestCompressedIcon(
      const webapps::AppId& app_id,
      const std::vector<IconPurpose>& purposes,
      int min_size_in_px) {
    EXPECT_TRUE(
        icon_manager().HasSmallestIcon(app_id, purposes, min_size_in_px));

    PurposeAndData result;

    base::RunLoop run_loop;
    icon_manager().ReadSmallestCompressedIcon(
        app_id, purposes, min_size_in_px,
        base::BindLambdaForTesting(
            [&](IconPurpose purpose, std::vector<uint8_t> data) {
              result.purpose = purpose;
              result.data = std::move(data);
              run_loop.Quit();
            }));

    run_loop.Run();
    return result;
  }

  SkColor ReadIconAndResize(const webapps::AppId& app_id,
                            IconPurpose purpose,
                            int desired_icon_size) {
    base::RunLoop run_loop;
    SkColor icon_color = SK_ColorBLACK;

    icon_manager().ReadIconAndResize(
        app_id, purpose, desired_icon_size,
        base::BindLambdaForTesting([&](OrderedSizeToBitmap icon_bitmaps) {
          EXPECT_EQ(1u, icon_bitmaps.size());
          SkBitmap bitmap = icon_bitmaps[desired_icon_size];
          EXPECT_FALSE(bitmap.empty());
          EXPECT_EQ(desired_icon_size, bitmap.width());
          EXPECT_EQ(desired_icon_size, bitmap.height());
          icon_color = bitmap.getColor(0, 0);

          run_loop.Quit();
        }));

    run_loop.Run();
    return icon_color;
  }

  SkColor ReadIconAndResize(const webapps::AppId& app_id,
                            int desired_icon_size) {
    return ReadIconAndResize(app_id, IconPurpose::ANY, desired_icon_size);
  }

  void AddAppToRegistry(std::unique_ptr<WebApp> web_app) {
    ScopedRegistryUpdate update = sync_bridge().BeginUpdate();
    update->CreateApp(std::move(web_app));
  }

  // Read favicons on web_app installation and await
  // WebAppIconManager::favicon_read_callback_ synchronously.
  void AwaitReadFaviconOnAddingWebApp(std::unique_ptr<WebApp> web_app) {
    const webapps::AppId& app_id = web_app->app_id();
    base::RunLoop run_loop;
    icon_manager().SetFaviconReadCallbackForTesting(
        base::BindLambdaForTesting([&](const webapps::AppId& cached_app_id) {
          EXPECT_EQ(cached_app_id, app_id);
          run_loop.Quit();
        }));

    AddAppToRegistry(std::move(web_app));
    install_manager().NotifyWebAppInstalled(app_id);
    run_loop.Run();
  }

  // Read Monochrome favicons on web_app installation and await
  // WebAppIconManager::favicon_monochrome_read_callback_ synchronously.
  void AwaitReadFaviconMonochromeOnAddingWebApp(
      std::unique_ptr<WebApp> web_app) {
    const webapps::AppId& app_id = web_app->app_id();
    base::RunLoop run_loop;
    icon_manager().SetFaviconMonochromeReadCallbackForTesting(
        base::BindLambdaForTesting([&](const webapps::AppId& cached_app_id) {
          EXPECT_EQ(cached_app_id, app_id);
          run_loop.Quit();
        }));

    AddAppToRegistry(std::move(web_app));
    install_manager().NotifyWebAppInstalled(app_id);
    run_loop.Run();
  }

  base::FilePath GetAppTrustedIconsDir(Profile* profile,
                                       const webapps::AppId& app_id) {
    base::FilePath web_apps_root_directory = GetWebAppsRootDirectory(profile);
    base::FilePath app_dir =
        GetManifestResourcesDirectoryForApp(web_apps_root_directory, app_id);
    return app_dir.AppendASCII("Trusted Icons");
  }

  base::FilePath GetAppPendingTrustedIconsDir(Profile* profile,
                                              const webapps::AppId& app_id) {
    base::FilePath web_apps_root_directory = GetWebAppsRootDirectory(profile);
    base::FilePath app_dir =
        GetManifestResourcesDirectoryForApp(web_apps_root_directory, app_id);
    return app_dir.AppendASCII("Pending Trusted Icons");
  }

  base::FilePath GetAppPendingManifestIconsDir(Profile* profile,
                                               const webapps::AppId& app_id) {
    base::FilePath web_apps_root_directory = GetWebAppsRootDirectory(profile);
    base::FilePath app_dir =
        GetManifestResourcesDirectoryForApp(web_apps_root_directory, app_id);
    return app_dir.AppendASCII("Pending Manifest Icons");
  }

  WebAppRegistrar& registrar() { return fake_provider().registrar_unsafe(); }
  WebAppInstallManager& install_manager() {
    return fake_provider().install_manager();
  }
  WebAppSyncBridge& sync_bridge() {
    return fake_provider().sync_bridge_unsafe();
  }
  WebAppIconManager& icon_manager() { return fake_provider().icon_manager(); }
  TestFileUtils& file_utils() {
    return *fake_provider().file_utils()->AsTestFileUtils();
  }
};

TEST_F(WebAppIconManagerTest, WriteAndReadIcons_AnyOnly) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k256, icon_size::k512};
  const std::vector<SkColor> colors{SK_ColorGREEN, SK_ColorYELLOW};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AddAppToRegistry(std::move(web_app));

  EXPECT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::ANY, sizes_px));
  {
    base::RunLoop run_loop;

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::ANY, sizes_px,
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::ANY);
          EXPECT_EQ(2u, icon_bitmaps.size());

          EXPECT_FALSE(icon_bitmaps[icon_size::k256].empty());
          EXPECT_EQ(SK_ColorGREEN,
                    icon_bitmaps[icon_size::k256].getColor(0, 0));

          EXPECT_FALSE(icon_bitmaps[icon_size::k512].empty());
          EXPECT_EQ(SK_ColorYELLOW,
                    icon_bitmaps[icon_size::k512].getColor(0, 0));

          run_loop.Quit();
        }));

    run_loop.Run();
  }
  EXPECT_FALSE(
      icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, sizes_px));
}

TEST_F(WebAppIconManagerTest, WriteAndReadIcons_MaskableOnly) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k256, icon_size::k512};
  const std::vector<SkColor> colors{SK_ColorGREEN, SK_ColorYELLOW};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::MASKABLE, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, sizes_px);

  AddAppToRegistry(std::move(web_app));

  EXPECT_FALSE(icon_manager().HasIcons(app_id, IconPurpose::ANY, sizes_px));
  EXPECT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, sizes_px));
  {
    base::RunLoop run_loop;

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::MASKABLE, sizes_px,
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::MASKABLE);
          EXPECT_EQ(2u, icon_bitmaps.size());

          EXPECT_FALSE(icon_bitmaps[icon_size::k256].empty());
          EXPECT_EQ(SK_ColorGREEN,
                    icon_bitmaps[icon_size::k256].getColor(0, 0));

          EXPECT_FALSE(icon_bitmaps[icon_size::k512].empty());
          EXPECT_EQ(SK_ColorYELLOW,
                    icon_bitmaps[icon_size::k512].getColor(0, 0));

          run_loop.Quit();
        }));

    run_loop.Run();
  }
}

TEST_F(WebAppIconManagerTest, WriteAndReadIcons_MonochromeOnly) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k128, icon_size::k256};
  const std::vector<SkColor> colors{SK_ColorGREEN, SK_ColorTRANSPARENT};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::MONOCHROME, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::MONOCHROME, sizes_px);

  AddAppToRegistry(std::move(web_app));

  EXPECT_FALSE(icon_manager().HasIcons(app_id, IconPurpose::ANY, sizes_px));
  EXPECT_FALSE(
      icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, sizes_px));
  EXPECT_TRUE(
      icon_manager().HasIcons(app_id, IconPurpose::MONOCHROME, sizes_px));
  {
    base::RunLoop run_loop;

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::MONOCHROME, sizes_px,
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::MONOCHROME);
          EXPECT_EQ(2u, icon_bitmaps.size());

          EXPECT_FALSE(icon_bitmaps[icon_size::k128].empty());
          EXPECT_EQ(SK_ColorGREEN,
                    icon_bitmaps[icon_size::k128].getColor(0, 0));

          EXPECT_FALSE(icon_bitmaps[icon_size::k256].empty());
          EXPECT_EQ(SK_ColorTRANSPARENT,
                    icon_bitmaps[icon_size::k256].getColor(0, 0));

          run_loop.Quit();
        }));

    run_loop.Run();
  }
}

TEST_F(WebAppIconManagerTest, WriteAndReadIcons_AnyAndMaskable) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k256, icon_size::k512};
  const std::vector<SkColor> colors{SK_ColorGREEN, SK_ColorYELLOW};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors},
                                  {IconPurpose::MASKABLE, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, sizes_px);

  AddAppToRegistry(std::move(web_app));

  EXPECT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::ANY, sizes_px));
  {
    base::RunLoop run_loop;

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::ANY, sizes_px,
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::ANY);
          EXPECT_EQ(2u, icon_bitmaps.size());

          EXPECT_FALSE(icon_bitmaps[icon_size::k256].empty());
          EXPECT_EQ(SK_ColorGREEN,
                    icon_bitmaps[icon_size::k256].getColor(0, 0));

          EXPECT_FALSE(icon_bitmaps[icon_size::k512].empty());
          EXPECT_EQ(SK_ColorYELLOW,
                    icon_bitmaps[icon_size::k512].getColor(0, 0));

          run_loop.Quit();
        }));

    run_loop.Run();
  }
  EXPECT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, sizes_px));
  {
    base::RunLoop run_loop;

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::MASKABLE, sizes_px,
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::MASKABLE);
          EXPECT_EQ(2u, icon_bitmaps.size());

          EXPECT_FALSE(icon_bitmaps[icon_size::k256].empty());
          EXPECT_EQ(SK_ColorGREEN,
                    icon_bitmaps[icon_size::k256].getColor(0, 0));

          EXPECT_FALSE(icon_bitmaps[icon_size::k512].empty());
          EXPECT_EQ(SK_ColorYELLOW,
                    icon_bitmaps[icon_size::k512].getColor(0, 0));

          run_loop.Quit();
        }));

    run_loop.Run();
  }
}

TEST_F(WebAppIconManagerTest, WriteAndReadIcons_AnyAndMonochrome) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px_any{icon_size::k256, icon_size::k512};
  const std::vector<SkColor> colors_any{SK_ColorGREEN, SK_ColorYELLOW};

  const std::vector<int> sizes_px_monochrome{icon_size::k64, icon_size::k128};
  const std::vector<SkColor> colors_monochrome{SK_ColorRED, SK_ColorBLUE};

  IconManagerWriteGeneratedIcons(
      icon_manager(), app_id,
      {{IconPurpose::ANY, sizes_px_any, colors_any},
       {IconPurpose::MONOCHROME, sizes_px_monochrome, colors_monochrome}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px_any);
  web_app->SetDownloadedIconSizes(IconPurpose::MONOCHROME, sizes_px_monochrome);

  AddAppToRegistry(std::move(web_app));

  EXPECT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::ANY, sizes_px_any));
  EXPECT_FALSE(icon_manager().HasIcons(app_id, IconPurpose::MASKABLE,
                                       sizes_px_monochrome));
  {
    base::RunLoop run_loop;

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::ANY, sizes_px_any,
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::ANY);
          EXPECT_EQ(2u, icon_bitmaps.size());

          EXPECT_FALSE(icon_bitmaps[icon_size::k256].empty());
          EXPECT_EQ(SK_ColorGREEN,
                    icon_bitmaps[icon_size::k256].getColor(0, 0));

          EXPECT_FALSE(icon_bitmaps[icon_size::k512].empty());
          EXPECT_EQ(SK_ColorYELLOW,
                    icon_bitmaps[icon_size::k512].getColor(0, 0));

          run_loop.Quit();
        }));

    run_loop.Run();
  }
  EXPECT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::MONOCHROME,
                                      sizes_px_monochrome));
  {
    base::RunLoop run_loop;

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::MONOCHROME, sizes_px_monochrome,
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::MONOCHROME);
          EXPECT_EQ(2u, icon_bitmaps.size());

          EXPECT_FALSE(icon_bitmaps[icon_size::k64].empty());
          EXPECT_EQ(SK_ColorRED, icon_bitmaps[icon_size::k64].getColor(0, 0));

          EXPECT_FALSE(icon_bitmaps[icon_size::k128].empty());
          EXPECT_EQ(SK_ColorBLUE, icon_bitmaps[icon_size::k128].getColor(0, 0));

          run_loop.Quit();
        }));

    run_loop.Run();
  }
}

TEST_F(WebAppIconManagerTest, OverwriteIcons) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // Write initial red icons to be overwritten.
  {
    std::vector<int> sizes_px{icon_size::k32, icon_size::k64, icon_size::k48};
    const std::vector<SkColor> colors{SK_ColorRED, SK_ColorRED, SK_ColorRED};
    IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                   {{IconPurpose::ANY, sizes_px, colors},
                                    {IconPurpose::MASKABLE, sizes_px, colors}});

    web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
    web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, std::move(sizes_px));
  }

  AddAppToRegistry(std::move(web_app));

  // k64 and k48 sizes to be overwritten. Skip k32 size and add new k96 size.
  const std::vector<int> overwritten_sizes_px{icon_size::k48, icon_size::k64,
                                              icon_size::k96};
  {
    IconBitmaps icon_bitmaps;
    for (int size_px : overwritten_sizes_px) {
      icon_bitmaps.any[size_px] = CreateSquareIcon(size_px, SK_ColorGREEN);
      icon_bitmaps.maskable[size_px] = CreateSquareIcon(size_px, SK_ColorBLUE);
    }

    base::RunLoop run_loop;

    // Overwrite red icons with green and blue ones.
    icon_manager().WriteData(app_id, std::move(icon_bitmaps), {}, {}, {},
                             base::BindLambdaForTesting([&](bool success) {
                               EXPECT_TRUE(success);
                               run_loop.Quit();
                             }));

    run_loop.Run();

    ScopedRegistryUpdate update = sync_bridge().BeginUpdate();
    update->UpdateApp(app_id)->SetDownloadedIconSizes(IconPurpose::ANY,
                                                      overwritten_sizes_px);
    update->UpdateApp(app_id)->SetDownloadedIconSizes(IconPurpose::MASKABLE,
                                                      overwritten_sizes_px);
  }

  // Check that all IconPurpose::ANY icons are now green. Check that all red
  // icons were deleted on disk (including the k32 size).
  {
    base::FilePath icons_dir = GetAppIconsAnyDir(profile(), app_id);
    std::vector<int> sizes_on_disk_px;

    base::FileEnumerator enumerator_any(icons_dir, true,
                                        base::FileEnumerator::FILES);
    for (base::FilePath path = enumerator_any.Next(); !path.empty();
         path = enumerator_any.Next()) {
      EXPECT_TRUE(path.MatchesExtension(FILE_PATH_LITERAL(".png")));

      SkBitmap bitmap = ReadBitmap(&file_utils(), path);
      EXPECT_FALSE(bitmap.empty());
      EXPECT_EQ(bitmap.width(), bitmap.height());
      EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(0, 0));

      sizes_on_disk_px.push_back(bitmap.width());
    }

    std::sort(sizes_on_disk_px.begin(), sizes_on_disk_px.end());
    EXPECT_EQ(overwritten_sizes_px, sizes_on_disk_px);
  }

  // Check that all IconPurpose::Maskable icons are now blue. Check that all red
  // icons were deleted on disk (including the k32 size).
  {
    base::FilePath icons_dir = GetAppIconsMaskableDir(profile(), app_id);
    std::vector<int> sizes_on_disk_px;

    base::FileEnumerator enumerator_maskable(icons_dir, true,
                                             base::FileEnumerator::FILES);
    for (base::FilePath path = enumerator_maskable.Next(); !path.empty();
         path = enumerator_maskable.Next()) {
      EXPECT_TRUE(path.MatchesExtension(FILE_PATH_LITERAL(".png")));

      SkBitmap bitmap = ReadBitmap(&file_utils(), path);
      EXPECT_FALSE(bitmap.empty());
      EXPECT_EQ(bitmap.width(), bitmap.height());
      EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, 0));

      sizes_on_disk_px.push_back(bitmap.width());
    }

    std::sort(sizes_on_disk_px.begin(), sizes_on_disk_px.end());
    EXPECT_EQ(overwritten_sizes_px, sizes_on_disk_px);
  }
}

TEST_F(WebAppIconManagerTest, ReadAllIconsLastUpdateTime) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k256, icon_size::k512};
  const std::vector<SkColor> colors{SK_ColorGREEN, SK_ColorYELLOW};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AddAppToRegistry(std::move(web_app));
  base::test::TestFuture<base::flat_map<SquareSizePx, base::Time>> future;
  {
    icon_manager().ReadIconsLastUpdateTime(app_id, future.GetCallback());
    EXPECT_TRUE(future.Wait());
  }
  base::flat_map<SquareSizePx, base::Time> time_data_map = future.Get();
  EXPECT_EQ(2u, time_data_map.size());
  EXPECT_FALSE(time_data_map[sizes_px[0]].is_null());
  EXPECT_FALSE(time_data_map[sizes_px[1]].is_null());
}

TEST_F(WebAppIconManagerTest, ReadAllShortcutMenuIconsWithTimestamp) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const int num_menu_items = 2;

  const std::vector<int> sizes = {icon_size::k64, icon_size::k128};
  const std::vector<SkColor> colors = {SK_ColorRED, SK_ColorRED};

  WriteGeneratedShortcutsMenuIcons(app_id,
                                   {{IconPurpose::ANY, sizes, colors},
                                    {IconPurpose::MASKABLE, sizes, colors},
                                    {IconPurpose::MONOCHROME, sizes, colors}},
                                   num_menu_items);

  IconSizes icon_sizes;
  icon_sizes.any = sizes;
  icon_sizes.maskable = sizes;
  icon_sizes.monochrome = sizes;

  web_app->SetShortcutsMenuInfo(
      CreateShortcutsMenuItemInfos(num_menu_items, icon_sizes));

  AddAppToRegistry(std::move(web_app));

  WebAppIconManager::ShortcutIconDataVector time_data_map;
  base::test::TestFuture<WebAppIconManager::ShortcutIconDataVector> future;
  {
    icon_manager().ReadAllShortcutMenuIconsWithTimestamp(app_id,
                                                         future.GetCallback());
    time_data_map = future.Get();
  }

  ASSERT_THAT(time_data_map.size(), num_menu_items);

  ASSERT_FALSE(time_data_map[0][IconPurpose::ANY][icon_size::k64].is_null());
  ASSERT_FALSE(time_data_map[0][IconPurpose::ANY][icon_size::k128].is_null());
  ASSERT_FALSE(
      time_data_map[0][IconPurpose::MASKABLE][icon_size::k64].is_null());
  ASSERT_FALSE(
      time_data_map[0][IconPurpose::MASKABLE][icon_size::k128].is_null());
  ASSERT_FALSE(
      time_data_map[0][IconPurpose::MONOCHROME][icon_size::k64].is_null());
  ASSERT_FALSE(
      time_data_map[0][IconPurpose::MONOCHROME][icon_size::k128].is_null());

  ASSERT_FALSE(time_data_map[1][IconPurpose::ANY][icon_size::k64].is_null());
  ASSERT_FALSE(time_data_map[1][IconPurpose::ANY][icon_size::k128].is_null());
  ASSERT_FALSE(
      time_data_map[1][IconPurpose::MASKABLE][icon_size::k64].is_null());
  ASSERT_FALSE(
      time_data_map[1][IconPurpose::MASKABLE][icon_size::k128].is_null());
  ASSERT_FALSE(
      time_data_map[1][IconPurpose::MONOCHROME][icon_size::k64].is_null());
  ASSERT_FALSE(
      time_data_map[1][IconPurpose::MONOCHROME][icon_size::k128].is_null());
}

TEST_F(WebAppIconManagerTest, ReadShortcutsMenuIconsFailed) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<SquareSizePx> sizes_px_any{icon_size::k96, icon_size::k256};
  const int num_menu_items = 10;

  IconSizes icon_sizes;
  icon_sizes.any = sizes_px_any;

  // Set shortcuts menu icons meta-info but don't write bitmaps to disk.
  web_app->SetShortcutsMenuInfo(
      CreateShortcutsMenuItemInfos(num_menu_items, icon_sizes));

  AddAppToRegistry(std::move(web_app));

  // Request shortcuts menu icons which don't exist on disk.
  ShortcutsMenuIconBitmaps shortcuts_menu_icons_map =
      ReadAllShortcutsMenuIcons(app_id);
  EXPECT_EQ(10u, shortcuts_menu_icons_map.size());
  for (const auto& icon_map : shortcuts_menu_icons_map) {
    EXPECT_TRUE(icon_map.empty());
  }
}

TEST_F(WebAppIconManagerTest, WriteAndReadAllShortcutsMenuIcons) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const int num_menu_items = 3;

  const std::vector<int> sizes_any = {icon_size::k64, icon_size::k128,
                                      icon_size::k256};
  const std::vector<SkColor> colors_any = {SK_ColorRED, SK_ColorWHITE,
                                           SK_ColorBLUE};

  const std::vector<int> sizes_maskable = {icon_size::k64, icon_size::k96,
                                           icon_size::k128};
  const std::vector<SkColor> colors_maskable = {SK_ColorCYAN, SK_ColorMAGENTA,
                                                SK_ColorYELLOW};

  const std::vector<int> sizes_monochrome = {icon_size::k64, icon_size::k96,
                                             icon_size::k128};
  const std::vector<SkColor> colors_monochrome = {SK_ColorGREEN, SK_ColorBLACK,
                                                  SK_ColorTRANSPARENT};

  WriteGeneratedShortcutsMenuIcons(
      app_id,
      {{IconPurpose::ANY, sizes_any, colors_any},
       {IconPurpose::MASKABLE, sizes_maskable, colors_maskable},
       {IconPurpose::MONOCHROME, sizes_monochrome, colors_monochrome}},
      num_menu_items);

  IconSizes icon_sizes;
  icon_sizes.any = sizes_any;
  icon_sizes.maskable = sizes_maskable;
  icon_sizes.monochrome = sizes_monochrome;

  web_app->SetShortcutsMenuInfo(
      CreateShortcutsMenuItemInfos(num_menu_items, icon_sizes));

  AddAppToRegistry(std::move(web_app));

  ShortcutsMenuIconBitmaps shortcuts_menu_icons_map =
      ReadAllShortcutsMenuIcons(app_id);
  EXPECT_EQ(3u, shortcuts_menu_icons_map.size());

  for (int i = 0; i < num_menu_items; ++i) {
    for (IconPurpose purpose : kIconPurposes) {
      SCOPED_TRACE(purpose);

      const std::vector<int>* expect_sizes;
      const std::vector<SkColor>* expect_colors;

      switch (purpose) {
        case IconPurpose::ANY:
          expect_sizes = &sizes_any;
          expect_colors = &colors_any;
          break;
        case IconPurpose::MASKABLE:
          expect_sizes = &sizes_maskable;
          expect_colors = &colors_maskable;
          break;
        case IconPurpose::MONOCHROME:
          expect_sizes = &sizes_monochrome;
          expect_colors = &colors_monochrome;
          break;
      }

      const OrderedSizeToBitmap& icon_bitmaps =
          shortcuts_menu_icons_map[i].GetBitmapsForPurpose(purpose);

      ASSERT_EQ(expect_sizes->size(), expect_colors->size());

      for (unsigned s = 0; s < expect_sizes->size(); ++s) {
        const SquareSizePx size_px = (*expect_sizes)[s];

        const auto& size_and_bitmap = icon_bitmaps.find(size_px);
        ASSERT_TRUE(size_and_bitmap != icon_bitmaps.end());

        EXPECT_EQ((*expect_colors)[s], size_and_bitmap->second.getColor(0, 0));
      }
    }
  }
}

TEST_F(WebAppIconManagerTest, WriteNonProductIconsEmptyMaps) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  web_app->SetShortcutsMenuInfo({});

  AddAppToRegistry(std::move(web_app));

  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {}, {}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();

  // Make sure that nothing was written to disk.
  ShortcutsMenuIconBitmaps shortcuts_menu_icons_map =
      ReadAllShortcutsMenuIcons(app_id);
  EXPECT_EQ(0u, shortcuts_menu_icons_map.size());

  EXPECT_FALSE(
      file_utils().PathExists(GetAppTrustedIconsDir(profile(), app_id)));
  EXPECT_FALSE(file_utils().PathExists(GetOtherIconsDir(profile(), app_id)));
  // TODO(estade): check that WebAppIconManager returns no data when other icons
  // are read. (When there is a read function.)
}

TEST_F(WebAppIconManagerTest, WriteTrustedIconsIntoDisk) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps trusted_bitmaps;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k32, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  SkBitmap any_bitmap3 = CreateSquareIcon(icon_size::k128, SK_ColorYELLOW);
  trusted_bitmaps.any[icon_size::k32] = any_bitmap1;
  trusted_bitmaps.any[icon_size::k64] = any_bitmap2;
  trusted_bitmaps.any[icon_size::k128] = any_bitmap3;

  SkBitmap maskable_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  SkBitmap maskable_bitmap2 = CreateSquareIcon(icon_size::k512, SK_ColorCYAN);
  SkBitmap maskable_bitmap3 = CreateSquareIcon(icon_size::k96, SK_ColorGRAY);
  trusted_bitmaps.maskable[icon_size::k256] = maskable_bitmap1;
  trusted_bitmaps.maskable[icon_size::k512] = maskable_bitmap2;
  trusted_bitmaps.maskable[icon_size::k96] = maskable_bitmap3;

  // Verify writing trusted icons to disk correctly.
  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {}, {trusted_bitmaps}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();
  EXPECT_TRUE(
      file_utils().PathExists(GetAppTrustedIconsDir(profile(), app_id)));

  // Verify bitmaps of purpose `any` are written correctly to disk.
  base::FilePath any_trusted_icons =
      GetAppTrustedIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_icons =
      ReadPngsFromDirectory(&file_utils(), any_trusted_icons);
  EXPECT_EQ(3u, any_icons.size());
  gfx::test::AreBitmapsEqual(any_bitmap1, any_icons[icon_size::k32]);
  gfx::test::AreBitmapsEqual(any_bitmap2, any_icons[icon_size::k64]);
  gfx::test::AreBitmapsEqual(any_bitmap3, any_icons[icon_size::k128]);

  // Verify bitmaps of purpose `maskable` are written correctly to disk.
  base::FilePath maskable_trusted_icons =
      GetAppTrustedIconsDir(profile(), app_id).AppendASCII("Icons Maskable");
  OrderedSizeToBitmap maskable_icons =
      ReadPngsFromDirectory(&file_utils(), maskable_trusted_icons);
  EXPECT_EQ(3u, any_icons.size());
  gfx::test::AreBitmapsEqual(maskable_bitmap1, maskable_icons[icon_size::k256]);
  gfx::test::AreBitmapsEqual(maskable_bitmap2, maskable_icons[icon_size::k512]);
  gfx::test::AreBitmapsEqual(maskable_bitmap3, maskable_icons[icon_size::k96]);
}

TEST_F(WebAppIconManagerTest, WriteTrustedAndManifestIconsBoth) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps manifest_icons;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k32, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  manifest_icons.any[icon_size::k64] = any_bitmap1;
  manifest_icons.any[icon_size::k128] = any_bitmap2;

  IconBitmaps trusted_icons;
  SkBitmap any_trusted_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  SkBitmap any_trusted_bitmap2 =
      CreateSquareIcon(icon_size::k512, SK_ColorCYAN);
  trusted_icons.any[icon_size::k256] = any_trusted_bitmap1;
  trusted_icons.any[icon_size::k512] = any_trusted_bitmap2;

  // Verify writing trusted icons to disk correctly.
  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {manifest_icons}, {trusted_icons}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();
  EXPECT_TRUE(
      file_utils().PathExists(GetAppTrustedIconsDir(profile(), app_id)));
  ASSERT_TRUE(file_utils().PathExists(GetAppIconsAnyDir(profile(), app_id)));

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // trusted folder.
  base::FilePath any_trusted_icons =
      GetAppTrustedIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_icons =
      ReadPngsFromDirectory(&file_utils(), any_trusted_icons);
  EXPECT_EQ(2u, any_icons.size());
  gfx::test::AreBitmapsEqual(any_trusted_bitmap1, any_icons[icon_size::k256]);
  gfx::test::AreBitmapsEqual(any_trusted_bitmap2, any_icons[icon_size::k512]);

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // manifest icons folder.
  OrderedSizeToBitmap disk_icons = ReadPngsFromDirectory(
      &file_utils(), GetAppIconsAnyDir(profile(), app_id));
  EXPECT_EQ(2u, disk_icons.size());
  gfx::test::AreBitmapsEqual(any_bitmap1, disk_icons[icon_size::k64]);
  gfx::test::AreBitmapsEqual(any_bitmap2, disk_icons[icon_size::k128]);
}

TEST_F(WebAppIconManagerTest, WriteAndReadTrustedIcons) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps trusted_bitmaps;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k32, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  SkBitmap any_bitmap3 = CreateSquareIcon(icon_size::k128, SK_ColorYELLOW);
  SkBitmap any_bitmap4 = CreateSquareIcon(icon_size::k256, SK_ColorBLACK);
  trusted_bitmaps.any[icon_size::k32] = any_bitmap1;
  trusted_bitmaps.any[icon_size::k64] = any_bitmap2;
  trusted_bitmaps.any[icon_size::k128] = any_bitmap3;
  trusted_bitmaps.any[icon_size::k256] = any_bitmap4;

  SkBitmap maskable_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  SkBitmap maskable_bitmap2 = CreateSquareIcon(icon_size::k512, SK_ColorCYAN);
  SkBitmap maskable_bitmap3 = CreateSquareIcon(icon_size::k64, SK_ColorGRAY);
  trusted_bitmaps.maskable[icon_size::k256] = maskable_bitmap1;
  trusted_bitmaps.maskable[icon_size::k512] = maskable_bitmap2;
  trusted_bitmaps.maskable[icon_size::k64] = maskable_bitmap3;

  // Verify writing trusted icons to disk correctly.
  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {}, {trusted_bitmaps}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();
  EXPECT_TRUE(
      file_utils().PathExists(GetAppTrustedIconsDir(profile(), app_id)));

  // Verify reading trusted icons from disk correctly, without taking purpose
  // into account.
  base::test::TestFuture<IconMetadataFromDisk> icons_future;
  icon_manager().ReadTrustedIconsWithFallbackToManifestIcons(
      app_id, {icon_size::k64, icon_size::k256}, IconPurpose::ANY,
      icons_future.GetCallback());
  ASSERT_TRUE(icons_future.Wait());
  auto icon_metadata = icons_future.Take();
  OrderedSizeToBitmap bitmaps_from_disk = std::move(icon_metadata.icons_map);
  EXPECT_EQ(2u, bitmaps_from_disk.size());

  SkBitmap bitmap_for_size256 = any_bitmap4;
  SkBitmap bitmap_for_size64 = any_bitmap2;
  IconPurpose expected_purpose = IconPurpose::ANY;
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
  bitmap_for_size256 = maskable_bitmap1;
  bitmap_for_size64 = maskable_bitmap3;
  expected_purpose = IconPurpose::MASKABLE;
#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)

  // Verify that maskable icons are preferred over any icons depending on OS.
  EXPECT_THAT(bitmaps_from_disk[icon_size::k256],
              gfx::test::EqualsBitmap(bitmap_for_size256));
  EXPECT_THAT(bitmaps_from_disk[icon_size::k64],
              gfx::test::EqualsBitmap(bitmap_for_size64));
  EXPECT_EQ(expected_purpose, icon_metadata.purpose);
}

TEST_F(WebAppIconManagerTest, ReadTrustedIconsFallbackToManifest) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  IconBitmaps manifest_icons;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k128, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k512, SK_ColorBLUE);
  manifest_icons.any[icon_size::k128] = any_bitmap1;
  manifest_icons.any[icon_size::k512] = any_bitmap2;

  web_app->SetDownloadedIconSizes(IconPurpose::ANY,
                                  {icon_size::k128, icon_size::k512});
  AddAppToRegistry(std::move(web_app));

  // Verify writing trusted icons to disk correctly.
  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {manifest_icons}, {}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();

  base::test::TestFuture<IconMetadataFromDisk> icons_future;
  icon_manager().ReadTrustedIconsWithFallbackToManifestIcons(
      app_id, {icon_size::k128, icon_size::k512}, IconPurpose::ANY,
      icons_future.GetCallback());
  ASSERT_TRUE(icons_future.Wait());

  auto icon_metadata = icons_future.Take();
  OrderedSizeToBitmap bitmaps_from_disk = std::move(icon_metadata.icons_map);
  EXPECT_EQ(2u, bitmaps_from_disk.size());

  // This is obtained from the directory containing the manifest icons.
  EXPECT_THAT(bitmaps_from_disk[icon_size::k128],
              gfx::test::EqualsBitmap(any_bitmap1));
  EXPECT_THAT(bitmaps_from_disk[icon_size::k512],
              gfx::test::EqualsBitmap(any_bitmap2));
  EXPECT_EQ(icon_metadata.purpose, IconPurpose::ANY);
}

TEST_F(WebAppIconManagerTest, TrustedIconsOfSizeNotFoundNoFallback) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps trusted_bitmaps;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k32, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  trusted_bitmaps.any[icon_size::k32] = any_bitmap1;
  trusted_bitmaps.any[icon_size::k64] = any_bitmap2;

  // Verify writing trusted icons to disk correctly.
  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {}, {trusted_bitmaps}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();

  // Verify reading trusted icons from disk correctly, without taking purpose
  // into account.
  base::test::TestFuture<IconMetadataFromDisk> icons_future;
  icon_manager().ReadTrustedIconsWithFallbackToManifestIcons(
      app_id, {icon_size::k64, icon_size::k256}, IconPurpose::ANY,
      icons_future.GetCallback());
  ASSERT_TRUE(icons_future.Wait());

  auto icon_metadata = icons_future.Take();
  OrderedSizeToBitmap bitmaps_from_disk = std::move(icon_metadata.icons_map);

  // Verify only icon of size 64 is read from the disk.
  EXPECT_EQ(1u, bitmaps_from_disk.size());
  EXPECT_THAT(bitmaps_from_disk[icon_size::k64],
              gfx::test::EqualsBitmap(any_bitmap2));
  EXPECT_FALSE(bitmaps_from_disk.contains(icon_size::k256));
  EXPECT_EQ(icon_metadata.purpose, IconPurpose::ANY);
}

TEST_F(WebAppIconManagerTest, WriteOtherIconsToDisk) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  AddAppToRegistry(std::move(web_app));

  IconsMap other_icons;
  const GURL example_gurl("https://example.com/image.png");
  AddIconToIconsMap(example_gurl, 48, SK_ColorBLUE, &other_icons);
  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {}, {}, {}, other_icons,
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();

  base::FilePath other_icons_dir = GetOtherIconsDir(profile(), app_id);
  EXPECT_TRUE(file_utils().DirectoryExists(other_icons_dir));
  EXPECT_FALSE(file_utils().IsDirectoryEmpty(other_icons_dir));
  // TODO(estade): check that WebAppIconManager returns correct data when other
  // icons are read. (When there is a read function.)
}

TEST_F(WebAppIconManagerTest, WritePendingTrustedIconsIntoDisk) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps pending_trusted_bitmaps;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k32, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  SkBitmap any_bitmap3 = CreateSquareIcon(icon_size::k128, SK_ColorYELLOW);
  pending_trusted_bitmaps.any[icon_size::k32] = any_bitmap1;
  pending_trusted_bitmaps.any[icon_size::k64] = any_bitmap2;
  pending_trusted_bitmaps.any[icon_size::k128] = any_bitmap3;

  SkBitmap maskable_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  SkBitmap maskable_bitmap2 = CreateSquareIcon(icon_size::k512, SK_ColorCYAN);
  SkBitmap maskable_bitmap3 = CreateSquareIcon(icon_size::k96, SK_ColorGRAY);
  pending_trusted_bitmaps.maskable[icon_size::k256] = maskable_bitmap1;
  pending_trusted_bitmaps.maskable[icon_size::k512] = maskable_bitmap2;
  pending_trusted_bitmaps.maskable[icon_size::k96] = maskable_bitmap3;

  // Verify writing pending trusted icons to disk correctly.
  base::RunLoop run_loop;
  icon_manager().WritePendingIconData(
      app_id, pending_trusted_bitmaps, {},
      base::BindLambdaForTesting([&](bool success) {
        EXPECT_TRUE(success);
        run_loop.Quit();
      }));
  run_loop.Run();
  EXPECT_TRUE(
      file_utils().PathExists(GetAppPendingTrustedIconsDir(profile(), app_id)));

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // pending trusted folder.
  base::FilePath any_trusted_icons_dir =
      GetAppPendingTrustedIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_icons =
      ReadPngsFromDirectory(&file_utils(), any_trusted_icons_dir);
  EXPECT_EQ(3u, any_icons.size());
  gfx::test::AreBitmapsEqual(any_bitmap1, any_icons[icon_size::k32]);
  gfx::test::AreBitmapsEqual(any_bitmap2, any_icons[icon_size::k64]);
  gfx::test::AreBitmapsEqual(any_bitmap3, any_icons[icon_size::k128]);

  // Verify bitmaps of purpose `maskable` are written correctly to disk under
  // the pending trusted folder.
  base::FilePath maskable_trusted_icons_dir =
      GetAppPendingTrustedIconsDir(profile(), app_id)
          .AppendASCII("Icons Maskable");
  OrderedSizeToBitmap maskable_icons =
      ReadPngsFromDirectory(&file_utils(), maskable_trusted_icons_dir);
  EXPECT_EQ(3u, any_icons.size());
  gfx::test::AreBitmapsEqual(maskable_bitmap1, maskable_icons[icon_size::k256]);
  gfx::test::AreBitmapsEqual(maskable_bitmap2, maskable_icons[icon_size::k512]);
  gfx::test::AreBitmapsEqual(maskable_bitmap3, maskable_icons[icon_size::k96]);
}

TEST_F(WebAppIconManagerTest, WritePendingManifestIconsIntoDisk) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps pending_manifest_bitmaps;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k32, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  SkBitmap any_bitmap3 = CreateSquareIcon(icon_size::k128, SK_ColorYELLOW);
  pending_manifest_bitmaps.any[icon_size::k32] = any_bitmap1;
  pending_manifest_bitmaps.any[icon_size::k64] = any_bitmap2;
  pending_manifest_bitmaps.any[icon_size::k128] = any_bitmap3;

  SkBitmap maskable_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  SkBitmap maskable_bitmap2 = CreateSquareIcon(icon_size::k512, SK_ColorCYAN);
  SkBitmap maskable_bitmap3 = CreateSquareIcon(icon_size::k96, SK_ColorGRAY);
  pending_manifest_bitmaps.maskable[icon_size::k256] = maskable_bitmap1;
  pending_manifest_bitmaps.maskable[icon_size::k512] = maskable_bitmap2;
  pending_manifest_bitmaps.maskable[icon_size::k96] = maskable_bitmap3;

  // Verify writing pending manifest icons to disk correctly.
  base::RunLoop run_loop;
  icon_manager().WritePendingIconData(
      app_id, {}, pending_manifest_bitmaps,
      base::BindLambdaForTesting([&](bool success) {
        EXPECT_TRUE(success);
        run_loop.Quit();
      }));
  run_loop.Run();
  EXPECT_TRUE(file_utils().PathExists(
      GetAppPendingManifestIconsDir(profile(), app_id)));

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // pending manifest folder.
  base::FilePath any_pending_manifest_icons_dir =
      GetAppPendingManifestIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_icons =
      ReadPngsFromDirectory(&file_utils(), any_pending_manifest_icons_dir);
  EXPECT_EQ(3u, any_icons.size());
  gfx::test::AreBitmapsEqual(any_bitmap1, any_icons[icon_size::k32]);
  gfx::test::AreBitmapsEqual(any_bitmap2, any_icons[icon_size::k64]);
  gfx::test::AreBitmapsEqual(any_bitmap3, any_icons[icon_size::k128]);

  // Verify bitmaps of purpose `maskable` are written correctly to disk under
  // the pending manifest folder.
  base::FilePath maskable_pending_manifest_icons_dir =
      GetAppPendingManifestIconsDir(profile(), app_id)
          .AppendASCII("Icons Maskable");
  OrderedSizeToBitmap maskable_icons =
      ReadPngsFromDirectory(&file_utils(), maskable_pending_manifest_icons_dir);
  EXPECT_EQ(3u, any_icons.size());
  gfx::test::AreBitmapsEqual(maskable_bitmap1, maskable_icons[icon_size::k256]);
  gfx::test::AreBitmapsEqual(maskable_bitmap2, maskable_icons[icon_size::k512]);
  gfx::test::AreBitmapsEqual(maskable_bitmap3, maskable_icons[icon_size::k96]);
}

TEST_F(WebAppIconManagerTest, WritePendingTrustedAndPendingManifestIconsBoth) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps pending_trusted_icons;
  SkBitmap any_trusted_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  SkBitmap any_trusted_bitmap2 =
      CreateSquareIcon(icon_size::k512, SK_ColorCYAN);
  pending_trusted_icons.any[icon_size::k256] = any_trusted_bitmap1;
  pending_trusted_icons.any[icon_size::k512] = any_trusted_bitmap2;

  IconBitmaps pending_manifest_icons;
  SkBitmap any_bitmap1 = CreateSquareIcon(icon_size::k32, SK_ColorGREEN);
  SkBitmap any_bitmap2 = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  pending_manifest_icons.any[icon_size::k64] = any_bitmap1;
  pending_manifest_icons.any[icon_size::k128] = any_bitmap2;

  // Verify writing pending trusted and pending manifest icons to disk
  // correctly.
  base::RunLoop run_loop;
  icon_manager().WritePendingIconData(
      app_id, pending_trusted_icons, pending_manifest_icons,
      base::BindLambdaForTesting([&](bool success) {
        EXPECT_TRUE(success);
        run_loop.Quit();
      }));
  run_loop.Run();
  EXPECT_TRUE(
      file_utils().PathExists(GetAppPendingTrustedIconsDir(profile(), app_id)));
  EXPECT_TRUE(file_utils().PathExists(
      GetAppPendingManifestIconsDir(profile(), app_id)));

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // pending trusted folder.
  base::FilePath any_pending_trusted_icons_dir =
      GetAppPendingTrustedIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_pending_trusted_icons =
      ReadPngsFromDirectory(&file_utils(), any_pending_trusted_icons_dir);
  EXPECT_EQ(2u, any_pending_trusted_icons.size());
  gfx::test::AreBitmapsEqual(any_trusted_bitmap1,
                             any_pending_trusted_icons[icon_size::k256]);
  gfx::test::AreBitmapsEqual(any_trusted_bitmap2,
                             any_pending_trusted_icons[icon_size::k512]);

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // pending manifest folder.
  base::FilePath any_pending_manifest_icons_dir =
      GetAppPendingManifestIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_pending_manifest_icons =
      ReadPngsFromDirectory(&file_utils(), any_pending_manifest_icons_dir);
  EXPECT_EQ(2u, any_pending_manifest_icons.size());
  gfx::test::AreBitmapsEqual(any_bitmap1,
                             any_pending_manifest_icons[icon_size::k32]);
  gfx::test::AreBitmapsEqual(any_bitmap2,
                             any_pending_manifest_icons[icon_size::k64]);
}

// Verify that pending update data can be written and it won't wipe out the
// manifest icon data even if manifest_icons is empty.
TEST_F(WebAppIconManagerTest, PendingIconsDoNotOverwriteManifestIcons) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AddAppToRegistry(std::move(web_app));

  IconBitmaps manifest_icons;
  SkBitmap any_bitmap = CreateSquareIcon(icon_size::k64, SK_ColorBLUE);
  manifest_icons.any[icon_size::k64] = any_bitmap;

  IconBitmaps pending_trusted_icons;
  SkBitmap any_trusted_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  pending_trusted_icons.any[icon_size::k256] = any_trusted_bitmap1;

  // Verify writing trusted icons to disk correctly.
  {
    base::RunLoop run_loop;
    icon_manager().WriteData(app_id, {manifest_icons}, {}, {}, {},
                             base::BindLambdaForTesting([&](bool success) {
                               EXPECT_TRUE(success);
                               run_loop.Quit();
                             }));
    run_loop.Run();
  }

  EXPECT_TRUE(file_utils().PathExists(GetAppIconsAnyDir(profile(), app_id)));
  EXPECT_FALSE(
      file_utils().PathExists(GetAppPendingTrustedIconsDir(profile(), app_id)));

  {
    base::RunLoop run_loop;
    icon_manager().WritePendingIconData(
        app_id, pending_trusted_icons, {},
        base::BindLambdaForTesting([&](bool success) {
          EXPECT_TRUE(success);
          run_loop.Quit();
        }));
    run_loop.Run();
  }

  EXPECT_TRUE(file_utils().PathExists(GetAppIconsAnyDir(profile(), app_id)));
  EXPECT_TRUE(
      file_utils().PathExists(GetAppPendingTrustedIconsDir(profile(), app_id)));

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // pending trusted icons folder.
  base::FilePath any_pending_trusted_icons =
      GetAppPendingTrustedIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_icons =
      ReadPngsFromDirectory(&file_utils(), any_pending_trusted_icons);
  EXPECT_EQ(1u, any_icons.size());
  EXPECT_THAT(any_icons[icon_size::k256],
              gfx::test::EqualsBitmap(any_trusted_bitmap1));

  // Verify bitmaps of purpose `any` are written correctly to disk under the
  // manifest icons folder and are not wiped out by the double write call.
  OrderedSizeToBitmap disk_icons = ReadPngsFromDirectory(
      &file_utils(), GetAppIconsAnyDir(profile(), app_id));
  EXPECT_EQ(1u, disk_icons.size());
  EXPECT_THAT(disk_icons[icon_size::k64], gfx::test::EqualsBitmap(any_bitmap));
}

// Creating shortcut icons but no manifest icons can cause the top level icon
// storing directory to not be created. Verify storing of pending update images
// works fine without that, and with other icons also working correctly.
TEST_F(WebAppIconManagerTest, PendingIconsEmptyManifestIconDirShortcutIcons) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // Writing shortcuts menu icons correctly to disk.
  const int num_menu_items = 2;
  const std::vector<int> sizes = {icon_size::k64, icon_size::k128};
  const std::vector<SkColor> colors = {SK_ColorRED, SK_ColorRED};
  WriteGeneratedShortcutsMenuIcons(app_id,
                                   {{IconPurpose::ANY, sizes, colors},
                                    {IconPurpose::MASKABLE, sizes, colors},
                                    {IconPurpose::MONOCHROME, sizes, colors}},
                                   num_menu_items);
  IconSizes icon_sizes;
  icon_sizes.any = sizes;
  icon_sizes.maskable = sizes;
  icon_sizes.monochrome = sizes;
  web_app->SetShortcutsMenuInfo(
      CreateShortcutsMenuItemInfos(num_menu_items, icon_sizes));

  AddAppToRegistry(std::move(web_app));

  // Verify no manifest icons exist for the app.
  EXPECT_FALSE(file_utils().PathExists(GetAppIconsAnyDir(profile(), app_id)));

  IconBitmaps pending_trusted_icons;
  SkBitmap any_trusted_bitmap = CreateSquareIcon(icon_size::k256, SK_ColorRED);
  pending_trusted_icons.any[icon_size::k256] = any_trusted_bitmap;

  {
    base::RunLoop run_loop;
    icon_manager().WritePendingIconData(
        app_id, pending_trusted_icons, {},
        base::BindLambdaForTesting([&](bool success) {
          EXPECT_TRUE(success);
          run_loop.Quit();
        }));
    run_loop.Run();
  }

  EXPECT_TRUE(
      file_utils().PathExists(GetAppPendingTrustedIconsDir(profile(), app_id)));

  // Verify pending icon bitmaps are correctly read from the pending icons
  // directory.
  base::FilePath any_pending_trusted_icons =
      GetAppPendingTrustedIconsDir(profile(), app_id).AppendASCII("Icons");
  OrderedSizeToBitmap any_icons =
      ReadPngsFromDirectory(&file_utils(), any_pending_trusted_icons);
  EXPECT_EQ(1u, any_icons.size());
  EXPECT_THAT(any_icons[icon_size::k256],
              gfx::test::EqualsBitmap(any_trusted_bitmap));

  // Verify shortcut menu item infos are read correctly as well post operation.
  ShortcutsMenuIconBitmaps shortcuts_menu_icons_map =
      ReadAllShortcutsMenuIcons(app_id);
  EXPECT_EQ(2u, shortcuts_menu_icons_map.size());

  for (int i = 0; i < num_menu_items; ++i) {
    for (IconPurpose purpose : kIconPurposes) {
      const OrderedSizeToBitmap& icon_bitmaps =
          shortcuts_menu_icons_map[i].GetBitmapsForPurpose(purpose);

      for (unsigned s = 0; s < sizes.size(); ++s) {
        const SquareSizePx size_px = sizes[s];
        const auto& size_and_bitmap = icon_bitmaps.find(size_px);
        ASSERT_TRUE(size_and_bitmap != icon_bitmaps.end());
        EXPECT_EQ(colors[s], size_and_bitmap->second.getColor(0, 0));
      }
    }
  }
}

TEST_F(WebAppIconManagerTest, ReadIconsFailed) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<SquareSizePx> icon_sizes_px{icon_size::k256};

  // Set icon meta-info but don't write bitmap to disk.
  web_app->SetDownloadedIconSizes(IconPurpose::ANY, icon_sizes_px);

  AddAppToRegistry(std::move(web_app));

  EXPECT_FALSE(
      icon_manager().HasIcons(app_id, IconPurpose::ANY, {icon_size::k96}));
  EXPECT_TRUE(
      icon_manager().HasIcons(app_id, IconPurpose::ANY, {icon_size::k256}));
  EXPECT_FALSE(icon_manager().HasIcons(app_id, IconPurpose::ANY,
                                       {icon_size::k96, icon_size::k256}));

  // Request existing icon size which doesn't exist on disk.
  base::RunLoop run_loop;

  icon_manager().ReadUntrustedIcons(
      app_id, IconPurpose::ANY, icon_sizes_px,
      base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
        OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
        EXPECT_EQ(icon_metadata.purpose, IconPurpose::ANY);
        EXPECT_TRUE(icon_bitmaps.empty());
        run_loop.Quit();
      }));

  run_loop.Run();
}

TEST_F(WebAppIconManagerTest, FindExact) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{10, 60, 50, 20, 30};
  const std::vector<SkColor> colors{SK_ColorRED, SK_ColorYELLOW, SK_ColorGREEN,
                                    SK_ColorBLUE, SK_ColorMAGENTA};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AddAppToRegistry(std::move(web_app));

  EXPECT_FALSE(icon_manager().HasIcons(app_id, IconPurpose::ANY, {40}));
  EXPECT_FALSE(icon_manager().HasIcons(app_id, IconPurpose::MASKABLE, {20}));

  {
    base::RunLoop run_loop;

    EXPECT_TRUE(icon_manager().HasIcons(app_id, IconPurpose::ANY, {20}));

    icon_manager().ReadUntrustedIcons(
        app_id, IconPurpose::ANY, {20},
        base::BindLambdaForTesting([&](IconMetadataFromDisk icon_metadata) {
          OrderedSizeToBitmap icon_bitmaps = std::move(icon_metadata.icons_map);
          EXPECT_EQ(icon_metadata.purpose, IconPurpose::ANY);
          EXPECT_EQ(1u, icon_bitmaps.size());
          EXPECT_FALSE(icon_bitmaps[20].empty());
          EXPECT_EQ(SK_ColorBLUE, icon_bitmaps[20].getColor(0, 0));
          run_loop.Quit();
        }));

    run_loop.Run();
  }
}

// Simple struct doesn't have an operator==.
bool operator==(const IconSizeAndPurpose& a, const IconSizeAndPurpose& b) {
  return a.size_px == b.size_px && a.purpose == b.purpose;
}

TEST_F(WebAppIconManagerTest, FindSmallest) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{10, 60, 50, 20, 30};
  const std::vector<SkColor> colors{SK_ColorRED, SK_ColorYELLOW, SK_ColorGREEN,
                                    SK_ColorBLUE, SK_ColorMAGENTA};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors},
                                  {IconPurpose::MASKABLE, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
  // Pretend we only have one size of maskable icon.
  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, {20});

  AddAppToRegistry(std::move(web_app));

  EXPECT_FALSE(icon_manager().HasSmallestIcon(app_id, {IconPurpose::ANY}, 70));
  EXPECT_EQ(std::nullopt,
            icon_manager().FindIconMatchBigger(app_id, {IconPurpose::ANY}, 70));

  EXPECT_FALSE(icon_manager().HasSmallestIcon(
      app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, 70));
  EXPECT_EQ(std::nullopt,
            icon_manager().FindIconMatchBigger(
                app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, 70));

  EXPECT_FALSE(
      icon_manager().HasSmallestIcon(app_id, {IconPurpose::MASKABLE}, 40));
  EXPECT_EQ(std::nullopt, icon_manager().FindIconMatchBigger(
                              app_id, {IconPurpose::MASKABLE}, 40));

  EXPECT_TRUE(icon_manager().HasSmallestIcon(
      app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, 40));
  EXPECT_EQ((IconSizeAndPurpose{50, IconPurpose::ANY}),
            icon_manager()
                .FindIconMatchBigger(
                    app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, 40)
                .value());

  EXPECT_TRUE(icon_manager().HasSmallestIcon(
      app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, 20));
  EXPECT_EQ((IconSizeAndPurpose{20, IconPurpose::ANY}),
            icon_manager()
                .FindIconMatchBigger(
                    app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, 20)
                .value());

  EXPECT_TRUE(icon_manager().HasSmallestIcon(
      app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, 10));
  EXPECT_EQ((IconSizeAndPurpose{20, IconPurpose::MASKABLE}),
            icon_manager()
                .FindIconMatchBigger(
                    app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, 10)
                .value());

  {
    EXPECT_TRUE(icon_manager().HasSmallestIcon(app_id, {IconPurpose::ANY}, 40));
    PurposeAndBitmap result = ReadSmallestIcon(app_id, {IconPurpose::ANY}, 40);
    EXPECT_FALSE(result.bitmap.empty());
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_EQ(SK_ColorGREEN, result.bitmap.getColor(0, 0));
  }
  {
    EXPECT_TRUE(icon_manager().HasSmallestIcon(app_id, {IconPurpose::ANY}, 20));
    PurposeAndBitmap result = ReadSmallestIcon(app_id, {IconPurpose::ANY}, 20);
    EXPECT_FALSE(result.bitmap.empty());
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_EQ(SK_ColorBLUE, result.bitmap.getColor(0, 0));
  }
  {
    PurposeAndBitmap result =
        ReadSmallestIcon(app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, 20);
    EXPECT_FALSE(result.bitmap.empty());
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_EQ(SK_ColorBLUE, result.bitmap.getColor(0, 0));
  }
  {
    PurposeAndBitmap result =
        ReadSmallestIcon(app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, 20);
    EXPECT_FALSE(result.bitmap.empty());
    EXPECT_EQ(IconPurpose::MASKABLE, result.purpose);
    EXPECT_EQ(SK_ColorBLUE, result.bitmap.getColor(0, 0));
  }
}

TEST_F(WebAppIconManagerTest, DeleteData_Success) {
  const webapps::AppId app1_id = GenerateAppId(
      /*manifest_id_path=*/std::nullopt, GURL("https://example.com/"));
  const webapps::AppId app2_id = GenerateAppId(
      /*manifest_id_path=*/std::nullopt, GURL("https://example.org/"));

  const std::vector<int> sizes_px{icon_size::k128};
  const std::vector<SkColor> colors{SK_ColorMAGENTA};
  IconManagerWriteGeneratedIcons(icon_manager(), app1_id,
                                 {{IconPurpose::ANY, sizes_px, colors},
                                  {IconPurpose::MASKABLE, sizes_px, colors}});
  IconManagerWriteGeneratedIcons(icon_manager(), app2_id,
                                 {{IconPurpose::ANY, sizes_px, colors},
                                  {IconPurpose::MASKABLE, sizes_px, colors}});

  const base::FilePath web_apps_root_directory =
      GetWebAppsRootDirectory(profile());
  const base::FilePath app1_dir =
      GetManifestResourcesDirectoryForApp(web_apps_root_directory, app1_id);
  const base::FilePath app2_dir =
      GetManifestResourcesDirectoryForApp(web_apps_root_directory, app2_id);

  EXPECT_TRUE(file_utils().DirectoryExists(app1_dir));
  EXPECT_FALSE(file_utils().IsDirectoryEmpty(app1_dir));

  EXPECT_TRUE(file_utils().DirectoryExists(app2_dir));
  EXPECT_FALSE(file_utils().IsDirectoryEmpty(app2_dir));

  base::RunLoop run_loop;
  icon_manager().DeleteData(app2_id,
                            base::BindLambdaForTesting([&](bool success) {
                              EXPECT_TRUE(success);
                              run_loop.Quit();
                            }));
  run_loop.Run();

  base::FilePath manifest_resources_directory =
      GetManifestResourcesDirectory(web_apps_root_directory);
  EXPECT_TRUE(file_utils().DirectoryExists(manifest_resources_directory));

  EXPECT_TRUE(file_utils().DirectoryExists(app1_dir));
  EXPECT_FALSE(file_utils().IsDirectoryEmpty(app1_dir));

  EXPECT_FALSE(file_utils().DirectoryExists(app2_dir));
}

TEST_F(WebAppIconManagerTest, DeleteData_Failure) {
  const webapps::AppId app_id =
      GenerateAppId(/*manifest_id=*/std::nullopt, GURL("https://example.com/"));

  file_utils().SetNextDeleteFileRecursivelyResult(false);

  base::RunLoop run_loop;
  icon_manager().DeleteData(app_id,
                            base::BindLambdaForTesting([&](bool success) {
                              EXPECT_FALSE(success);
                              run_loop.Quit();
                            }));
  run_loop.Run();
}

TEST_F(WebAppIconManagerTest, ReadSmallestCompressedIcon_Success_AnyOnly) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k128};
  const std::vector<SkColor> colors{SK_ColorGREEN};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AddAppToRegistry(std::move(web_app));

  {
    PurposeAndData result =
        ReadSmallestCompressedIcon(app_id, {IconPurpose::ANY}, sizes_px[0]);
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
  {
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, sizes_px[0]);
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
  {
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, sizes_px[0]);
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
}

TEST_F(WebAppIconManagerTest, ReadSmallestCompressedIcon_Success) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k64, icon_size::k128};
  const std::vector<SkColor> colors{SK_ColorGREEN, SK_ColorGREEN};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors},
                                  {IconPurpose::MASKABLE, sizes_px, colors}});

  int size_smaller = icon_size::k64;
  int size_larger = icon_size::k128;
  // Lie about available icon sizes so any/maskable have different sizes.
  web_app->SetDownloadedIconSizes(IconPurpose::ANY, {size_smaller});
  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, {size_larger});

  AddAppToRegistry(std::move(web_app));

  {
    PurposeAndData result =
        ReadSmallestCompressedIcon(app_id, {IconPurpose::ANY}, size_smaller);
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_FALSE(result.data.empty());

    auto* data_ptr = reinterpret_cast<const char*>(result.data.data());

    // Check that |compressed_data| starts with the 8-byte PNG magic string.
    std::string png_magic_string{data_ptr, 8};
    EXPECT_EQ(png_magic_string, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a");
  }

  {
    // Maskable returned when purpose specified.
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::MASKABLE}, size_larger);
    EXPECT_EQ(IconPurpose::MASKABLE, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
  {
    // Maskable returned even though size doesn't exactly match.
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::MASKABLE}, size_smaller);
    EXPECT_EQ(IconPurpose::MASKABLE, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
  {
    // Any returned because it is first in purposes.
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, size_smaller);
    EXPECT_EQ(IconPurpose::ANY, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
  {
    // Maskable returned because it is the only one of sufficient size.
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, size_larger);
    EXPECT_EQ(IconPurpose::MASKABLE, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
  {
    // Maskable returned because it is first in purposes.
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, size_smaller);
    EXPECT_EQ(IconPurpose::MASKABLE, result.purpose);
    EXPECT_FALSE(result.data.empty());
  }
}

TEST_F(WebAppIconManagerTest, ReadSmallestCompressedIcon_Failure) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k64};
  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, sizes_px);

  AddAppToRegistry(std::move(web_app));

  {
    PurposeAndData result =
        ReadSmallestCompressedIcon(app_id, {IconPurpose::ANY}, sizes_px[0]);
    EXPECT_TRUE(result.data.empty());
  }
  {
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::MASKABLE}, sizes_px[0]);
    EXPECT_TRUE(result.data.empty());
  }
  {
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::ANY, IconPurpose::MASKABLE}, sizes_px[0]);
    EXPECT_TRUE(result.data.empty());
  }
  {
    PurposeAndData result = ReadSmallestCompressedIcon(
        app_id, {IconPurpose::MASKABLE, IconPurpose::ANY}, sizes_px[0]);
    EXPECT_TRUE(result.data.empty());
  }
}

TEST_F(WebAppIconManagerTest, ReadIconAndResize_Success_AnyOnly) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k32, icon_size::k64,
                                  icon_size::k256, icon_size::k512};
  const std::vector<SkColor> colors{SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW,
                                    SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AddAppToRegistry(std::move(web_app));

  for (size_t i = 0; i < sizes_px.size(); ++i) {
    EXPECT_EQ(colors[i], ReadIconAndResize(app_id, sizes_px[i]));
  }

  // ReadIconAndResize should work for non-present icon sizes as long as an icon
  // (with matching IconPurpose) is present. It should prefer shrinking over
  // enlarging.
  EXPECT_EQ(SK_ColorYELLOW, ReadIconAndResize(app_id, icon_size::k128));
  EXPECT_EQ(SK_ColorBLUE, ReadIconAndResize(app_id, icon_size::k16));
  EXPECT_EQ(SK_ColorRED, ReadIconAndResize(app_id, 1024));

  // Maskable icons not found.
  base::RunLoop run_loop;
  icon_manager().ReadIconAndResize(
      app_id, IconPurpose::MASKABLE, icon_size::k128,
      base::BindLambdaForTesting([&](OrderedSizeToBitmap icon_bitmaps) {
        EXPECT_TRUE(icon_bitmaps.empty());
        run_loop.Quit();
      }));

  run_loop.Run();
}

TEST_F(WebAppIconManagerTest, ReadIconAndResize_Success_AnyAndMaskable) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{icon_size::k32, icon_size::k64,
                                  icon_size::k256, icon_size::k512};
  const std::vector<SkColor> colors{SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW,
                                    SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors},
                                  {IconPurpose::MASKABLE, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);
  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, sizes_px);

  AddAppToRegistry(std::move(web_app));

  for (size_t i = 0; i < sizes_px.size(); ++i) {
    EXPECT_EQ(colors[i],
              ReadIconAndResize(app_id, IconPurpose::ANY, sizes_px[i]));
  }
  for (size_t i = 0; i < sizes_px.size(); ++i) {
    EXPECT_EQ(colors[i],
              ReadIconAndResize(app_id, IconPurpose::MASKABLE, sizes_px[i]));
  }

  // ReadIconAndResize should work for non-present icon sizes as long as an icon
  // (with matching IconPurpose) is present. It should prefer shrinking over
  // enlarging.
  EXPECT_EQ(SK_ColorYELLOW,
            ReadIconAndResize(app_id, IconPurpose::ANY, icon_size::k128));
  EXPECT_EQ(SK_ColorBLUE,
            ReadIconAndResize(app_id, IconPurpose::ANY, icon_size::k16));
  EXPECT_EQ(SK_ColorRED, ReadIconAndResize(app_id, IconPurpose::ANY, 1024));
  EXPECT_EQ(SK_ColorYELLOW,
            ReadIconAndResize(app_id, IconPurpose::MASKABLE, icon_size::k128));
  EXPECT_EQ(SK_ColorBLUE,
            ReadIconAndResize(app_id, IconPurpose::MASKABLE, icon_size::k16));
  EXPECT_EQ(SK_ColorRED,
            ReadIconAndResize(app_id, IconPurpose::MASKABLE, 1024));
}

TEST_F(WebAppIconManagerTest, ReadIconAndResize_Failure) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  web_app->SetDownloadedIconSizes(IconPurpose::ANY,
                                  {icon_size::k32, icon_size::k64});
  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE,
                                  {icon_size::k32, icon_size::k64});

  AddAppToRegistry(std::move(web_app));

  {
    base::RunLoop run_loop;
    icon_manager().ReadIconAndResize(
        app_id, IconPurpose::ANY, icon_size::k128,
        base::BindLambdaForTesting([&](OrderedSizeToBitmap icon_bitmaps) {
          EXPECT_TRUE(icon_bitmaps.empty());
          run_loop.Quit();
        }));
    run_loop.Run();
  }
  {
    base::RunLoop run_loop;
    icon_manager().ReadIconAndResize(
        app_id, IconPurpose::MASKABLE, icon_size::k128,
        base::BindLambdaForTesting([&](OrderedSizeToBitmap icon_bitmaps) {
          EXPECT_TRUE(icon_bitmaps.empty());
          run_loop.Quit();
        }));
    run_loop.Run();
  }
}

TEST_F(WebAppIconManagerTest, CacheExistingAppFavicon) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{gfx::kFaviconSize, icon_size::k48};
  const std::vector<SkColor> colors{SK_ColorGREEN, SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  SkBitmap bitmap = icon_manager().GetFavicon(app_id);
  EXPECT_FALSE(bitmap.empty());
  EXPECT_EQ(gfx::kFaviconSize, bitmap.width());
  EXPECT_EQ(gfx::kFaviconSize, bitmap.height());
  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(0, 0));
}

TEST_F(WebAppIconManagerTest, CacheAppFaviconWithResize) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // App does not declare an icon of gfx::kFaviconSize, forcing a resize.
  const std::vector<int> sizes_px{8, icon_size::k48, icon_size::k64};
  ASSERT_FALSE(std::ranges::contains(sizes_px, gfx::kFaviconSize));
  const std::vector<SkColor> colors{SK_ColorBLACK, SK_ColorGREEN, SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  SkBitmap bitmap = icon_manager().GetFavicon(app_id);
  EXPECT_FALSE(bitmap.empty());
  EXPECT_EQ(gfx::kFaviconSize, bitmap.width());
  EXPECT_EQ(gfx::kFaviconSize, bitmap.height());
  // Correct size wasn't available so larger icon should be used.
  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(0, 0));
}

TEST_F(WebAppIconManagerTest, CacheNewAppFavicon) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  const std::vector<int> sizes_px{gfx::kFaviconSize, icon_size::k48};
  const std::vector<SkColor> colors{SK_ColorBLUE, SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  SkBitmap bitmap = icon_manager().GetFavicon(app_id);
  EXPECT_FALSE(bitmap.empty());
  EXPECT_EQ(gfx::kFaviconSize, bitmap.width());
  EXPECT_EQ(gfx::kFaviconSize, bitmap.height());
  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, 0));
}

TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_NoMissingIcons) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // App declares icons precisely matching suspported UI scale factors.
  const std::vector<int> sizes_px{icon_size::k16, icon_size::k32,
                                  icon_size::k48, icon_size::k64};
  ASSERT_TRUE(std::ranges::contains(sizes_px, gfx::kFaviconSize));

  const std::vector<SkColor> colors{SK_ColorYELLOW, SK_ColorGREEN, SK_ColorRED,
                                    SK_ColorBLUE};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
  ASSERT_FALSE(image_skia.isNull());

  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
  {
    SCOPED_TRACE(icon_size::k16);
    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
                       SK_ColorYELLOW);
  }
  {
    SCOPED_TRACE(icon_size::k32);
    ExpectImageSkiaRep(image_skia, /*scale=*/2.0f, /*size_px=*/icon_size::k32,
                       SK_ColorGREEN);
  }
  {
    SCOPED_TRACE(icon_size::k48);
    ExpectImageSkiaRep(image_skia, /*scale=*/3.0f, /*size_px=*/icon_size::k48,
                       SK_ColorRED);
  }
  {
    SCOPED_TRACE(icon_size::k64);
    ExpectImageSkiaRep(image_skia, /*scale=*/4.0f, /*size_px=*/icon_size::k64,
                       SK_ColorBLUE);
  }
}

TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_DownsizingIcons) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // App declares only bigger icons, forcing a downsize to suspported UI scale
  // factors.
  const std::vector<int> sizes_px{icon_size::k24, icon_size::k48};
  ASSERT_FALSE(std::ranges::contains(sizes_px, gfx::kFaviconSize));

  const std::vector<SkColor> colors{SK_ColorCYAN, SK_ColorMAGENTA};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::ANY, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
  ASSERT_FALSE(image_skia.isNull());

  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
  {
    SCOPED_TRACE(icon_size::k16);
    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
                       SK_ColorCYAN);
  }
  {
    SCOPED_TRACE(icon_size::k24);
    ExpectImageSkiaRep(image_skia, /*scale=*/1.5f, /*size_px=*/icon_size::k24,
                       SK_ColorCYAN);
  }
  {
    SCOPED_TRACE(icon_size::k48);
    ExpectImageSkiaRep(image_skia, /*scale=*/3.0f, /*size_px=*/icon_size::k48,
                       SK_ColorMAGENTA);
  }
}

TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_NoIcons) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
  EXPECT_TRUE(image_skia.isNull());
}

TEST_F(WebAppIconManagerTest, CacheAppFavicon_UiScaleFactors_NoMatchSmaller) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // App declares only smaller icon and implementations ignore it: no upsizing.
  const std::vector<int> sizes_px{15};
  IconManagerWriteGeneratedIcons(
      icon_manager(), app_id,
      {{IconPurpose::ANY, sizes_px, /*colors=*/{SK_ColorRED}}});
  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
  EXPECT_TRUE(image_skia.isNull());
}

TEST_F(WebAppIconManagerTest,
       CacheAppFavicon_UiScaleFactors_DownsizingFromSingleIcon) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // App declares only one jumbo icon.
  const std::vector<int> sizes_px{icon_size::k512};
  IconManagerWriteGeneratedIcons(
      icon_manager(), app_id,
      {{IconPurpose::ANY, sizes_px, /*colors=*/{SK_ColorLTGRAY}}});
  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
  ASSERT_FALSE(image_skia.isNull());

  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
  {
    SCOPED_TRACE(icon_size::k16);
    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
                       SK_ColorLTGRAY);
  }
  {
    SCOPED_TRACE(icon_size::k64);
    ExpectImageSkiaRep(image_skia, /*scale=*/4.0f, /*size_px=*/icon_size::k64,
                       SK_ColorLTGRAY);
  }
  EXPECT_FALSE(image_skia.HasRepresentation(2.0f));
  EXPECT_FALSE(image_skia.HasRepresentation(3.0f));
  EXPECT_FALSE(image_skia.HasRepresentation(32.0f));
}

TEST_F(WebAppIconManagerTest,
       CacheAppFavicon_UiScaleFactors_BiggerUiScaleFactorIconMissing) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // App declares the icon which is ok for 100P but small for 300P.
  const std::vector<int> sizes_px{icon_size::k32};
  IconManagerWriteGeneratedIcons(
      icon_manager(), app_id,
      {{IconPurpose::ANY, sizes_px, /*colors=*/{SK_ColorDKGRAY}}});
  web_app->SetDownloadedIconSizes(IconPurpose::ANY, sizes_px);

  AwaitReadFaviconOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia image_skia = icon_manager().GetFaviconImageSkia(app_id);
  ASSERT_FALSE(image_skia.isNull());

  EXPECT_EQ(gfx::kFaviconSize, image_skia.width());
  EXPECT_EQ(gfx::kFaviconSize, image_skia.height());
  {
    SCOPED_TRACE(icon_size::k16);
    ExpectImageSkiaRep(image_skia, /*scale=*/1.0f, /*size_px=*/icon_size::k16,
                       SK_ColorDKGRAY);
  }
  {
    SCOPED_TRACE(icon_size::k32);
    ExpectImageSkiaRep(image_skia, /*scale=*/2.0f, /*size_px=*/icon_size::k32,
                       SK_ColorDKGRAY);
  }
  EXPECT_FALSE(image_skia.HasRepresentation(3.0f));
}

TEST_F(WebAppIconManagerTest, ReadAllIcons_AnyOnly) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  // Write icon bitmaps.
  IconBitmaps icon_bitmaps;
  SkBitmap icon_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorGREEN);
  SkBitmap icon_bitmap2 = CreateSquareIcon(icon_size::k512, SK_ColorYELLOW);
  icon_bitmaps.any[icon_size::k256] = icon_bitmap1;
  icon_bitmaps.any[icon_size::k512] = icon_bitmap2;
  web_app->SetDownloadedIconSizes(IconPurpose::ANY,
                                  {icon_size::k256, icon_size::k512});

  // Also write trusted icons.
  IconBitmaps trusted_bitmaps;
  SkBitmap any_bitmap = CreateSquareIcon(icon_size::k128, SK_ColorBLUE);
  trusted_bitmaps.any[icon_size::k128] = any_bitmap;
  web_app->SetStoredTrustedIconSizes(IconPurpose::ANY, {icon_size::k128});

  AddAppToRegistry(std::move(web_app));

  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {icon_bitmaps}, {trusted_bitmaps}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();

  base::test::TestFuture<WebAppIconManager::WebAppBitmaps> bitmaps_future;
  icon_manager().ReadAllIcons(app_id, bitmaps_future.GetCallback());
  ASSERT_TRUE(bitmaps_future.Wait());

  // manifest_icons check.
  IconBitmaps manifest_icons = bitmaps_future.Get().manifest_icons;
  EXPECT_FALSE(manifest_icons.empty());
  EXPECT_EQ(2u, manifest_icons.any.size());
  EXPECT_THAT(manifest_icons.any[icon_size::k256],
              gfx::test::EqualsBitmap(icon_bitmap1));
  EXPECT_THAT(manifest_icons.any[icon_size::k512],
              gfx::test::EqualsBitmap(icon_bitmap2));
  EXPECT_EQ(0u, manifest_icons.maskable.size());

  // trusted_icons check.
  IconBitmaps trusted_icons = bitmaps_future.Get().trusted_icons;
  EXPECT_FALSE(trusted_icons.empty());
  EXPECT_EQ(1u, trusted_icons.any.size());
  EXPECT_THAT(trusted_icons.any[icon_size::k128],
              gfx::test::EqualsBitmap(any_bitmap));
  EXPECT_EQ(0u, trusted_icons.maskable.size());
}

TEST_F(WebAppIconManagerTest, ReadAllIcons_AnyAndMaskable) {
  auto web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();

  IconBitmaps icon_bitmaps;
  SkBitmap icon_bitmap1 = CreateSquareIcon(icon_size::k256, SK_ColorGREEN);
  SkBitmap icon_bitmap2 = CreateSquareIcon(icon_size::k512, SK_ColorYELLOW);
  SkBitmap icon_bitmap3 = CreateSquareIcon(icon_size::k128, SK_ColorRED);
  icon_bitmaps.any[icon_size::k256] = icon_bitmap1;
  icon_bitmaps.any[icon_size::k512] = icon_bitmap2;
  icon_bitmaps.maskable[icon_size::k128] = icon_bitmap3;
  web_app->SetDownloadedIconSizes(IconPurpose::ANY,
                                  {icon_size::k256, icon_size::k512});
  web_app->SetDownloadedIconSizes(IconPurpose::MASKABLE, {icon_size::k128});

  IconBitmaps trusted_bitmaps;
  SkBitmap expected_bitmap = CreateSquareIcon(icon_size::k128, SK_ColorYELLOW);
  bool prefer_maskable = false;
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
  trusted_bitmaps.maskable[icon_size::k128] = expected_bitmap;
  web_app->SetStoredTrustedIconSizes(IconPurpose::MASKABLE, {icon_size::k128});
  prefer_maskable = true;
#else
  trusted_bitmaps.any[icon_size::k128] = expected_bitmap;
  web_app->SetStoredTrustedIconSizes(IconPurpose::ANY, {icon_size::k128});
  prefer_maskable = false;
#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)

  AddAppToRegistry(std::move(web_app));

  base::RunLoop run_loop;
  icon_manager().WriteData(app_id, {icon_bitmaps}, {trusted_bitmaps}, {}, {},
                           base::BindLambdaForTesting([&](bool success) {
                             EXPECT_TRUE(success);
                             run_loop.Quit();
                           }));
  run_loop.Run();

  base::test::TestFuture<WebAppIconManager::WebAppBitmaps> bitmaps_future;
  icon_manager().ReadAllIcons(app_id, bitmaps_future.GetCallback());
  ASSERT_TRUE(bitmaps_future.Wait());

  // manifest_icons check.
  IconBitmaps manifest_icons = bitmaps_future.Get().manifest_icons;
  EXPECT_FALSE(manifest_icons.empty());
  EXPECT_EQ(2u, manifest_icons.any.size());
  EXPECT_EQ(1u, manifest_icons.maskable.size());
  EXPECT_THAT(manifest_icons.any[icon_size::k256],
              gfx::test::EqualsBitmap(icon_bitmap1));
  EXPECT_THAT(manifest_icons.any[icon_size::k512],
              gfx::test::EqualsBitmap(icon_bitmap2));
  EXPECT_THAT(manifest_icons.maskable[icon_size::k128],
              gfx::test::EqualsBitmap(icon_bitmap3));

  // trusted_icons check, verify either any or maskable icons are used.
  IconBitmaps trusted_icons = bitmaps_future.Get().trusted_icons;
  EXPECT_EQ(!prefer_maskable, trusted_icons.any.size() == 1u);
  EXPECT_EQ(prefer_maskable, trusted_icons.maskable.size() == 1u);
  if (prefer_maskable) {
    EXPECT_THAT(trusted_icons.maskable[icon_size::k128],
                gfx::test::EqualsBitmap(expected_bitmap));
  } else {
    EXPECT_THAT(trusted_icons.any[icon_size::k128],
                gfx::test::EqualsBitmap(expected_bitmap));
  }
}

#if BUILDFLAG(IS_CHROMEOS)
using WebAppIconManagerTest_NotificationIconAndTitle = WebAppIconManagerTest;

// TODO(b/321111988): Reenable this test.
TEST_F(WebAppIconManagerTest_NotificationIconAndTitle,
       DISABLED_CacheAppMonochromeFavicon_NoMissingIcons) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  web_app->SetThemeColor(std::make_optional(SK_ColorBLUE));

  const webapps::AppId app_id = web_app->app_id();

  // App declares icons precisely matching suspported UI scale factors.
  const std::vector<int> sizes_px{icon_size::k16, icon_size::k32,
                                  icon_size::k64};
  ASSERT_TRUE(std::ranges::contains(sizes_px, gfx::kFaviconSize));

  const std::vector<SkColor> colors{SK_ColorYELLOW, SK_ColorTRANSPARENT,
                                    SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::MONOCHROME, sizes_px, colors}});

  web_app->SetDownloadedIconSizes(IconPurpose::MONOCHROME, sizes_px);

  AwaitReadFaviconMonochromeOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia monochrome_image = icon_manager().GetMonochromeFavicon(app_id);
  ASSERT_FALSE(monochrome_image.isNull());

  EXPECT_EQ(gfx::kFaviconSize, monochrome_image.width());
  EXPECT_EQ(gfx::kFaviconSize, monochrome_image.height());
  {
    SCOPED_TRACE(icon_size::k16);
    ExpectImageSkiaRep(monochrome_image, /*scale=*/1.0f,
                       /*size_px=*/icon_size::k16, SK_ColorBLUE);
  }
  {
    SCOPED_TRACE(icon_size::k32);
    ExpectImageSkiaRep(monochrome_image, /*scale=*/2.0f,
                       /*size_px=*/icon_size::k32, SK_ColorTRANSPARENT);
  }
  {
    SCOPED_TRACE(icon_size::k64);
    ExpectImageSkiaRep(monochrome_image, /*scale=*/4.0f,
                       /*size_px=*/icon_size::k64, SK_ColorBLUE);
  }
}

TEST_F(WebAppIconManagerTest_NotificationIconAndTitle,
       CacheAppMonochromeFavicon_CacheAfterAppInstall) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  web_app->SetThemeColor(std::make_optional(SK_ColorGREEN));

  const webapps::AppId app_id = web_app->app_id();

  // App declares only one jumbo icon.
  const std::vector<int> sizes_px{icon_size::k512};
  const std::vector<SkColor> colors{SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::MONOCHROME, sizes_px, colors}});
  web_app->SetDownloadedIconSizes(IconPurpose::MONOCHROME, sizes_px);

  AwaitReadFaviconMonochromeOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia monochrome_image = icon_manager().GetMonochromeFavicon(app_id);
  ASSERT_FALSE(monochrome_image.isNull());

  EXPECT_EQ(gfx::kFaviconSize, monochrome_image.width());
  EXPECT_EQ(gfx::kFaviconSize, monochrome_image.height());
  {
    SCOPED_TRACE(icon_size::k16);
    ExpectImageSkiaRep(monochrome_image, /*scale=*/1.0f,
                       /*size_px=*/icon_size::k16, SK_ColorGREEN);
  }
  {
    SCOPED_TRACE(icon_size::k64);
    ExpectImageSkiaRep(monochrome_image, /*scale=*/4.0f,
                       /*size_px=*/icon_size::k64, SK_ColorGREEN);
  }
  EXPECT_FALSE(monochrome_image.HasRepresentation(2.0f));
  EXPECT_FALSE(monochrome_image.HasRepresentation(3.0f));
  EXPECT_FALSE(monochrome_image.HasRepresentation(32.0f));
}

TEST_F(WebAppIconManagerTest_NotificationIconAndTitle,
       CacheAppMonochromeFavicon_NoThemeColor) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  web_app->SetThemeColor(std::nullopt);

  const webapps::AppId app_id = web_app->app_id();

  // Provides only k200Percent icon.
  const std::vector<int> sizes_px{icon_size::k32};
  const std::vector<SkColor> colors{SK_ColorRED};
  IconManagerWriteGeneratedIcons(icon_manager(), app_id,
                                 {{IconPurpose::MONOCHROME, sizes_px, colors}});
  web_app->SetDownloadedIconSizes(IconPurpose::MONOCHROME, sizes_px);

  AwaitReadFaviconMonochromeOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia monochrome_image = icon_manager().GetMonochromeFavicon(app_id);
  ASSERT_FALSE(monochrome_image.isNull());

  EXPECT_EQ(gfx::kFaviconSize, monochrome_image.width());
  EXPECT_EQ(gfx::kFaviconSize, monochrome_image.height());
  {
    SCOPED_TRACE(icon_size::k16);
    ExpectImageSkiaRep(monochrome_image, /*scale=*/1.0f,
                       /*size_px=*/icon_size::k16, SK_ColorDKGRAY);
  }
  {
    SCOPED_TRACE(icon_size::k32);
    ExpectImageSkiaRep(monochrome_image, /*scale=*/2.0f,
                       /*size_px=*/icon_size::k32, SK_ColorDKGRAY);
  }
  EXPECT_FALSE(monochrome_image.HasRepresentation(3.0));
}

TEST_F(WebAppIconManagerTest_NotificationIconAndTitle,
       CacheAppMonochromeFavicon_NoIcons) {
  std::unique_ptr<WebApp> web_app = test::CreateWebApp();
  const webapps::AppId app_id = web_app->app_id();
  AwaitReadFaviconMonochromeOnAddingWebApp(std::move(web_app));

  gfx::ImageSkia monochrome_image = icon_manager().GetMonochromeFavicon(app_id);
  EXPECT_TRUE(monochrome_image.isNull());
}

#endif  // BUILDFLAG(IS_CHROMEOS)

}  // namespace web_app
