// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gfx/win/icon_util.h"

#include <stddef.h>

#include <memory>
#include <vector>

#include "base/compiler_specific.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_family.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/gfx/switches.h"
#include "ui/gfx/win/icon_util_unittests_resource.h"

namespace {

static const char kSmallIconName[] = "16_X_16_icon.ico";
static const char kLargeIconName[] = "128_X_128_icon.ico";
static const char kTempIconFilename[] = "temp_test_icon.ico";

}  // namespace

class IconUtilTest : public testing::Test {
 public:
  void SetUp() override {
    ASSERT_TRUE(
        base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_data_dir_));
    test_data_dir_ = test_data_dir_.Append(FILE_PATH_LITERAL("ui"))
                         .Append(FILE_PATH_LITERAL("gfx"))
                         .Append(FILE_PATH_LITERAL("test"))
                         .Append(FILE_PATH_LITERAL("data"))
                         .Append(FILE_PATH_LITERAL("icon_util"));
    ASSERT_TRUE(base::PathExists(test_data_dir_));

    ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
  }

  static const int kSmallIconWidth = 16;
  static const int kSmallIconHeight = 16;
  static const int kLargeIconWidth = 128;
  static const int kLargeIconHeight = 128;

  // Given a file name for an .ico file and an image dimensions, this
  // function loads the icon and returns an HICON handle.
  base::win::ScopedGDIObject<HICON>
  LoadIconFromFile(const base::FilePath& filename, int width, int height) {
    HICON icon = static_cast<HICON>(LoadImage(NULL,
                                    filename.value().c_str(),
                                    IMAGE_ICON,
                                    width,
                                    height,
                                    LR_LOADTRANSPARENT | LR_LOADFROMFILE));
    return base::win::ScopedGDIObject<HICON>(icon);
  }

  SkBitmap CreateTransparentBlackSkBitmap(int width, int height) {
    SkBitmap bitmap;
    bitmap.allocN32Pixels(width, height);
    // Setting the pixels to transparent-black.
    UNSAFE_TODO(memset(bitmap.getPixels(), 0, width * height * 4));
    return bitmap;
  }

  SkBitmap CreateNonTransparentSkBitmap(int width, int height) {
    SkBitmap bitmap;
    bitmap.allocN32Pixels(width, height, /*isOpaque=*/true);
    // Setting the pixels to non-transparent white.
    UNSAFE_TODO(memset(bitmap.getPixels(), 0xFF, width * height * 4));
    return bitmap;
  }

  // Loads an .ico file from |icon_filename| and asserts that it contains all of
  // the expected icon sizes up to and including |max_icon_size|, and no other
  // icons. If |max_icon_size| >= 256, this tests for a 256x256 PNG icon entry.
  void CheckAllIconSizes(const base::FilePath& icon_filename,
                         int max_icon_size);

 protected:
  // The root directory for test files. This should be treated as read-only.
  base::FilePath test_data_dir_;

  // Directory for creating files by this test.
  base::ScopedTempDir temp_directory_;
};

class IconUtilTransparentCursorTest : public IconUtilTest,
                                      public testing::WithParamInterface<bool> {
};

