1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/favicon/core/large_icon_service.h"
10 #include "base/memory/ref_counted_memory.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/task/cancelable_task_tracker.h"
14 #include "components/favicon/core/favicon_client.h"
15 #include "components/favicon/core/favicon_service.h"
16 #include "components/favicon_base/fallback_icon_style.h"
17 #include "components/favicon_base/favicon_types.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "third_party/skia/include/core/SkColor.h"
21 #include "ui/gfx/codec/png_codec.h"
22 #include "ui/gfx/geometry/size.h"
23 #include "ui/gfx/image/image.h"
29 const char kDummyUrl
[] = "http://www.example.com";
30 const char kDummyIconUrl
[] = "http://www.example.com/touch_icon.png";
32 const SkColor kTestColor
= SK_ColorRED
;
34 favicon_base::FaviconRawBitmapResult
CreateTestBitmap(
35 int w
, int h
, SkColor color
) {
36 favicon_base::FaviconRawBitmapResult result
;
37 result
.expired
= false;
39 // Create bitmap and fill with |color|.
40 scoped_refptr
<base::RefCountedBytes
> data(new base::RefCountedBytes());
42 bitmap
.allocN32Pixels(w
, h
);
43 bitmap
.eraseColor(color
);
44 gfx::PNGCodec::EncodeBGRASkBitmap(bitmap
, false, &data
->data());
45 result
.bitmap_data
= data
;
47 result
.pixel_size
= gfx::Size(w
, h
);
48 result
.icon_url
= GURL(kDummyIconUrl
);
49 result
.icon_type
= favicon_base::TOUCH_ICON
;
50 CHECK(result
.is_valid());
54 // A mock FaviconService that emits pre-programmed response.
55 class MockFaviconService
: public FaviconService
{
57 MockFaviconService() : FaviconService(nullptr, nullptr) {
60 ~MockFaviconService() override
{
63 base::CancelableTaskTracker::TaskId
GetLargestRawFaviconForPageURL(
65 const std::vector
<int>& icon_types
,
66 int minimum_size_in_pixels
,
67 const favicon_base::FaviconRawBitmapCallback
& callback
,
68 base::CancelableTaskTracker
* tracker
) override
{
69 favicon_base::FaviconRawBitmapResult mock_result
=
70 mock_result_queue_
.front();
71 mock_result_queue_
.pop_front();
72 return tracker
->PostTask(
73 base::MessageLoop::current()->task_runner().get(), FROM_HERE
,
74 base::Bind(callback
, mock_result
));
77 void InjectResult(const favicon_base::FaviconRawBitmapResult
& mock_result
) {
78 mock_result_queue_
.push_back(mock_result
);
81 bool HasUnusedResults() {
82 return !mock_result_queue_
.empty();
86 std::deque
<favicon_base::FaviconRawBitmapResult
> mock_result_queue_
;
88 DISALLOW_COPY_AND_ASSIGN(MockFaviconService
);
91 // This class provides access to LargeIconService internals.
92 class TestLargeIconService
: public LargeIconService
{
94 explicit TestLargeIconService(MockFaviconService
* mock_favicon_service
)
95 : LargeIconService(mock_favicon_service
) {
97 ~TestLargeIconService() override
{
100 // Using the current thread's task runner for testing.
101 scoped_refptr
<base::TaskRunner
> GetBackgroundTaskRunner() override
{
102 return base::MessageLoop::current()->task_runner();
106 DISALLOW_COPY_AND_ASSIGN(TestLargeIconService
);
109 class LargeIconServiceTest
: public testing::Test
{
111 LargeIconServiceTest() : is_callback_invoked_(false) {
114 ~LargeIconServiceTest() override
{
117 void SetUp() override
{
118 testing::Test::SetUp();
119 mock_favicon_service_
.reset(new MockFaviconService());
120 large_icon_service_
.reset(
121 new TestLargeIconService(mock_favicon_service_
.get()));
124 void ResultCallback(const favicon_base::LargeIconResult
& result
) {
125 is_callback_invoked_
= true;
127 // Checking presence and absence of results.
128 EXPECT_EQ(expected_bitmap_
.is_valid(), result
.bitmap
.is_valid());
129 EXPECT_EQ(expected_fallback_icon_style_
!= nullptr,
130 result
.fallback_icon_style
!= nullptr);
132 if (expected_bitmap_
.is_valid()) {
133 EXPECT_EQ(expected_bitmap_
.pixel_size
, result
.bitmap
.pixel_size
);
134 // Not actually checking bitmap content.
136 if (expected_fallback_icon_style_
.get()) {
137 EXPECT_EQ(*expected_fallback_icon_style_
,
138 *result
.fallback_icon_style
);
140 // Ensure all mock results have been consumed.
141 EXPECT_FALSE(mock_favicon_service_
->HasUnusedResults());
145 base::MessageLoopForIO loop_
;
147 scoped_ptr
<MockFaviconService
> mock_favicon_service_
;
148 scoped_ptr
<TestLargeIconService
> large_icon_service_
;
149 base::CancelableTaskTracker cancelable_task_tracker_
;
151 favicon_base::FaviconRawBitmapResult expected_bitmap_
;
152 scoped_ptr
<favicon_base::FallbackIconStyle
> expected_fallback_icon_style_
;
154 bool is_callback_invoked_
;
157 DISALLOW_COPY_AND_ASSIGN(LargeIconServiceTest
);
160 TEST_F(LargeIconServiceTest
, SameSize
) {
161 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 24, kTestColor
));
162 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
163 large_icon_service_
->GetLargeIconOrFallbackStyle(
165 24, // |min_source_size_in_pixel|
166 24, // |desired_size_in_pixel|
167 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
168 &cancelable_task_tracker_
);
169 base::MessageLoop::current()->RunUntilIdle();
170 EXPECT_TRUE(is_callback_invoked_
);
173 TEST_F(LargeIconServiceTest
, ScaleDown
) {
174 mock_favicon_service_
->InjectResult(CreateTestBitmap(32, 32, kTestColor
));
175 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
176 large_icon_service_
->GetLargeIconOrFallbackStyle(
180 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
181 &cancelable_task_tracker_
);
182 base::MessageLoop::current()->RunUntilIdle();
183 EXPECT_TRUE(is_callback_invoked_
);
186 TEST_F(LargeIconServiceTest
, ScaleUp
) {
187 mock_favicon_service_
->InjectResult(CreateTestBitmap(16, 16, kTestColor
));
188 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
189 large_icon_service_
->GetLargeIconOrFallbackStyle(
191 14, // Lowered requirement so stored bitmap is admitted.
193 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
194 &cancelable_task_tracker_
);
195 base::MessageLoop::current()->RunUntilIdle();
196 EXPECT_TRUE(is_callback_invoked_
);
199 // |desired_size_in_pixel| == 0 means retrieve original image without scaling.
200 TEST_F(LargeIconServiceTest
, NoScale
) {
201 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 24, kTestColor
));
202 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
203 large_icon_service_
->GetLargeIconOrFallbackStyle(
207 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
208 &cancelable_task_tracker_
);
209 base::MessageLoop::current()->RunUntilIdle();
210 EXPECT_TRUE(is_callback_invoked_
);
213 TEST_F(LargeIconServiceTest
, FallbackSinceIconTooSmall
) {
214 mock_favicon_service_
->InjectResult(CreateTestBitmap(16, 16, kTestColor
));
215 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
216 expected_fallback_icon_style_
->background_color
= kTestColor
;
217 large_icon_service_
->GetLargeIconOrFallbackStyle(
221 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
222 &cancelable_task_tracker_
);
223 base::MessageLoop::current()->RunUntilIdle();
224 EXPECT_TRUE(is_callback_invoked_
);
227 TEST_F(LargeIconServiceTest
, FallbackSinceIconNotSquare
) {
228 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 32, kTestColor
));
229 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
230 expected_fallback_icon_style_
->background_color
= kTestColor
;
231 large_icon_service_
->GetLargeIconOrFallbackStyle(
235 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
236 &cancelable_task_tracker_
);
237 base::MessageLoop::current()->RunUntilIdle();
238 EXPECT_TRUE(is_callback_invoked_
);
241 TEST_F(LargeIconServiceTest
, FallbackSinceIconMissing
) {
242 mock_favicon_service_
->InjectResult(favicon_base::FaviconRawBitmapResult());
243 // Expect default fallback style, including background.
244 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
245 large_icon_service_
->GetLargeIconOrFallbackStyle(
249 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
250 &cancelable_task_tracker_
);
251 base::MessageLoop::current()->RunUntilIdle();
252 EXPECT_TRUE(is_callback_invoked_
);
255 TEST_F(LargeIconServiceTest
, FallbackSinceIconMissingNoScale
) {
256 mock_favicon_service_
->InjectResult(favicon_base::FaviconRawBitmapResult());
257 // Expect default fallback style, including background.
258 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
259 large_icon_service_
->GetLargeIconOrFallbackStyle(
263 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
264 &cancelable_task_tracker_
);
265 base::MessageLoop::current()->RunUntilIdle();
266 EXPECT_TRUE(is_callback_invoked_
);
269 // Oddball case where we demand a high resolution icon to scale down. Generates
270 // fallback even though an icon with the final size is available.
271 TEST_F(LargeIconServiceTest
, FallbackSinceTooPicky
) {
272 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 24, kTestColor
));
273 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
274 expected_fallback_icon_style_
->background_color
= kTestColor
;
275 large_icon_service_
->GetLargeIconOrFallbackStyle(
279 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
280 &cancelable_task_tracker_
);
281 base::MessageLoop::current()->RunUntilIdle();
282 EXPECT_TRUE(is_callback_invoked_
);
286 } // namespace favicon