diff options
-rw-r--r-- | components/components_tests.gyp | 1 | ||||
-rw-r--r-- | components/favicon/core/BUILD.gn | 18 | ||||
-rw-r--r-- | components/favicon/core/favicon_service.h | 2 | ||||
-rw-r--r-- | components/favicon/core/large_icon_service.cc | 3 | ||||
-rw-r--r-- | components/favicon/core/large_icon_service.h | 3 | ||||
-rw-r--r-- | components/favicon/core/large_icon_service_unittest.cc | 281 | ||||
-rw-r--r-- | components/favicon_base/fallback_icon_style.cc | 7 | ||||
-rw-r--r-- | components/favicon_base/fallback_icon_style.h | 2 |
8 files changed, 316 insertions, 1 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 2266483..5178329 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -165,6 +165,7 @@ 'favicon_unittest_sources': [ 'favicon/content/content_favicon_driver_unittest.cc', 'favicon/core/favicon_handler_unittest.cc', + 'favicon/core/large_icon_service_unittest.cc', ], 'undo_unittest_sources': [ 'undo/bookmark_undo_service_test.cc', diff --git a/components/favicon/core/BUILD.gn b/components/favicon/core/BUILD.gn index f4e8a15e..d411522 100644 --- a/components/favicon/core/BUILD.gn +++ b/components/favicon/core/BUILD.gn @@ -35,3 +35,21 @@ static_library("core") { "//url", ] } + +source_set("unit_tests") { + testonly = true + sources = [ + "favicon_handler_unittest.cc", + "large_icon_service_unittest.cc", + ] + + deps = [ + "//base", + "//components/favicon_base", + "//skia", + "//testing/gtest", + "//ui/base", + "//ui/gfx", + "//url", + ] +} diff --git a/components/favicon/core/favicon_service.h b/components/favicon/core/favicon_service.h index e237967..f8ebea4 100644 --- a/components/favicon/core/favicon_service.h +++ b/components/favicon/core/favicon_service.h @@ -117,7 +117,7 @@ class FaviconService : public KeyedService { base::CancelableTaskTracker* tracker); // See HistoryService::GetLargestFaviconForPageURL(). - base::CancelableTaskTracker::TaskId GetLargestRawFaviconForPageURL( + virtual base::CancelableTaskTracker::TaskId GetLargestRawFaviconForPageURL( const GURL& page_url, const std::vector<int>& icon_types, int minimum_size_in_pixels, diff --git a/components/favicon/core/large_icon_service.cc b/components/favicon/core/large_icon_service.cc index 92ea4e0..ed1a17f 100644 --- a/components/favicon/core/large_icon_service.cc +++ b/components/favicon/core/large_icon_service.cc @@ -30,6 +30,9 @@ base::CancelableTaskTracker::TaskId int desired_size_in_pixel, const favicon_base::LargeIconCallback& callback, base::CancelableTaskTracker* tracker) { + DCHECK_LE(1, min_source_size_in_pixel); + DCHECK_LE(0, desired_size_in_pixel); + // TODO(beaudoin): For now this is just a wrapper around // GetLargestRawFaviconForPageURL. Add the logic required to select the best // possible large icon. Also add logic to fetch-on-demand when the URL of diff --git a/components/favicon/core/large_icon_service.h b/components/favicon/core/large_icon_service.h index 0bb3998..a214e20 100644 --- a/components/favicon/core/large_icon_service.h +++ b/components/favicon/core/large_icon_service.h @@ -48,6 +48,9 @@ class LargeIconService : public KeyedService { base::CancelableTaskTracker* tracker); private: + // For testing. + friend class TestLargeIconService; + // Resizes |bitmap_result| to |desired_size_in_pixel|x|desired_size_in_pixel|. // Stores the resized bitmap data in |resized_bitmap_result| and returns true // if successful. diff --git a/components/favicon/core/large_icon_service_unittest.cc b/components/favicon/core/large_icon_service_unittest.cc new file mode 100644 index 0000000..48422ab --- /dev/null +++ b/components/favicon/core/large_icon_service_unittest.cc @@ -0,0 +1,281 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/favicon/core/large_icon_service.h" + +#include <deque> + +#include "base/bind.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/task/cancelable_task_tracker.h" +#include "components/favicon/core/favicon_service.h" +#include "components/favicon_base/fallback_icon_style.h" +#include "components/favicon_base/favicon_types.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 "url/gurl.h" + +namespace favicon { +namespace { + +const char kDummyUrl[] = "http://www.example.com"; +const char kDummyIconUrl[] = "http://www.example.com/touch_icon.png"; + +const SkColor kTestColor = SK_ColorRED; + +favicon_base::FaviconRawBitmapResult CreateTestBitmap( + int w, int h, SkColor color) { + favicon_base::FaviconRawBitmapResult result; + result.expired = false; + + // Create bitmap and fill with |color|. + scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); + SkBitmap bitmap; + bitmap.allocN32Pixels(w, h); + bitmap.eraseColor(color); + gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &data->data()); + result.bitmap_data = data; + + result.pixel_size = gfx::Size(w, h); + result.icon_url = GURL(kDummyIconUrl); + result.icon_type = favicon_base::TOUCH_ICON; + CHECK(result.is_valid()); + return result; +} + +// A mock FaviconService that emits pre-programmed response. +class MockFaviconService : public FaviconService { + public: + MockFaviconService() : FaviconService(nullptr, nullptr) { + } + + ~MockFaviconService() override { + } + + base::CancelableTaskTracker::TaskId GetLargestRawFaviconForPageURL( + const GURL& page_url, + const std::vector<int>& icon_types, + int minimum_size_in_pixels, + const favicon_base::FaviconRawBitmapCallback& callback, + base::CancelableTaskTracker* tracker) override { + favicon_base::FaviconRawBitmapResult mock_result = + mock_result_queue_.front(); + mock_result_queue_.pop_front(); + return tracker->PostTask( + base::MessageLoop::current()->task_runner().get(), FROM_HERE, + base::Bind(callback, mock_result)); + } + + void InjectResult(const favicon_base::FaviconRawBitmapResult& mock_result) { + mock_result_queue_.push_back(mock_result); + } + + bool HasUnusedResults() { + return !mock_result_queue_.empty(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MockFaviconService); + + std::deque<favicon_base::FaviconRawBitmapResult> mock_result_queue_; +}; + +// This class provides access to LargeIconService internals. +class TestLargeIconService : public LargeIconService { + public: + explicit TestLargeIconService(MockFaviconService* mock_favicon_service) + : LargeIconService(mock_favicon_service) { + } + ~TestLargeIconService() override { + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestLargeIconService); +}; + +class LargeIconServiceTest : public testing::Test { + public: + LargeIconServiceTest() : is_callback_invoked_(false) { + } + + ~LargeIconServiceTest() override { + } + + void SetUp() override { + testing::Test::SetUp(); + mock_favicon_service_.reset(new MockFaviconService()); + large_icon_service_.reset( + new TestLargeIconService(mock_favicon_service_.get())); + } + + void ResultCallback(const favicon_base::LargeIconResult& result) { + is_callback_invoked_ = true; + + // Checking presence and absence of results. + EXPECT_EQ(expected_bitmap_.is_valid(), result.bitmap.is_valid()); + EXPECT_EQ(expected_fallback_icon_style_ != nullptr, + result.fallback_icon_style != nullptr); + + if (expected_bitmap_.is_valid()) { + EXPECT_EQ(expected_bitmap_.pixel_size, result.bitmap.pixel_size); + // Not actually checking bitmap content. + } + if (expected_fallback_icon_style_.get()) { + EXPECT_EQ(*expected_fallback_icon_style_, + *result.fallback_icon_style); + } + // Ensure all mock results have been consumed. + EXPECT_FALSE(mock_favicon_service_->HasUnusedResults()); + } + + protected: + base::MessageLoopForIO loop_; + + scoped_ptr<MockFaviconService> mock_favicon_service_; + scoped_ptr<TestLargeIconService> large_icon_service_; + base::CancelableTaskTracker cancelable_task_tracker_; + + favicon_base::FaviconRawBitmapResult expected_bitmap_; + scoped_ptr<favicon_base::FallbackIconStyle> expected_fallback_icon_style_; + + bool is_callback_invoked_; + + private: + DISALLOW_COPY_AND_ASSIGN(LargeIconServiceTest); +}; + +TEST_F(LargeIconServiceTest, SameSize) { + + mock_favicon_service_->InjectResult(CreateTestBitmap(24, 24, kTestColor)); + expected_bitmap_ = CreateTestBitmap(24, 24, kTestColor); + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 24, // |min_source_size_in_pixel| + 24, // |desired_size_in_pixel| + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +TEST_F(LargeIconServiceTest, ScaleDown) { + mock_favicon_service_->InjectResult(CreateTestBitmap(32, 32, kTestColor)); + expected_bitmap_ = CreateTestBitmap(24, 24, kTestColor); + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 24, + 24, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +TEST_F(LargeIconServiceTest, ScaleUp) { + mock_favicon_service_->InjectResult(CreateTestBitmap(16, 16, kTestColor)); + expected_bitmap_ = CreateTestBitmap(24, 24, kTestColor); + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 14, // Lowered requirement so stored bitmap is admitted. + 24, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +// |desired_size_in_pixel| == 0 means retrieve original image without scaling. +TEST_F(LargeIconServiceTest, NoScale) { + mock_favicon_service_->InjectResult(CreateTestBitmap(24, 24, kTestColor)); + expected_bitmap_ = CreateTestBitmap(24, 24, kTestColor); + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 16, + 0, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +TEST_F(LargeIconServiceTest, FallbackSinceIconTooSmall) { + mock_favicon_service_->InjectResult(CreateTestBitmap(16, 16, kTestColor)); + expected_fallback_icon_style_.reset(new favicon_base::FallbackIconStyle); + expected_fallback_icon_style_->background_color = kTestColor; + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 24, + 24, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +TEST_F(LargeIconServiceTest, FallbackSinceIconNotSquare) { + mock_favicon_service_->InjectResult(CreateTestBitmap(24, 32, kTestColor)); + expected_fallback_icon_style_.reset(new favicon_base::FallbackIconStyle); + expected_fallback_icon_style_->background_color = kTestColor; + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 24, + 24, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +TEST_F(LargeIconServiceTest, FallbackSinceIconMissing) { + mock_favicon_service_->InjectResult(favicon_base::FaviconRawBitmapResult()); + // Expect default fallback style, including background. + expected_fallback_icon_style_.reset(new favicon_base::FallbackIconStyle); + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 24, + 24, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +TEST_F(LargeIconServiceTest, FallbackSinceIconMissingNoScale) { + mock_favicon_service_->InjectResult(favicon_base::FaviconRawBitmapResult()); + // Expect default fallback style, including background. + expected_fallback_icon_style_.reset(new favicon_base::FallbackIconStyle); + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 24, + 0, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +// Oddball case where we demand a high resolution icon to scale down. Generates +// fallback even though an icon with the final size is available. +TEST_F(LargeIconServiceTest, FallbackSinceTooPicky) { + mock_favicon_service_->InjectResult(CreateTestBitmap(24, 24, kTestColor)); + expected_fallback_icon_style_.reset(new favicon_base::FallbackIconStyle); + expected_fallback_icon_style_->background_color = kTestColor; + large_icon_service_->GetLargeIconOrFallbackStyle( + GURL(kDummyUrl), + 32, + 24, + base::Bind(&LargeIconServiceTest::ResultCallback, base::Unretained(this)), + &cancelable_task_tracker_); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(is_callback_invoked_); +} + +} // namespace +} // namespace favicon diff --git a/components/favicon_base/fallback_icon_style.cc b/components/favicon_base/fallback_icon_style.cc index d3e395b..a3aa658 100644 --- a/components/favicon_base/fallback_icon_style.cc +++ b/components/favicon_base/fallback_icon_style.cc @@ -41,6 +41,13 @@ FallbackIconStyle::FallbackIconStyle() FallbackIconStyle::~FallbackIconStyle() { } +bool FallbackIconStyle::operator==(const FallbackIconStyle& other) const { + return background_color == other.background_color && + text_color == other.text_color && + font_size_ratio == other.font_size_ratio && + roundness == other.roundness; +} + void MatchFallbackIconTextColorAgainstBackgroundColor( FallbackIconStyle* style) { int luminance = color_utils::GetLuminanceForColor(style->background_color); diff --git a/components/favicon_base/fallback_icon_style.h b/components/favicon_base/fallback_icon_style.h index 84842ed..478efb8 100644 --- a/components/favicon_base/fallback_icon_style.h +++ b/components/favicon_base/fallback_icon_style.h @@ -30,6 +30,8 @@ struct FallbackIconStyle { // The roundness of the icon's corners. 0 => square icon, 1 => circle icon. double roundness; + + bool operator==(const FallbackIconStyle& other) const; }; // Reassigns |style|'s |text_color| to matches well against |background_color|. |