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, using the current
92 // thread's task runner for testing.
93 class TestLargeIconService
: public LargeIconService
{
95 explicit TestLargeIconService(MockFaviconService
* mock_favicon_service
)
96 : LargeIconService(mock_favicon_service
,
97 base::MessageLoop::current()->task_runner()) {}
98 ~TestLargeIconService() override
{
102 DISALLOW_COPY_AND_ASSIGN(TestLargeIconService
);
105 class LargeIconServiceTest
: public testing::Test
{
107 LargeIconServiceTest() : is_callback_invoked_(false) {
110 ~LargeIconServiceTest() override
{
113 void SetUp() override
{
114 testing::Test::SetUp();
115 mock_favicon_service_
.reset(new MockFaviconService());
116 large_icon_service_
.reset(
117 new TestLargeIconService(mock_favicon_service_
.get()));
120 void ResultCallback(const favicon_base::LargeIconResult
& result
) {
121 is_callback_invoked_
= true;
123 // Checking presence and absence of results.
124 EXPECT_EQ(expected_bitmap_
.is_valid(), result
.bitmap
.is_valid());
125 EXPECT_EQ(expected_fallback_icon_style_
!= nullptr,
126 result
.fallback_icon_style
!= nullptr);
128 if (expected_bitmap_
.is_valid()) {
129 EXPECT_EQ(expected_bitmap_
.pixel_size
, result
.bitmap
.pixel_size
);
130 // Not actually checking bitmap content.
132 if (expected_fallback_icon_style_
.get()) {
133 EXPECT_EQ(*expected_fallback_icon_style_
,
134 *result
.fallback_icon_style
);
136 // Ensure all mock results have been consumed.
137 EXPECT_FALSE(mock_favicon_service_
->HasUnusedResults());
141 base::MessageLoopForIO loop_
;
143 scoped_ptr
<MockFaviconService
> mock_favicon_service_
;
144 scoped_ptr
<TestLargeIconService
> large_icon_service_
;
145 base::CancelableTaskTracker cancelable_task_tracker_
;
147 favicon_base::FaviconRawBitmapResult expected_bitmap_
;
148 scoped_ptr
<favicon_base::FallbackIconStyle
> expected_fallback_icon_style_
;
150 bool is_callback_invoked_
;
153 DISALLOW_COPY_AND_ASSIGN(LargeIconServiceTest
);
156 TEST_F(LargeIconServiceTest
, SameSize
) {
157 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 24, kTestColor
));
158 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
159 large_icon_service_
->GetLargeIconOrFallbackStyle(
161 24, // |min_source_size_in_pixel|
162 24, // |desired_size_in_pixel|
163 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
164 &cancelable_task_tracker_
);
165 base::MessageLoop::current()->RunUntilIdle();
166 EXPECT_TRUE(is_callback_invoked_
);
169 TEST_F(LargeIconServiceTest
, ScaleDown
) {
170 mock_favicon_service_
->InjectResult(CreateTestBitmap(32, 32, kTestColor
));
171 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
172 large_icon_service_
->GetLargeIconOrFallbackStyle(
176 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
177 &cancelable_task_tracker_
);
178 base::MessageLoop::current()->RunUntilIdle();
179 EXPECT_TRUE(is_callback_invoked_
);
182 TEST_F(LargeIconServiceTest
, ScaleUp
) {
183 mock_favicon_service_
->InjectResult(CreateTestBitmap(16, 16, kTestColor
));
184 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
185 large_icon_service_
->GetLargeIconOrFallbackStyle(
187 14, // Lowered requirement so stored bitmap is admitted.
189 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
190 &cancelable_task_tracker_
);
191 base::MessageLoop::current()->RunUntilIdle();
192 EXPECT_TRUE(is_callback_invoked_
);
195 // |desired_size_in_pixel| == 0 means retrieve original image without scaling.
196 TEST_F(LargeIconServiceTest
, NoScale
) {
197 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 24, kTestColor
));
198 expected_bitmap_
= CreateTestBitmap(24, 24, kTestColor
);
199 large_icon_service_
->GetLargeIconOrFallbackStyle(
203 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
204 &cancelable_task_tracker_
);
205 base::MessageLoop::current()->RunUntilIdle();
206 EXPECT_TRUE(is_callback_invoked_
);
209 TEST_F(LargeIconServiceTest
, FallbackSinceIconTooSmall
) {
210 mock_favicon_service_
->InjectResult(CreateTestBitmap(16, 16, kTestColor
));
211 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
212 expected_fallback_icon_style_
->background_color
= kTestColor
;
213 large_icon_service_
->GetLargeIconOrFallbackStyle(
217 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
218 &cancelable_task_tracker_
);
219 base::MessageLoop::current()->RunUntilIdle();
220 EXPECT_TRUE(is_callback_invoked_
);
223 TEST_F(LargeIconServiceTest
, FallbackSinceIconNotSquare
) {
224 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 32, kTestColor
));
225 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
226 expected_fallback_icon_style_
->background_color
= kTestColor
;
227 large_icon_service_
->GetLargeIconOrFallbackStyle(
231 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
232 &cancelable_task_tracker_
);
233 base::MessageLoop::current()->RunUntilIdle();
234 EXPECT_TRUE(is_callback_invoked_
);
237 TEST_F(LargeIconServiceTest
, FallbackSinceIconMissing
) {
238 mock_favicon_service_
->InjectResult(favicon_base::FaviconRawBitmapResult());
239 // Expect default fallback style, including background.
240 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
241 large_icon_service_
->GetLargeIconOrFallbackStyle(
245 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
246 &cancelable_task_tracker_
);
247 base::MessageLoop::current()->RunUntilIdle();
248 EXPECT_TRUE(is_callback_invoked_
);
251 TEST_F(LargeIconServiceTest
, FallbackSinceIconMissingNoScale
) {
252 mock_favicon_service_
->InjectResult(favicon_base::FaviconRawBitmapResult());
253 // Expect default fallback style, including background.
254 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
255 large_icon_service_
->GetLargeIconOrFallbackStyle(
259 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
260 &cancelable_task_tracker_
);
261 base::MessageLoop::current()->RunUntilIdle();
262 EXPECT_TRUE(is_callback_invoked_
);
265 // Oddball case where we demand a high resolution icon to scale down. Generates
266 // fallback even though an icon with the final size is available.
267 TEST_F(LargeIconServiceTest
, FallbackSinceTooPicky
) {
268 mock_favicon_service_
->InjectResult(CreateTestBitmap(24, 24, kTestColor
));
269 expected_fallback_icon_style_
.reset(new favicon_base::FallbackIconStyle
);
270 expected_fallback_icon_style_
->background_color
= kTestColor
;
271 large_icon_service_
->GetLargeIconOrFallbackStyle(
275 base::Bind(&LargeIconServiceTest::ResultCallback
, base::Unretained(this)),
276 &cancelable_task_tracker_
);
277 base::MessageLoop::current()->RunUntilIdle();
278 EXPECT_TRUE(is_callback_invoked_
);
282 } // namespace favicon