void IconUtilTest::CheckAllIconSizes(const base::FilePath& icon_filename,
                                     int max_icon_size) {
  ASSERT_TRUE(base::PathExists(icon_filename));

  // Determine how many icons to expect, based on |max_icon_size|.
  int expected_num_icons = 0;
  for (int dimension : IconUtil::kIconDimensions) {
    if (dimension > max_icon_size) {
      break;
    }
    ++expected_num_icons;
  }

  // First, use the Windows API to load the icon, a basic validity test.
  EXPECT_TRUE(LoadIconFromFile(icon_filename, kSmallIconWidth, kSmallIconHeight)
                  .is_valid());

  // Read the file completely into memory.
  std::string icon_data;
  ASSERT_TRUE(base::ReadFileToString(icon_filename, &icon_data));
  ASSERT_GE(icon_data.length(), sizeof(IconUtil::ICONDIR));

  // Ensure that it has exactly the expected number and sizes of icons, in the
  // expected order. This matches each entry of the loaded file's icon directory
  // with the corresponding element of kIconDimensions.
  // Also extracts the 256x256 entry as png_entry.
  const IconUtil::ICONDIR* icon_dir =
      UNSAFE_TODO(reinterpret_cast<const IconUtil::ICONDIR*>(icon_data.data()));
  EXPECT_EQ(expected_num_icons, icon_dir->idCount);
  ASSERT_GE(IconUtil::kIconDimensions.size(), icon_dir->idCount);
  ASSERT_GE(icon_data.length(),
            sizeof(IconUtil::ICONDIR) +
                icon_dir->idCount * sizeof(IconUtil::ICONDIRENTRY));
  const IconUtil::ICONDIRENTRY* png_entry = NULL;
  for (size_t i = 0; i < icon_dir->idCount; ++i) {
    const IconUtil::ICONDIRENTRY* entry = &UNSAFE_TODO(icon_dir->idEntries[i]);
    // Mod 256 because as a special case in ICONDIRENTRY, the value 0 represents
    // a width or height of 256.
    int expected_size = IconUtil::kIconDimensions[i] % 256;
    EXPECT_EQ(expected_size, static_cast<int>(entry->bWidth));
    EXPECT_EQ(expected_size, static_cast<int>(entry->bHeight));
    if (entry->bWidth == 0 && entry->bHeight == 0) {
      EXPECT_EQ(NULL, png_entry);
      png_entry = entry;
    }
  }

  if (max_icon_size >= 256) {
    ASSERT_TRUE(png_entry);

    // Convert the PNG entry data back to a SkBitmap to ensure it's valid.
    ASSERT_GE(icon_data.length(),
              png_entry->dwImageOffset + png_entry->dwBytesInRes);
    gfx::Image image =
        gfx::Image::CreateFrom1xPNGBytes(base::as_byte_span(icon_data).subspan(
            png_entry->dwImageOffset, png_entry->dwBytesInRes));
    SkBitmap bitmap = image.AsBitmap();
    EXPECT_EQ(256, bitmap.width());
    EXPECT_EQ(256, bitmap.height());
  }
}

// The following test case makes sure IconUtil::SkBitmapFromHICON fails
// gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestIconToBitmapInvalidParameters) {
  base::FilePath icon_filename = test_data_dir_.AppendASCII(kSmallIconName);
  gfx::Size icon_size(kSmallIconWidth, kSmallIconHeight);
  base::win::ScopedGDIObject<HICON> icon(
      LoadIconFromFile(icon_filename, icon_size.width(), icon_size.height()));
  ASSERT_TRUE(icon.is_valid());

  // Invalid size parameter.
  gfx::Size invalid_icon_size(kSmallIconHeight, 0);
  EXPECT_TRUE(IconUtil::CreateSkBitmapFromHICON(icon.get(), invalid_icon_size)
                  .isNull());

  // Invalid icon.
  EXPECT_TRUE(IconUtil::CreateSkBitmapFromHICON(nullptr, icon_size).isNull());

  // The following code should succeed.
  EXPECT_FALSE(
      IconUtil::CreateSkBitmapFromHICON(icon.get(), icon_size).drawsNothing());
}

// The following test case makes sure IconUtil::CreateHICONFromSkBitmap fails
// gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) {
  base::win::ScopedGDIObject<HICON> icon;
  std::unique_ptr<SkBitmap> bitmap;

  // Wrong bitmap format.
  bitmap = std::make_unique<SkBitmap>();
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setInfo(SkImageInfo::MakeA8(kSmallIconWidth, kSmallIconHeight));
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_FALSE(icon.is_valid());

  // Invalid bitmap size.
  bitmap = std::make_unique<SkBitmap>();
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setInfo(SkImageInfo::MakeN32Premul(0, 0));
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_FALSE(icon.is_valid());

  // Valid bitmap configuration but no pixels allocated.
  bitmap = std::make_unique<SkBitmap>();
  ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL));
  bitmap->setInfo(SkImageInfo::MakeN32Premul(kSmallIconWidth,
                                             kSmallIconHeight));
  icon = IconUtil::CreateHICONFromSkBitmap(*bitmap);
  EXPECT_FALSE(icon.is_valid());
}

// The following test case makes sure IconUtil::CreateIconFileFromImageFamily
// fails gracefully when called with invalid input parameters.
TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) {
  gfx::ImageFamily image_family;
  base::FilePath valid_icon_filename =
      temp_directory_.GetPath().AppendASCII(kTempIconFilename);
  base::FilePath invalid_icon_filename =
      temp_directory_.GetPath().AppendASCII("<>?.ico");

  // Invalid file name.
  image_family.clear();
  image_family.Add(gfx::test::CreateImage(/*size=*/1));
  EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                       invalid_icon_filename));
  EXPECT_FALSE(base::PathExists(invalid_icon_filename));
}

