Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: components/favicon/core/favicon_driver_impl_unittest.cc

Issue 2732653002: Add favicon integration tests for FaviconDriverImpl (Closed)
Patch Set: Added verification of color Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017 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.
4
5 #include "components/favicon/core/favicon_driver_impl.h"
6
7 #include <stddef.h>
8
9 #include <map>
10 #include <memory>
11 #include <set>
12 #include <utility>
13 #include <vector>
14
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/run_loop.h"
18 #include "base/strings/stringprintf.h"
19 #include "components/favicon/core/favicon_client.h"
20 #include "components/favicon/core/favicon_service_impl.h"
21 #include "components/favicon/core/favicon_url.h"
22 #include "components/favicon/core/test/mock_favicon_service.h"
23 #include "components/history/core/browser/history_database_params.h"
24 #include "components/history/core/browser/history_service.h"
25 #include "components/history/core/test/test_history_database.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/skia/include/core/SkBitmap.h"
29 #include "ui/base/layout.h"
30 #include "ui/gfx/codec/png_codec.h"
31 #include "ui/gfx/color_analysis.h"
32 #include "ui/gfx/favicon_size.h"
33 #include "ui/gfx/image/image.h"
34
35 namespace favicon {
36 namespace {
37
38 using favicon_base::FAVICON;
39 using favicon_base::FaviconRawBitmapResult;
40 using favicon_base::TOUCH_ICON;
41 using testing::Contains;
42 using testing::Eq;
43
44 // |arg| is a FaviconRawBitmapResult.
45 MATCHER_P(HasColor, expected_color, "") {
46 if (arg.bitmap_data == nullptr || arg.bitmap_data->size() == 0) {
47 *result_listener << "expected color but no bitmap data available for URL "
48 << arg.icon_url;
49 return false;
50 }
51 SkColor actual_color = color_utils::CalculateKMeanColorOfPNG(arg.bitmap_data);
52 if (actual_color != expected_color) {
53 *result_listener << "expected color "
54 << base::StringPrintf("%08X", expected_color)
55 << " but actual color is "
56 << base::StringPrintf("%08X", actual_color) << " for URL "
57 << arg.icon_url;
58 return false;
59 }
60 return true;
61 }
62
63 SkBitmap CreateBitmap(int w, int h, SkColor color) {
64 SkBitmap bmp;
65 bmp.allocN32Pixels(w, h);
66 bmp.eraseColor(color);
67 return bmp;
68 }
69
70 class FakeImageDownloader {
71 public:
72 struct Bitmap {
73 Bitmap(gfx::Size size, SkColor color) : size(size), color(color) {}
74
75 gfx::Size size;
76 SkColor color;
77 };
78
79 FakeImageDownloader() : num_downloads_(0) {}
80
81 void SetFakeResponse(const GURL& url, const std::vector<Bitmap>& bitmaps) {
82 fake_responses_[url].first.clear();
83 fake_responses_[url].second.clear();
84 for (const Bitmap& bitmap : bitmaps) {
85 fake_responses_[url].second.push_back(bitmap.size);
86 fake_responses_[url].first.push_back(CreateBitmap(
87 bitmap.size.width(), bitmap.size.height(), bitmap.color));
88 }
89 }
90
91 int DownloadImage(const GURL& url,
92 int max_image_size,
93 FaviconHandler::Delegate::ImageDownloadCallback callback) {
94 ++num_downloads_;
95 auto it = fake_responses_.find(url);
96 if (it == fake_responses_.end()) {
97 base::ThreadTaskRunnerHandle::Get()->PostTask(
98 FROM_HERE,
99 base::Bind(callback, num_downloads_, 404, url,
100 std::vector<SkBitmap>(), std::vector<gfx::Size>()));
101 } else {
102 base::ThreadTaskRunnerHandle::Get()->PostTask(
103 FROM_HERE, base::Bind(callback, num_downloads_, 200, url,
104 it->second.first, it->second.second));
105 }
106 return num_downloads_;
107 }
108
109 int NumDownloads() { return num_downloads_; }
110
111 private:
112 std::map<GURL, std::pair<std::vector<SkBitmap>, std::vector<gfx::Size>>>
113 fake_responses_;
114 int num_downloads_;
115 };
116
117 class TestFaviconDriver : public FaviconDriverImpl {
118 public:
119 TestFaviconDriver(FaviconService* favicon_service,
120 history::HistoryService* history_service,
121 FakeImageDownloader* fake_image_downloader)
122 : FaviconDriverImpl(/*enable_touch_icons=*/true,
123 favicon_service,
124 history_service,
125 /*bookmark_model=*/nullptr),
126 fake_image_downloader_(fake_image_downloader) {}
127
128 // Test method exercising the two events during a page visit.
129 void TestFetchFaviconForPage(const GURL& page_url,
130 const std::vector<FaviconURL>& candidates) {
131 FetchFavicon(page_url);
132 OnUpdateFaviconURL(page_url, candidates);
133 }
134
135 // FaviconDriver implementation.
136 gfx::Image GetFavicon() const override { return gfx::Image(); }
137
138 // FaviconHandler::Delegate implementation.
139 int DownloadImage(const GURL& url,
140 int max_image_size,
141 ImageDownloadCallback callback) override {
142 return fake_image_downloader_->DownloadImage(url, max_image_size, callback);
143 }
144
145 bool IsOffTheRecord() override { return false; }
146
147 void OnFaviconUpdated(
148 const GURL& page_url,
149 FaviconDriverObserver::NotificationIconType notification_icon_type,
150 const GURL& icon_url,
151 bool icon_url_changed,
152 const gfx::Image& image) override {
153 received_favicon_updates_.insert(page_url);
154 }
155
156 const std::set<GURL>& GetReceivedFaviconUpdates() const {
157 return received_favicon_updates_;
158 }
159
160 private:
161 FakeImageDownloader* fake_image_downloader_;
162 std::set<GURL> received_favicon_updates_;
163 };
164
165 // Integration tests that use a test fixture with a real implementation for
166 // FaviconService (with an instance of HistoryService behind).
167 class FaviconDriverImplIntegrationTest : public testing::Test {
168 protected:
169 const std::vector<gfx::Size> kEmptyIconSizes;
170
171 FaviconDriverImplIntegrationTest()
172 : favicon_service_(/*favicon_client=*/{}, &history_service_),
173 scoped_set_supported_scale_factors_({ui::SCALE_FACTOR_200P}),
174 test_favicon_driver_(&favicon_service_,
175 &history_service_,
176 &fake_image_downloader_) {
177 CHECK(temp_history_dir_.CreateUniqueTempDir());
178 // We use the main thread (task runner) for the history thread during
179 // testing.
180 history_service_.InitForTest(
181 history::TestHistoryDatabaseParamsForPath(temp_history_dir_.GetPath()),
182 base::ThreadTaskRunnerHandle::Get());
183 }
184
185 ~FaviconDriverImplIntegrationTest() override { history_service_.Shutdown(); }
186
187 // Convenience synchronous API that mimics the async one in FaviconService.
188 FaviconRawBitmapResult GetRawFaviconForPageURL(const GURL& page_url,
189 int icon_types,
190 int desired_size_in_pixel) {
191 FaviconRawBitmapResult result;
192 base::CancelableTaskTracker tracker;
193 base::RunLoop loop;
194 favicon_service_.GetRawFaviconForPageURL(
195 page_url, icon_types, desired_size_in_pixel,
196 base::Bind(
197 [](FaviconRawBitmapResult* save_result, base::RunLoop* loop,
198 const FaviconRawBitmapResult& result) {
199 *save_result = result;
200 loop->Quit();
201 },
202 &result, &loop),
203 &tracker);
204 loop.Run();
205 return result;
206 }
207
208 base::MessageLoopForUI message_loop_;
209 base::ScopedTempDir temp_history_dir_;
210 history::HistoryService history_service_;
211 FaviconServiceImpl favicon_service_;
212 ui::test::ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
213 FakeImageDownloader fake_image_downloader_;
214 TestFaviconDriver test_favicon_driver_;
215 };
216
217 TEST_F(FaviconDriverImplIntegrationTest, ShouldCacheSingle32x32) {
218 const GURL kPageURL("http://www.google.com");
219 const GURL kIconURL("http://www.google.com/googleg_lodp.ico");
220
221 fake_image_downloader_.SetFakeResponse(kIconURL,
222 {{gfx::Size(32, 32), SK_ColorRED}});
223
224 test_favicon_driver_.TestFetchFaviconForPage(
225 kPageURL, {FaviconURL(kIconURL, FAVICON, kEmptyIconSizes)});
226 base::RunLoop().RunUntilIdle();
227
228 EXPECT_FALSE(GetRawFaviconForPageURL(kPageURL, TOUCH_ICON,
229 /*desired_size_in_pixel=*/0)
230 .is_valid());
231
232 const FaviconRawBitmapResult result =
233 GetRawFaviconForPageURL(kPageURL, FAVICON, /*desired_size_in_pixel=*/0);
234 EXPECT_TRUE(result.is_valid());
235 EXPECT_THAT(result.icon_type, Eq(FAVICON));
236 EXPECT_THAT(result.pixel_size, Eq(gfx::Size(32, 32)));
237 EXPECT_THAT(result, HasColor(SK_ColorRED));
238 EXPECT_THAT(fake_image_downloader_.NumDownloads(), Eq(1));
239 }
240
241 TEST_F(FaviconDriverImplIntegrationTest, ShouldGetExactMatch) {
242 const GURL kPageURL("http://www.google.com");
243 const GURL kIconURL("http://www.google.com/googleg_lodp.ico");
244
245 fake_image_downloader_.SetFakeResponse(
246 kIconURL,
247 {{gfx::Size(32, 32), SK_ColorRED}, {gfx::Size(64, 64), SK_ColorGREEN}});
248
249 test_favicon_driver_.TestFetchFaviconForPage(
250 kPageURL, {FaviconURL(kIconURL, FAVICON, kEmptyIconSizes)});
251 base::RunLoop().RunUntilIdle();
252
253 // TODO(mastiz) / DONOTSUBMIT: Why is the biggest one preferred?
254 EXPECT_THAT(
255 GetRawFaviconForPageURL(kPageURL, FAVICON, /*desired_size_in_pixel=*/32),
256 HasColor(SK_ColorGREEN));
257 }
258
259 TEST_F(FaviconDriverImplIntegrationTest, ShouldCacheMultipleWithExplicitSize) {
260 const GURL kPageURL("http://www.reddit.com");
261 const GURL kFaviconURL64("https://www.redditstatic.com/64x64.png");
262 const GURL kFaviconURL128("https://www.redditstatic.com/128x128.png");
263 const GURL kFaviconURL192("https://www.redditstatic.com/192x192.png");
264 const GURL kTouchIconURL76("https://www.redditstatic.com/76x76.png");
265 const GURL kTouchIconURL120("https://www.redditstatic.com/120x120.png");
266 const GURL kTouchIconURL152("https://www.redditstatic.com/152x152.png");
267 const GURL kTouchIconURL180("https://www.redditstatic.com/180x180.png");
268
269 fake_image_downloader_.SetFakeResponse(kFaviconURL64,
270 {{gfx::Size(64, 64), SK_ColorRED}});
271 fake_image_downloader_.SetFakeResponse(
272 kFaviconURL128, {{gfx::Size(128, 128), SK_ColorGREEN}});
273 fake_image_downloader_.SetFakeResponse(kFaviconURL192,
274 {{gfx::Size(192, 192), SK_ColorBLUE}});
275 fake_image_downloader_.SetFakeResponse(kTouchIconURL76,
276 {{gfx::Size(76, 76), SK_ColorYELLOW}});
277 fake_image_downloader_.SetFakeResponse(kTouchIconURL120,
278 {{gfx::Size(120, 120), SK_ColorCYAN}});
279 fake_image_downloader_.SetFakeResponse(
280 kTouchIconURL152, {{gfx::Size(152, 152), SK_ColorMAGENTA}});
281 fake_image_downloader_.SetFakeResponse(kTouchIconURL180,
282 {{gfx::Size(180, 180), SK_ColorGRAY}});
283
284 test_favicon_driver_.TestFetchFaviconForPage(
285 kPageURL,
286 {
287 FaviconURL(kFaviconURL64, FAVICON, {gfx::Size(64, 64)}),
288 FaviconURL(kFaviconURL128, FAVICON, {gfx::Size(128, 128)}),
289 FaviconURL(kFaviconURL192, FAVICON, {gfx::Size(192, 192)}),
290 FaviconURL(kTouchIconURL76, TOUCH_ICON, {gfx::Size(76, 76)}),
291 FaviconURL(kTouchIconURL120, TOUCH_ICON, {gfx::Size(120, 120)}),
292 FaviconURL(kTouchIconURL152, TOUCH_ICON, {gfx::Size(152, 152)}),
293 FaviconURL(kTouchIconURL180, TOUCH_ICON, {gfx::Size(180, 180)}),
294 });
295 base::RunLoop().RunUntilIdle();
296
297 const FaviconRawBitmapResult favicon_result =
298 GetRawFaviconForPageURL(kPageURL, FAVICON, /*desired_size_in_pixel=*/128);
299 EXPECT_TRUE(favicon_result.is_valid());
300 EXPECT_THAT(favicon_result.icon_type, Eq(FAVICON));
301 EXPECT_THAT(favicon_result.pixel_size, Eq(gfx::Size(128, 128)));
302 // This is the current behavior but not necessarily ideal, since icons with
303 // a closer size are published by the site.
304 EXPECT_THAT(favicon_result, HasColor(SK_ColorBLUE));
305
306 const FaviconRawBitmapResult touch_icon_result =
307 GetRawFaviconForPageURL(kPageURL, TOUCH_ICON,
308 /*desired_size_in_pixel=*/0);
309 EXPECT_TRUE(touch_icon_result.is_valid());
310 EXPECT_THAT(touch_icon_result.icon_type, Eq(TOUCH_ICON));
311 EXPECT_THAT(touch_icon_result.pixel_size, Eq(gfx::Size(180, 180)));
312 EXPECT_THAT(touch_icon_result, HasColor(SK_ColorGRAY));
313
314 EXPECT_THAT(fake_image_downloader_.NumDownloads(), Eq(2));
315 }
316
317 TEST_F(FaviconDriverImplIntegrationTest, ShouldNotifyUpdateDespiteFinal404) {
318 const GURL kPage1URL("http://www.foo.com/page1");
319 const GURL kPage2URL("http://www.foo.com/page2");
320 const GURL kFavicon10x10URL("http://www.foo.com/favicon10x10.png");
321 const GURL kFavicon11x11URL("http://www.foo.com/favicon11x11.png");
322 const GURL kBadIconURL("http://www.foo.com/404.ico");
323
324 fake_image_downloader_.SetFakeResponse(kFavicon10x10URL,
325 {{gfx::Size(10, 10), SK_ColorRED}});
326 fake_image_downloader_.SetFakeResponse(kFavicon11x11URL,
327 {{gfx::Size(11, 11), SK_ColorGREEN}});
328
329 test_favicon_driver_.TestFetchFaviconForPage(
330 kPage1URL, {FaviconURL(kBadIconURL, FAVICON, kEmptyIconSizes)});
331 base::RunLoop().RunUntilIdle();
332
333 test_favicon_driver_.TestFetchFaviconForPage(
334 kPage2URL, {
335 FaviconURL(kFavicon10x10URL, FAVICON, kEmptyIconSizes),
336 FaviconURL(kFavicon11x11URL, FAVICON, kEmptyIconSizes),
337 FaviconURL(kBadIconURL, FAVICON, kEmptyIconSizes),
338 });
339 base::RunLoop().RunUntilIdle();
340
341 EXPECT_THAT(test_favicon_driver_.GetReceivedFaviconUpdates(),
342 Contains(kPage2URL));
343 }
344
345 } // namespace
346 } // namespace favicon
OLDNEW
« no previous file with comments | « components/favicon/core/favicon_driver_impl.cc ('k') | components/favicon/ios/web_favicon_driver.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698