blob: 0f5e315004c0abf1a1e28416f639021cea96b88e [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/shortcuts/icon_badging.h"
#include <iterator>
#include <string>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/numerics/clamped_math.h"
#include "base/path_service.h"
#include "base/strings/to_string.h"
#include "base/test/gmock_expected_support.h"
#include "build/branding_buildflags.h"
#include "build/buildflag.h"
#include "chrome/browser/shortcuts/image_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/codec/png_codec.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_skia.h"
#include "ui/gfx/image/image_unittest_util.h"
#include "ui/gfx/test/sk_gmock_support.h"
namespace shortcuts {
namespace {
constexpr SkColor kSiteColor = SK_ColorRED;
gfx::Image CreateImage(int icon_size, SkColor color) {
return gfx::test::CreateImage(icon_size, color);
}
bool ShouldRebaselineTestImages() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
return command_line.HasSwitch("rebaseline-shortcuts-icon-testing");
}
base::FilePath GetCompileTimeTestFolders() {
base::FilePath compile_time_folder;
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
compile_time_folder = base::FilePath(FILE_PATH_LITERAL("chrome_branded"));
#elif BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
compile_time_folder =
base::FilePath(FILE_PATH_LITERAL("chrome_for_testing_branded"));
#else
compile_time_folder = base::FilePath(FILE_PATH_LITERAL("chromium"));
#endif
return compile_time_folder;
}
base::expected<base::FilePath, std::string> GetPathForShortcutBadgedIcons() {
base::FilePath chrome_src_dir;
if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &chrome_src_dir)) {
return base::unexpected("Could not find src directory.");
}
base::FilePath folder_path =
chrome_src_dir
.Append(FILE_PATH_LITERAL("chrome/test/data/shortcuts/badging_icons"))
.Append(GetCompileTimeTestFolders());
if (!base::PathExists(folder_path)) {
base::CreateDirectory(folder_path);
}
return folder_path;
}
void WriteTestIconsToDiskOrDie(const gfx::ImageFamily& family) {
auto shortcuts_test_path = GetPathForShortcutBadgedIcons();
CHECK(shortcuts_test_path.has_value())
<< " Unable to parse icons folder for testing";
base::FilePath shortcuts_path(shortcuts_test_path.value());
for (const gfx::Image& image : family) {
const SkBitmap image_bitmap = image.AsBitmap();
std::optional<std::vector<uint8_t>> png_output = gfx::PNGCodec::Encode(
reinterpret_cast<const unsigned char*>(image_bitmap.getPixels()),
gfx::PNGCodec::FORMAT_SkBitmap, image.Size(),
static_cast<int>(image_bitmap.rowBytes()),
/*discard_transparency=*/true, std::vector<gfx::PNGCodec::Comment>());
std::string width = base::ToString(image.Width());
base::FilePath out_filename;
#if BUILDFLAG(IS_WIN)
std::wstring width_str(width.begin(), width.end());
out_filename = shortcuts_path.Append(width_str);
#else
out_filename = shortcuts_path.Append(width);
#endif // BUILDFLAG(IS_WIN)
out_filename = out_filename.AddExtension(FILE_PATH_LITERAL(".png"));
const bool success = base::WriteFile(out_filename, png_output.value());
CHECK(success) << base::StrCat({"Failed to write icon of size ", width});
}
}
// This is hardcoded based on the sizes of kSizesNeededForShortcutCreation.
int GetOsSpecificSizes() {
#if BUILDFLAG(IS_MAC)
return 5;
#elif BUILDFLAG(IS_LINUX)
return 2;
#elif BUILDFLAG(IS_WIN)
return 4;
#endif
}
} // namespace
// Verifies that badging logic works as intended by comparing with icons stored
// on disk. The icons are stored in file names corresponding to their sizes, and
// are stored inside `chrome/test/data/shortcuts/badging_icons/chromium` for non
// branded builds and `chrome/test/data/shortcuts/badging_icons/chrome_branded`
// for branded builds.
// If badging behavior changes, the icons in the listed folders would need to be
// updated. That can be done manually, or can be done automatically by using the
// `rebaseline-shortcuts-icon-testing` command line flag. Example usage:
// out/Default/unit_tests --gtest_filter=*IconBadgingTest*
// --rebaseline-shortcuts-icon-testing
TEST(IconBadgingTest, VerifyFromDisk) {
std::vector<SkBitmap> bitmaps;
// The sizes have been chosen randomly for testing values in the extremities.
bitmaps.emplace_back(CreateImage(20, kSiteColor).AsBitmap());
bitmaps.emplace_back(CreateImage(1000, kSiteColor).AsBitmap());
bitmaps.emplace_back(CreateImage(150, kSiteColor).AsBitmap());
gfx::ImageFamily family = shortcuts::ApplyProductLogoBadgeToIcons(bitmaps);
if (ShouldRebaselineTestImages()) {
WriteTestIconsToDiskOrDie(family);
}
EXPECT_EQ(std::distance(family.begin(), family.end()), GetOsSpecificSizes());
for (const gfx::Image& image_icon : family) {
std::string width = base::ToString(image_icon.Width());
SkBitmap expected_bitmap;
base::FilePath icon_path_relative =
base::FilePath(FILE_PATH_LITERAL("shortcuts/badging_icons"))
.Append(GetCompileTimeTestFolders());
#if BUILDFLAG(IS_WIN)
std::wstring width_str(width.begin(), width.end());
icon_path_relative = icon_path_relative.Append(width_str);
#else
icon_path_relative = icon_path_relative.Append(width);
#endif // BUILDFLAG(IS_WIN)
icon_path_relative =
icon_path_relative.AddExtension(FILE_PATH_LITERAL(".png"));
ASSERT_OK_AND_ASSIGN(expected_bitmap,
LoadImageFromTestFile(icon_path_relative));
EXPECT_THAT(expected_bitmap,
gfx::test::EqualsBitmap(image_icon.AsBitmap()));
}
}
} // namespace shortcuts