// This test case makes sure IconUtil::CreateIconFileFromImageFamily fails if
// the image family is empty or invalid.
TEST_F(IconUtilTest, TestCreateIconFileEmptyImageFamily) {
  base::FilePath icon_filename =
      temp_directory_.GetPath().AppendASCII(kTempIconFilename);

  // Empty image family.
  EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(gfx::ImageFamily(),
                                                       icon_filename));
  EXPECT_FALSE(base::PathExists(icon_filename));

  // Image family with only an empty image.
  gfx::ImageFamily image_family;
  image_family.Add(gfx::Image());
  EXPECT_FALSE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                       icon_filename));
  EXPECT_FALSE(base::PathExists(icon_filename));
}

// This test case makes sure that when we load an icon from disk and convert
// the HICON into a bitmap, the bitmap has the expected format and dimensions.
TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) {
  base::FilePath small_icon_filename =
      test_data_dir_.AppendASCII(kSmallIconName);
  gfx::Size small_icon_size(kSmallIconWidth, kSmallIconHeight);
  base::win::ScopedGDIObject<HICON> small_icon(LoadIconFromFile(
      small_icon_filename, small_icon_size.width(), small_icon_size.height()));
  ASSERT_TRUE(small_icon.is_valid());
  SkBitmap bitmap =
      IconUtil::CreateSkBitmapFromHICON(small_icon.get(), small_icon_size);
  ASSERT_FALSE(bitmap.isNull());
  EXPECT_EQ(bitmap.width(), small_icon_size.width());
  EXPECT_EQ(bitmap.height(), small_icon_size.height());
  EXPECT_EQ(bitmap.colorType(), kN32_SkColorType);

  base::FilePath large_icon_filename =
      test_data_dir_.AppendASCII(kLargeIconName);
  gfx::Size large_icon_size(kLargeIconWidth, kLargeIconHeight);
  base::win::ScopedGDIObject<HICON> large_icon(LoadIconFromFile(
      large_icon_filename, large_icon_size.width(), large_icon_size.height()));
  ASSERT_TRUE(large_icon.is_valid());
  bitmap = IconUtil::CreateSkBitmapFromHICON(large_icon.get(), large_icon_size);
  ASSERT_FALSE(bitmap.isNull());
  EXPECT_EQ(bitmap.width(), large_icon_size.width());
  EXPECT_EQ(bitmap.height(), large_icon_size.height());
  EXPECT_EQ(bitmap.colorType(), kN32_SkColorType);
}

// This test case makes sure that when an HICON is created from an SkBitmap,
// the returned handle is valid and refers to an icon with the expected
// dimensions color depth etc.
TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) {
  SkBitmap bitmap =
      CreateTransparentBlackSkBitmap(kSmallIconWidth, kSmallIconHeight);
  base::win::ScopedGDIObject<HICON> icon(
      IconUtil::CreateHICONFromSkBitmap(bitmap));
  EXPECT_TRUE(icon.is_valid());
  ICONINFO icon_info;
  ASSERT_TRUE(GetIconInfo(icon.get(), &icon_info));
  EXPECT_TRUE(icon_info.fIcon);

  // Now that have the icon information, we should obtain the specification of
  // the icon's bitmap and make sure it matches the specification of the
  // SkBitmap we started with.
  //
  // The bitmap handle contained in the icon information is a handle to a
  // compatible bitmap so we need to call ::GetDIBits() in order to retrieve
  // the bitmap's header information.
  BITMAPINFO bitmap_info;
  UNSAFE_TODO(::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO)));
  bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO);
  HDC hdc = ::GetDC(NULL);
  int result = ::GetDIBits(hdc,
                           icon_info.hbmColor,
                           0,
                           kSmallIconWidth,
                           NULL,
                           &bitmap_info,
                           DIB_RGB_COLORS);
  ASSERT_GT(result, 0);
  EXPECT_EQ(bitmap_info.bmiHeader.biWidth, kSmallIconWidth);
  EXPECT_EQ(bitmap_info.bmiHeader.biHeight, kSmallIconHeight);
  EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1);
  EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32);
  ::ReleaseDC(NULL, hdc);
}

