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

Unified 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, 10 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: components/favicon/core/favicon_driver_impl_unittest.cc
diff --git a/components/favicon/core/favicon_driver_impl_unittest.cc b/components/favicon/core/favicon_driver_impl_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e34861d8f557c43e53fe832d996bcd7a1b64dcf3
--- /dev/null
+++ b/components/favicon/core/favicon_driver_impl_unittest.cc
@@ -0,0 +1,346 @@
+// Copyright (c) 2017 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/favicon_driver_impl.h"
+
+#include <stddef.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/files/scoped_temp_dir.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "components/favicon/core/favicon_client.h"
+#include "components/favicon/core/favicon_service_impl.h"
+#include "components/favicon/core/favicon_url.h"
+#include "components/favicon/core/test/mock_favicon_service.h"
+#include "components/history/core/browser/history_database_params.h"
+#include "components/history/core/browser/history_service.h"
+#include "components/history/core/test/test_history_database.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/layout.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/color_analysis.h"
+#include "ui/gfx/favicon_size.h"
+#include "ui/gfx/image/image.h"
+
+namespace favicon {
+namespace {
+
+using favicon_base::FAVICON;
+using favicon_base::FaviconRawBitmapResult;
+using favicon_base::TOUCH_ICON;
+using testing::Contains;
+using testing::Eq;
+
+// |arg| is a FaviconRawBitmapResult.
+MATCHER_P(HasColor, expected_color, "") {
+ if (arg.bitmap_data == nullptr || arg.bitmap_data->size() == 0) {
+ *result_listener << "expected color but no bitmap data available for URL "
+ << arg.icon_url;
+ return false;
+ }
+ SkColor actual_color = color_utils::CalculateKMeanColorOfPNG(arg.bitmap_data);
+ if (actual_color != expected_color) {
+ *result_listener << "expected color "
+ << base::StringPrintf("%08X", expected_color)
+ << " but actual color is "
+ << base::StringPrintf("%08X", actual_color) << " for URL "
+ << arg.icon_url;
+ return false;
+ }
+ return true;
+}
+
+SkBitmap CreateBitmap(int w, int h, SkColor color) {
+ SkBitmap bmp;
+ bmp.allocN32Pixels(w, h);
+ bmp.eraseColor(color);
+ return bmp;
+}
+
+class FakeImageDownloader {
+ public:
+ struct Bitmap {
+ Bitmap(gfx::Size size, SkColor color) : size(size), color(color) {}
+
+ gfx::Size size;
+ SkColor color;
+ };
+
+ FakeImageDownloader() : num_downloads_(0) {}
+
+ void SetFakeResponse(const GURL& url, const std::vector<Bitmap>& bitmaps) {
+ fake_responses_[url].first.clear();
+ fake_responses_[url].second.clear();
+ for (const Bitmap& bitmap : bitmaps) {
+ fake_responses_[url].second.push_back(bitmap.size);
+ fake_responses_[url].first.push_back(CreateBitmap(
+ bitmap.size.width(), bitmap.size.height(), bitmap.color));
+ }
+ }
+
+ int DownloadImage(const GURL& url,
+ int max_image_size,
+ FaviconHandler::Delegate::ImageDownloadCallback callback) {
+ ++num_downloads_;
+ auto it = fake_responses_.find(url);
+ if (it == fake_responses_.end()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, num_downloads_, 404, url,
+ std::vector<SkBitmap>(), std::vector<gfx::Size>()));
+ } else {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, num_downloads_, 200, url,
+ it->second.first, it->second.second));
+ }
+ return num_downloads_;
+ }
+
+ int NumDownloads() { return num_downloads_; }
+
+ private:
+ std::map<GURL, std::pair<std::vector<SkBitmap>, std::vector<gfx::Size>>>
+ fake_responses_;
+ int num_downloads_;
+};
+
+class TestFaviconDriver : public FaviconDriverImpl {
+ public:
+ TestFaviconDriver(FaviconService* favicon_service,
+ history::HistoryService* history_service,
+ FakeImageDownloader* fake_image_downloader)
+ : FaviconDriverImpl(/*enable_touch_icons=*/true,
+ favicon_service,
+ history_service,
+ /*bookmark_model=*/nullptr),
+ fake_image_downloader_(fake_image_downloader) {}
+
+ // Test method exercising the two events during a page visit.
+ void TestFetchFaviconForPage(const GURL& page_url,
+ const std::vector<FaviconURL>& candidates) {
+ FetchFavicon(page_url);
+ OnUpdateFaviconURL(page_url, candidates);
+ }
+
+ // FaviconDriver implementation.
+ gfx::Image GetFavicon() const override { return gfx::Image(); }
+
+ // FaviconHandler::Delegate implementation.
+ int DownloadImage(const GURL& url,
+ int max_image_size,
+ ImageDownloadCallback callback) override {
+ return fake_image_downloader_->DownloadImage(url, max_image_size, callback);
+ }
+
+ bool IsOffTheRecord() override { return false; }
+
+ void OnFaviconUpdated(
+ const GURL& page_url,
+ FaviconDriverObserver::NotificationIconType notification_icon_type,
+ const GURL& icon_url,
+ bool icon_url_changed,
+ const gfx::Image& image) override {
+ received_favicon_updates_.insert(page_url);
+ }
+
+ const std::set<GURL>& GetReceivedFaviconUpdates() const {
+ return received_favicon_updates_;
+ }
+
+ private:
+ FakeImageDownloader* fake_image_downloader_;
+ std::set<GURL> received_favicon_updates_;
+};
+
+// Integration tests that use a test fixture with a real implementation for
+// FaviconService (with an instance of HistoryService behind).
+class FaviconDriverImplIntegrationTest : public testing::Test {
+ protected:
+ const std::vector<gfx::Size> kEmptyIconSizes;
+
+ FaviconDriverImplIntegrationTest()
+ : favicon_service_(/*favicon_client=*/{}, &history_service_),
+ scoped_set_supported_scale_factors_({ui::SCALE_FACTOR_200P}),
+ test_favicon_driver_(&favicon_service_,
+ &history_service_,
+ &fake_image_downloader_) {
+ CHECK(temp_history_dir_.CreateUniqueTempDir());
+ // We use the main thread (task runner) for the history thread during
+ // testing.
+ history_service_.InitForTest(
+ history::TestHistoryDatabaseParamsForPath(temp_history_dir_.GetPath()),
+ base::ThreadTaskRunnerHandle::Get());
+ }
+
+ ~FaviconDriverImplIntegrationTest() override { history_service_.Shutdown(); }
+
+ // Convenience synchronous API that mimics the async one in FaviconService.
+ FaviconRawBitmapResult GetRawFaviconForPageURL(const GURL& page_url,
+ int icon_types,
+ int desired_size_in_pixel) {
+ FaviconRawBitmapResult result;
+ base::CancelableTaskTracker tracker;
+ base::RunLoop loop;
+ favicon_service_.GetRawFaviconForPageURL(
+ page_url, icon_types, desired_size_in_pixel,
+ base::Bind(
+ [](FaviconRawBitmapResult* save_result, base::RunLoop* loop,
+ const FaviconRawBitmapResult& result) {
+ *save_result = result;
+ loop->Quit();
+ },
+ &result, &loop),
+ &tracker);
+ loop.Run();
+ return result;
+ }
+
+ base::MessageLoopForUI message_loop_;
+ base::ScopedTempDir temp_history_dir_;
+ history::HistoryService history_service_;
+ FaviconServiceImpl favicon_service_;
+ ui::test::ScopedSetSupportedScaleFactors scoped_set_supported_scale_factors_;
+ FakeImageDownloader fake_image_downloader_;
+ TestFaviconDriver test_favicon_driver_;
+};
+
+TEST_F(FaviconDriverImplIntegrationTest, ShouldCacheSingle32x32) {
+ const GURL kPageURL("http://www.google.com");
+ const GURL kIconURL("http://www.google.com/googleg_lodp.ico");
+
+ fake_image_downloader_.SetFakeResponse(kIconURL,
+ {{gfx::Size(32, 32), SK_ColorRED}});
+
+ test_favicon_driver_.TestFetchFaviconForPage(
+ kPageURL, {FaviconURL(kIconURL, FAVICON, kEmptyIconSizes)});
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(GetRawFaviconForPageURL(kPageURL, TOUCH_ICON,
+ /*desired_size_in_pixel=*/0)
+ .is_valid());
+
+ const FaviconRawBitmapResult result =
+ GetRawFaviconForPageURL(kPageURL, FAVICON, /*desired_size_in_pixel=*/0);
+ EXPECT_TRUE(result.is_valid());
+ EXPECT_THAT(result.icon_type, Eq(FAVICON));
+ EXPECT_THAT(result.pixel_size, Eq(gfx::Size(32, 32)));
+ EXPECT_THAT(result, HasColor(SK_ColorRED));
+ EXPECT_THAT(fake_image_downloader_.NumDownloads(), Eq(1));
+}
+
+TEST_F(FaviconDriverImplIntegrationTest, ShouldGetExactMatch) {
+ const GURL kPageURL("http://www.google.com");
+ const GURL kIconURL("http://www.google.com/googleg_lodp.ico");
+
+ fake_image_downloader_.SetFakeResponse(
+ kIconURL,
+ {{gfx::Size(32, 32), SK_ColorRED}, {gfx::Size(64, 64), SK_ColorGREEN}});
+
+ test_favicon_driver_.TestFetchFaviconForPage(
+ kPageURL, {FaviconURL(kIconURL, FAVICON, kEmptyIconSizes)});
+ base::RunLoop().RunUntilIdle();
+
+ // TODO(mastiz) / DONOTSUBMIT: Why is the biggest one preferred?
+ EXPECT_THAT(
+ GetRawFaviconForPageURL(kPageURL, FAVICON, /*desired_size_in_pixel=*/32),
+ HasColor(SK_ColorGREEN));
+}
+
+TEST_F(FaviconDriverImplIntegrationTest, ShouldCacheMultipleWithExplicitSize) {
+ const GURL kPageURL("http://www.reddit.com");
+ const GURL kFaviconURL64("https://www.redditstatic.com/64x64.png");
+ const GURL kFaviconURL128("https://www.redditstatic.com/128x128.png");
+ const GURL kFaviconURL192("https://www.redditstatic.com/192x192.png");
+ const GURL kTouchIconURL76("https://www.redditstatic.com/76x76.png");
+ const GURL kTouchIconURL120("https://www.redditstatic.com/120x120.png");
+ const GURL kTouchIconURL152("https://www.redditstatic.com/152x152.png");
+ const GURL kTouchIconURL180("https://www.redditstatic.com/180x180.png");
+
+ fake_image_downloader_.SetFakeResponse(kFaviconURL64,
+ {{gfx::Size(64, 64), SK_ColorRED}});
+ fake_image_downloader_.SetFakeResponse(
+ kFaviconURL128, {{gfx::Size(128, 128), SK_ColorGREEN}});
+ fake_image_downloader_.SetFakeResponse(kFaviconURL192,
+ {{gfx::Size(192, 192), SK_ColorBLUE}});
+ fake_image_downloader_.SetFakeResponse(kTouchIconURL76,
+ {{gfx::Size(76, 76), SK_ColorYELLOW}});
+ fake_image_downloader_.SetFakeResponse(kTouchIconURL120,
+ {{gfx::Size(120, 120), SK_ColorCYAN}});
+ fake_image_downloader_.SetFakeResponse(
+ kTouchIconURL152, {{gfx::Size(152, 152), SK_ColorMAGENTA}});
+ fake_image_downloader_.SetFakeResponse(kTouchIconURL180,
+ {{gfx::Size(180, 180), SK_ColorGRAY}});
+
+ test_favicon_driver_.TestFetchFaviconForPage(
+ kPageURL,
+ {
+ FaviconURL(kFaviconURL64, FAVICON, {gfx::Size(64, 64)}),
+ FaviconURL(kFaviconURL128, FAVICON, {gfx::Size(128, 128)}),
+ FaviconURL(kFaviconURL192, FAVICON, {gfx::Size(192, 192)}),
+ FaviconURL(kTouchIconURL76, TOUCH_ICON, {gfx::Size(76, 76)}),
+ FaviconURL(kTouchIconURL120, TOUCH_ICON, {gfx::Size(120, 120)}),
+ FaviconURL(kTouchIconURL152, TOUCH_ICON, {gfx::Size(152, 152)}),
+ FaviconURL(kTouchIconURL180, TOUCH_ICON, {gfx::Size(180, 180)}),
+ });
+ base::RunLoop().RunUntilIdle();
+
+ const FaviconRawBitmapResult favicon_result =
+ GetRawFaviconForPageURL(kPageURL, FAVICON, /*desired_size_in_pixel=*/128);
+ EXPECT_TRUE(favicon_result.is_valid());
+ EXPECT_THAT(favicon_result.icon_type, Eq(FAVICON));
+ EXPECT_THAT(favicon_result.pixel_size, Eq(gfx::Size(128, 128)));
+ // This is the current behavior but not necessarily ideal, since icons with
+ // a closer size are published by the site.
+ EXPECT_THAT(favicon_result, HasColor(SK_ColorBLUE));
+
+ const FaviconRawBitmapResult touch_icon_result =
+ GetRawFaviconForPageURL(kPageURL, TOUCH_ICON,
+ /*desired_size_in_pixel=*/0);
+ EXPECT_TRUE(touch_icon_result.is_valid());
+ EXPECT_THAT(touch_icon_result.icon_type, Eq(TOUCH_ICON));
+ EXPECT_THAT(touch_icon_result.pixel_size, Eq(gfx::Size(180, 180)));
+ EXPECT_THAT(touch_icon_result, HasColor(SK_ColorGRAY));
+
+ EXPECT_THAT(fake_image_downloader_.NumDownloads(), Eq(2));
+}
+
+TEST_F(FaviconDriverImplIntegrationTest, ShouldNotifyUpdateDespiteFinal404) {
+ const GURL kPage1URL("http://www.foo.com/page1");
+ const GURL kPage2URL("http://www.foo.com/page2");
+ const GURL kFavicon10x10URL("http://www.foo.com/favicon10x10.png");
+ const GURL kFavicon11x11URL("http://www.foo.com/favicon11x11.png");
+ const GURL kBadIconURL("http://www.foo.com/404.ico");
+
+ fake_image_downloader_.SetFakeResponse(kFavicon10x10URL,
+ {{gfx::Size(10, 10), SK_ColorRED}});
+ fake_image_downloader_.SetFakeResponse(kFavicon11x11URL,
+ {{gfx::Size(11, 11), SK_ColorGREEN}});
+
+ test_favicon_driver_.TestFetchFaviconForPage(
+ kPage1URL, {FaviconURL(kBadIconURL, FAVICON, kEmptyIconSizes)});
+ base::RunLoop().RunUntilIdle();
+
+ test_favicon_driver_.TestFetchFaviconForPage(
+ kPage2URL, {
+ FaviconURL(kFavicon10x10URL, FAVICON, kEmptyIconSizes),
+ FaviconURL(kFavicon11x11URL, FAVICON, kEmptyIconSizes),
+ FaviconURL(kBadIconURL, FAVICON, kEmptyIconSizes),
+ });
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_THAT(test_favicon_driver_.GetReceivedFaviconUpdates(),
+ Contains(kPage2URL));
+}
+
+} // namespace
+} // namespace favicon
« 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