blob: 02865c0d43bb0f055167953001fe78d3b101ff75 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/thumbnail/cc/jpeg_thumbnail_helper.h"
#include <cstring>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
#include "chrome/browser/thumbnail/cc/thumbnail.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "third_party/skia/include/effects/SkGradientShader.h"
#include "ui/gfx/codec/jpeg_codec.h"
namespace thumbnail {
namespace {
constexpr int kDimension = 16;
constexpr int kKiB = 1024;
SkPaint SetupPaint() {
SkColor colors[] = {SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE};
SkScalar pos[] = {0, SK_Scalar1 / 2, SK_Scalar1};
SkPaint paint;
paint.setShader(SkGradientShader::MakeSweep(256, 256, colors, pos, 3));
return paint;
}
} // anonymous namespace
class JpegThumbnailHelperTest : public ::testing::Test {
protected:
JpegThumbnailHelperTest()
: task_environment_(
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {}
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
interface_ = std::make_unique<thumbnail::JpegThumbnailHelper>(
temp_dir_.GetPath(),
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}));
}
void TearDown() override {}
thumbnail::JpegThumbnailHelper& GetInterface() { return *interface_; }
base::FilePath GetFile(int tab_id) {
return interface_->GetJpegFilePath(tab_id);
}
base::test::TaskEnvironment task_environment_;
private:
std::unique_ptr<thumbnail::JpegThumbnailHelper> interface_;
base::ScopedTempDir temp_dir_;
};
TEST_F(JpegThumbnailHelperTest, CompressThumbnail) {
// Create a bitmap
SkBitmap image;
ASSERT_TRUE(image.tryAllocN32Pixels(kDimension * kKiB, kDimension));
SkCanvas canvas(image);
canvas.drawPaint(SetupPaint());
image.setImmutable();
// Compress the bitmap
base::RunLoop loop1;
base::OnceCallback<void(std::vector<uint8_t>)> once =
base::BindOnce([](std::vector<uint8_t> jpeg_data) {
EXPECT_FALSE(jpeg_data.empty());
SkBitmap bitmap = gfx::JPEGCodec::Decode(jpeg_data);
EXPECT_FALSE(bitmap.isNull());
EXPECT_GT(bitmap.width(), 0);
EXPECT_GT(bitmap.height(), 0);
}).Then(loop1.QuitClosure());
GetInterface().Compress(image, std::move(once));
task_environment_.RunUntilIdle();
loop1.Run();
}
TEST_F(JpegThumbnailHelperTest, WriteThumbnail) {
int tab_id = 0;
// Create a bitmap.
SkBitmap image;
ASSERT_TRUE(image.tryAllocN32Pixels(kDimension * kKiB, kDimension));
SkCanvas canvas(image);
canvas.drawPaint(SetupPaint());
image.setImmutable();
constexpr int kCompressionQuality = 97;
std::optional<std::vector<uint8_t>> data =
gfx::JPEGCodec::Encode(image, kCompressionQuality);
// Write the image.
base::RunLoop loop1;
GetInterface().Write(tab_id, data.value(),
base::BindOnce(
[](base::OnceClosure quit, bool success) {
EXPECT_TRUE(success);
std::move(quit).Run();
},
loop1.QuitClosure()));
task_environment_.RunUntilIdle();
loop1.Run();
base::FilePath file_path = GetFile(tab_id);
EXPECT_TRUE(base::PathExists(file_path));
// Compare original data with written data.
std::optional<std::vector<uint8_t>> read_data =
base::ReadFileToBytes(file_path);
EXPECT_EQ(data, read_data);
}
TEST_F(JpegThumbnailHelperTest, ReadThumbnail) {
int tab_id = 0;
// Create a bitmap
SkBitmap image;
ASSERT_TRUE(image.tryAllocN32Pixels(kDimension * kKiB, kDimension));
SkCanvas canvas(image);
canvas.drawPaint(SetupPaint());
image.setImmutable();
constexpr int kCompressionQuality = 97;
std::optional<std::vector<uint8_t>> data =
gfx::JPEGCodec::Encode(image, kCompressionQuality);
// Write the image
base::FilePath file_path = GetFile(tab_id);
base::File file(file_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
file.Write(0, reinterpret_cast<const char*>(data.value().data()),
data.value().size());
// Read the image
base::RunLoop loop1;
base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> once =
base::BindOnce([](std::optional<std::vector<uint8_t>> compressed_data) {
ASSERT_TRUE(compressed_data.has_value());
EXPECT_FALSE(compressed_data->empty());
SkBitmap bitmap = gfx::JPEGCodec::Decode(compressed_data.value());
EXPECT_FALSE(bitmap.isNull());
EXPECT_GT(bitmap.width(), 0);
EXPECT_GT(bitmap.height(), 0);
}).Then(loop1.QuitClosure());
GetInterface().Read(tab_id, std::move(once));
task_environment_.RunUntilIdle();
loop1.Run();
}
TEST_F(JpegThumbnailHelperTest, DeleteThumbnail) {
int tab_id = 0;
// Create a bitmap
SkBitmap image;
ASSERT_TRUE(image.tryAllocN32Pixels(kDimension * kKiB, kDimension));
SkCanvas canvas(image);
canvas.drawPaint(SetupPaint());
image.setImmutable();
constexpr int kCompressionQuality = 97;
std::optional<std::vector<uint8_t>> data =
gfx::JPEGCodec::Encode(image, kCompressionQuality);
// Write the image
base::FilePath file_path = GetFile(tab_id);
base::File file(file_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
file.Write(0, reinterpret_cast<const char*>(data->data()), data->size());
// Delete the image
GetInterface().Delete(tab_id);
task_environment_.RunUntilIdle();
// Check deletion occurred
base::FilePath post_delete_file_path = GetFile(tab_id);
EXPECT_FALSE(base::PathExists(post_delete_file_path));
}
} // namespace thumbnail