// This test case makes sure that CreateHICONFromSkBitmapSizedTo creates a
// valid HICON with the requested dimensions.
TEST_F(IconUtilTest, TestCreateHICONFromSkBitmapSizedTo) {
  // Create a bitmap larger than the target icon size.
  int src_width = 48;
  int src_height = 48;
  SkBitmap bitmap = CreateTransparentBlackSkBitmap(src_width, src_height);

  // Resize to small icon size.
  int target_width = kSmallIconWidth;
  int target_height = kSmallIconHeight;

  base::win::ScopedGDIObject<HICON> icon(
      IconUtil::CreateHICONFromSkBitmapSizedTo(bitmap, target_width,
                                               target_height));
  EXPECT_TRUE(icon.is_valid());

  // Verify the icon dimensions.
  ICONINFO icon_info;
  ASSERT_TRUE(GetIconInfo(icon.get(), &icon_info));
  EXPECT_TRUE(icon_info.fIcon);

  // The bitmap handle contained in the icon information is a handle to a
  // compatible bitmap so we need to call ::GetDIBits() in order to retrieve
  // the bitmap's header information.
  BITMAPINFO bitmap_info;
  UNSAFE_TODO(::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO)));
  bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO);
  HDC hdc = ::GetDC(NULL);
  int result = ::GetDIBits(hdc, icon_info.hbmColor, 0, target_width, NULL,
                           &bitmap_info, DIB_RGB_COLORS);
  ASSERT_GT(result, 0);
  EXPECT_EQ(bitmap_info.bmiHeader.biWidth, target_width);
  EXPECT_EQ(bitmap_info.bmiHeader.biHeight, target_height);
  EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1);
  EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32);
  ::ReleaseDC(NULL, hdc);
}

// This test case makes sure that CreateIconFileFromImageFamily creates a
// valid .ico file given an ImageFamily, and appropriately creates all icon
// sizes from the given input.
TEST_F(IconUtilTest, TestCreateIconFileFromImageFamily) {
  gfx::ImageFamily image_family;
  base::FilePath icon_filename =
      temp_directory_.GetPath().AppendASCII(kTempIconFilename);

  // Test with only a 16x16 icon. Should only scale up to 48x48.
  image_family.Add(gfx::Image::CreateFrom1xBitmap(
      CreateTransparentBlackSkBitmap(kSmallIconWidth, kSmallIconHeight)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                      icon_filename));
  CheckAllIconSizes(icon_filename, 48);

  // Test with a 48x48 icon. Should only scale down.
  image_family.Add(
      gfx::Image::CreateFrom1xBitmap(CreateTransparentBlackSkBitmap(48, 48)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                      icon_filename));
  CheckAllIconSizes(icon_filename, 48);

  // Test with a 64x64 icon. Should scale up to 256x256.
  image_family.Add(
      gfx::Image::CreateFrom1xBitmap(CreateTransparentBlackSkBitmap(64, 64)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                      icon_filename));
  CheckAllIconSizes(icon_filename, 256);

  // Test with a 256x256 icon. Should include the 256x256 in the output.
  image_family.Add(
      gfx::Image::CreateFrom1xBitmap(CreateTransparentBlackSkBitmap(256, 256)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                      icon_filename));
  CheckAllIconSizes(icon_filename, 256);

  // Test with a 49x49 icon. Should scale up to 256x256, but exclude the
  // original 49x49 representation from the output.
  image_family.clear();
  image_family.Add(
      gfx::Image::CreateFrom1xBitmap(CreateTransparentBlackSkBitmap(49, 49)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                      icon_filename));
  CheckAllIconSizes(icon_filename, 256);

  // Test with a non-square 16x32 icon. Should scale up to 48, but exclude the
  // original 16x32 representation from the output.
  image_family.clear();
  image_family.Add(
      gfx::Image::CreateFrom1xBitmap(CreateTransparentBlackSkBitmap(16, 32)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                      icon_filename));
  CheckAllIconSizes(icon_filename, 48);

  // Test with a non-square 32x49 icon. Should scale up to 256, but exclude the
  // original 32x49 representation from the output.
  image_family.Add(
      gfx::Image::CreateFrom1xBitmap(CreateTransparentBlackSkBitmap(32, 49)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                      icon_filename));
  CheckAllIconSizes(icon_filename, 256);

  // Test with an empty and non-empty image.
  // The empty image should be ignored.
  image_family.clear();
  image_family.Add(gfx::Image());
  image_family.Add(
      gfx::Image::CreateFrom1xBitmap(CreateTransparentBlackSkBitmap(16, 16)));
  ASSERT_TRUE(IconUtil::CreateIconFileFromImageFamily(image_family,
                                                       icon_filename));
  CheckAllIconSizes(icon_filename, 48);
}

TEST_F(IconUtilTest, TestCreateImageFamilyFromIconResource) {
  HMODULE module = GetModuleHandle(NULL);
  std::unique_ptr<gfx::ImageFamily> family(
      IconUtil::CreateImageFamilyFromIconResource(module, IDR_MAINFRAME));
  ASSERT_TRUE(family.get());
  EXPECT_FALSE(family->empty());
  std::vector<gfx::Image> images;
  for (const auto& image : *family)
    images.push_back(image);

  // Assert that the family contains all of the images from the icon resource.
  EXPECT_EQ(5u, images.size());
  EXPECT_EQ(16, images[0].Width());
  EXPECT_EQ(24, images[1].Width());
  EXPECT_EQ(32, images[2].Width());
  EXPECT_EQ(48, images[3].Width());
  EXPECT_EQ(256, images[4].Width());
}

// This tests that kNumIconDimensionsUpToMediumSize has the correct value.
TEST_F(IconUtilTest, TestNumIconDimensionsUpToMediumSize) {
  ASSERT_LE(IconUtil::kNumIconDimensionsUpToMediumSize,
            IconUtil::kIconDimensions.size());
  EXPECT_EQ(
      IconUtil::kMediumIconSize,
      IconUtil::kIconDimensions[IconUtil::kNumIconDimensionsUpToMediumSize -
                                1]);
}

TEST_F(IconUtilTest, TestTransparentIcon) {
  base::FilePath icon_filename =
      temp_directory_.GetPath().AppendASCII(kTempIconFilename);
  int size = 48;
  auto semi_transparent_red = SkColorSetARGB(0x77, 0xFF, 0x00, 0x00);

  // Create a bitmap with a semi transparent red dot.
  SkBitmap bitmap;
  bitmap.allocN32Pixels(size, size, false);
  EXPECT_EQ(bitmap.alphaType(), kPremul_SkAlphaType);
  {
    SkCanvas canvas(bitmap, SkSurfaceProps{});
    canvas.drawColor(SK_ColorWHITE);
    SkPaint paint;
    paint.setColor(semi_transparent_red);
    paint.setBlendMode(SkBlendMode::kSrc);
    canvas.drawPoint(1, 1, paint);
  }

  // Create icon from that bitmap.
  gfx::ImageFamily image_family;
  image_family.Add(gfx::Image::CreateFrom1xBitmap(bitmap));
  ASSERT_TRUE(
      IconUtil::CreateIconFileFromImageFamily(image_family, icon_filename));

  // Load icon and check that dot has same color.
  base::win::ScopedGDIObject<HICON> icon(
      LoadIconFromFile(icon_filename, size, size));
  ASSERT_TRUE(icon.is_valid());
  SkBitmap bitmap_loaded =
      IconUtil::CreateSkBitmapFromHICON(icon.get(), gfx::Size(size, size));
  EXPECT_EQ(bitmap_loaded.getColor(1, 1), semi_transparent_red);
}

// This test ensures that when an custom cursor is created from an SkBitmap
// that is not fully transparent, the returned handle is a valid HICON.
TEST_P(IconUtilTransparentCursorTest,
       TestCreateNonTransparentCursorFromSkBitmap) {
  base::test::ScopedFeatureList feature_list_;
  if (GetParam()) {
    feature_list_.InitAndEnableFeature(features::kTransparentIconWorkaround);
  } else {
    feature_list_.InitAndDisableFeature(features::kTransparentIconWorkaround);
  }
  SkBitmap bitmap =
      CreateNonTransparentSkBitmap(kSmallIconWidth, kSmallIconHeight);
  base::win::ScopedGDIObject<HICON> icon(
      IconUtil::CreateCursorFromSkBitmap(bitmap, gfx::Point()));
  // The feature being enabled shouldn't affect non-transparent cursors.
  EXPECT_TRUE(icon.is_valid());
}

// This test ensures that when an custom cursor is created from a fully
// transparent SkBitmap, the returned handle is a null shape. This is a
// workaround to an issue in Windows where fully transparent cursors are
// rendered as black squares. Instead of creating a fully transparent
// cursor. See https://crbug.com/441293180
TEST_P(IconUtilTransparentCursorTest, TestCreateTransparentCursorFromSkBitmap) {
  base::test::ScopedFeatureList feature_list_;
  if (GetParam()) {
    feature_list_.InitAndEnableFeature(features::kTransparentIconWorkaround);
  } else {
    feature_list_.InitAndDisableFeature(features::kTransparentIconWorkaround);
  }
  SkBitmap bitmap =
      CreateTransparentBlackSkBitmap(kSmallIconWidth, kSmallIconHeight);
  base::win::ScopedGDIObject<HICON> icon(
      IconUtil::CreateCursorFromSkBitmap(bitmap, gfx::Point()));
  // If the feature is enabled, the icon will have a nullptr mask and bitmap and
  // will be invalid.
  if (!GetParam()) {
    EXPECT_TRUE(icon.is_valid());
  } else {
    EXPECT_FALSE(icon.is_valid());
  }
}

INSTANTIATE_TEST_SUITE_P(All, IconUtilTransparentCursorTest, ::testing::Bool());
