| // 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_impl.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/task/cancelable_task_tracker.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "components/favicon/core/favicon_client.h" |
| #include "components/favicon/core/favicon_server_fetcher_params.h" |
| #include "components/favicon/core/test/mock_favicon_service.h" |
| #include "components/favicon_base/fallback_icon_style.h" |
| #include "components/favicon_base/favicon_types.h" |
| #include "components/image_fetcher/core/image_fetcher.h" |
| #include "components/image_fetcher/core/mock_image_fetcher.h" |
| #include "components/image_fetcher/core/request_metadata.h" |
| #include "components/variations/variations_params_manager.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "testing/gmock/include/gmock/gmock.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/base/layout.h" |
| #include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/image/image.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "url/gurl.h" |
| |
| namespace favicon { |
| namespace { |
| |
| using image_fetcher::MockImageFetcher; |
| using testing::ElementsAre; |
| using testing::IsEmpty; |
| using testing::IsNull; |
| using testing::Eq; |
| using testing::HasSubstr; |
| using testing::NiceMock; |
| using testing::Not; |
| using testing::Property; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::_; |
| |
| const char kDummyUrl[] = "http://www.example.com"; |
| const char kDummyIconUrl[] = "http://www.example.com/touch_icon.png"; |
| const SkColor kTestColor = SK_ColorRED; |
| |
| ACTION_P(PostFetchReply, p0) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(*arg2), p0, image_fetcher::RequestMetadata())); |
| } |
| |
| ACTION_P2(PostFetchReplyWithMetadata, p0, p1) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(*arg2), p0, p1)); |
| } |
| |
| ACTION_P(PostBoolReplyToArg4, p0) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(*arg4), p0)); |
| } |
| |
| ACTION_P(PostBoolReplyToArg2, p0) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(*arg2), p0)); |
| } |
| |
| SkBitmap CreateTestSkBitmap(int w, int h, SkColor color) { |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(w, h); |
| bitmap.eraseColor(color); |
| return bitmap; |
| } |
| |
| favicon_base::FaviconRawBitmapResult CreateTestBitmapResult(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::IconType::kTouchIcon; |
| CHECK(result.is_valid()); |
| return result; |
| } |
| |
| favicon_base::FaviconRawBitmapResult CreateTestBitmapResultWithIconUrl( |
| const GURL& icon_url) { |
| favicon_base::FaviconRawBitmapResult result = |
| CreateTestBitmapResult(64, 64, kTestColor); |
| result.icon_url = icon_url; |
| return result; |
| } |
| |
| bool HasBackgroundColor( |
| const favicon_base::FallbackIconStyle& fallback_icon_style, |
| SkColor color) { |
| return !fallback_icon_style.is_default_background_color && |
| fallback_icon_style.background_color == color; |
| } |
| |
| // TODO(jkrcal): Make the tests a bit crisper, see crbug.com/725822. |
| class LargeIconServiceTest : public testing::Test { |
| public: |
| LargeIconServiceTest() |
| : mock_image_fetcher_(new NiceMock<MockImageFetcher>()), |
| large_icon_service_(&mock_favicon_service_, |
| base::WrapUnique(mock_image_fetcher_)) { |
| scoped_set_supported_scale_factors_.reset( |
| new ui::test::ScopedSetSupportedScaleFactors({ui::SCALE_FACTOR_200P})); |
| } |
| |
| ~LargeIconServiceTest() override {} |
| |
| protected: |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| NiceMock<MockImageFetcher>* mock_image_fetcher_; |
| testing::NiceMock<MockFaviconService> mock_favicon_service_; |
| LargeIconServiceImpl large_icon_service_; |
| base::HistogramTester histogram_tester_; |
| std::unique_ptr<ui::test::ScopedSetSupportedScaleFactors> |
| scoped_set_supported_scale_factors_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(LargeIconServiceTest); |
| }; |
| |
| TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServer) { |
| const GURL kExpectedServerUrl( |
| "https://t0.gstatic.com/faviconV2?client=chrome&nfrp=2" |
| "&check_seen=true&size=61&min_size=42&max_size=256" |
| "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/"); |
| |
| EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0); |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrl), |
| favicon_base::IconType::kTouchIcon, _)) |
| .WillOnce(PostBoolReplyToArg2(true)); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| EXPECT_CALL(*mock_image_fetcher_, |
| FetchImageAndData_(kExpectedServerUrl, _, _, _)) |
| .WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap( |
| CreateTestSkBitmap(64, 64, kTestColor)))); |
| EXPECT_CALL(mock_favicon_service_, |
| SetOnDemandFavicons(GURL(kDummyUrl), kExpectedServerUrl, |
| favicon_base::IconType::kTouchIcon, _, _)) |
| .WillOnce(PostBoolReplyToArg4(true)); |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, |
| Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS)); |
| scoped_task_environment_.RunUntilIdle(); |
| histogram_tester_.ExpectUniqueSample( |
| "Favicons.LargeIconService.DownloadedSize", 64, /*expected_count=*/1); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerForDesktop) { |
| const GURL kExpectedServerUrl( |
| "https://t0.gstatic.com/faviconV2?client=chrome_desktop" |
| "&nfrp=2&check_seen=true&size=32&min_size=32&max_size=256" |
| "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/"); |
| |
| EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0); |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrl), |
| favicon_base::IconType::kFavicon, _)) |
| .WillOnce(PostBoolReplyToArg2(true)); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| EXPECT_CALL(*mock_image_fetcher_, |
| FetchImageAndData_(kExpectedServerUrl, _, _, _)) |
| .WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap( |
| CreateTestSkBitmap(32, 32, kTestColor)))); |
| EXPECT_CALL(mock_favicon_service_, |
| SetOnDemandFavicons(GURL(kDummyUrl), kExpectedServerUrl, |
| favicon_base::IconType::kFavicon, _, _)) |
| .WillOnce(PostBoolReplyToArg4(true)); |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForDesktop( |
| GURL(kDummyUrl)), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, |
| Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS)); |
| scoped_task_environment_.RunUntilIdle(); |
| histogram_tester_.ExpectUniqueSample( |
| "Favicons.LargeIconService.DownloadedSize", 32, /*expected_count=*/1); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerWithCustomUrl) { |
| variations::testing::VariationParamsManager variation_params( |
| "LargeIconServiceFetching", |
| {{"request_format", |
| "https://t0.gstatic.com/" |
| "faviconV2?%ssize=%d&min_size=%d&max_size=%d&url=%s"}, |
| {"enforced_min_size_in_pixel", "43"}, |
| {"desired_to_max_size_factor", "6.5"}}, |
| {"LargeIconServiceFetching"}); |
| const GURL kExpectedServerUrl( |
| "https://t0.gstatic.com/faviconV2?check_seen=true&" |
| "size=61&min_size=43&max_size=396&url=http://www.example.com/"); |
| |
| EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0); |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrl), |
| favicon_base::IconType::kTouchIcon, _)) |
| .WillOnce(PostBoolReplyToArg2(true)); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| EXPECT_CALL(*mock_image_fetcher_, |
| FetchImageAndData_(kExpectedServerUrl, _, _, _)) |
| .WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap( |
| CreateTestSkBitmap(64, 64, kTestColor)))); |
| EXPECT_CALL(mock_favicon_service_, |
| SetOnDemandFavicons(GURL(kDummyUrl), kExpectedServerUrl, |
| favicon_base::IconType::kTouchIcon, _, _)) |
| .WillOnce(PostBoolReplyToArg4(true)); |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, |
| Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS)); |
| scoped_task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServerWithOriginalUrl) { |
| const GURL kExpectedServerUrl( |
| "https://t0.gstatic.com/faviconV2?client=chrome&nfrp=2" |
| "&check_seen=true&size=61&min_size=42&max_size=256" |
| "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/"); |
| const GURL kExpectedOriginalUrl("http://www.example.com/favicon.png"); |
| |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrl), |
| favicon_base::IconType::kTouchIcon, _)) |
| .WillOnce(PostBoolReplyToArg2(true)); |
| |
| image_fetcher::RequestMetadata expected_metadata; |
| expected_metadata.content_location_header = kExpectedOriginalUrl.spec(); |
| EXPECT_CALL(*mock_image_fetcher_, |
| FetchImageAndData_(kExpectedServerUrl, _, _, _)) |
| .WillOnce(PostFetchReplyWithMetadata( |
| gfx::Image::CreateFrom1xBitmap( |
| CreateTestSkBitmap(64, 64, kTestColor)), |
| expected_metadata)); |
| EXPECT_CALL(mock_favicon_service_, |
| SetOnDemandFavicons(GURL(kDummyUrl), kExpectedOriginalUrl, |
| favicon_base::IconType::kTouchIcon, _, _)) |
| .WillOnce(PostBoolReplyToArg4(true)); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, |
| Run(favicon_base::GoogleFaviconServerRequestStatus::SUCCESS)); |
| scoped_task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldTrimQueryParametersForGoogleServer) { |
| const GURL kDummyUrlWithQuery("http://www.example.com?foo=1"); |
| const GURL kExpectedServerUrl( |
| "https://t0.gstatic.com/faviconV2?client=chrome&nfrp=2" |
| "&check_seen=true&size=61&min_size=42&max_size=256" |
| "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/"); |
| |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrlWithQuery), |
| favicon_base::IconType::kTouchIcon, _)) |
| .WillOnce(PostBoolReplyToArg2(true)); |
| |
| EXPECT_CALL(*mock_image_fetcher_, |
| FetchImageAndData_(kExpectedServerUrl, _, _, _)) |
| .WillOnce(PostFetchReply(gfx::Image::CreateFrom1xBitmap( |
| CreateTestSkBitmap(64, 64, kTestColor)))); |
| // Verify that the non-trimmed page URL is used when writing to the database. |
| EXPECT_CALL(mock_favicon_service_, |
| SetOnDemandFavicons(_, kExpectedServerUrl, _, _, _)); |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrlWithQuery), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| favicon_base::GoogleFaviconServerCallback()); |
| |
| scoped_task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldNotCheckOnPublicUrls) { |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrl), |
| favicon_base::IconType::kTouchIcon, _)) |
| .WillOnce(PostBoolReplyToArg2(true)); |
| // The request has no "check_seen=true"; full URL is tested elsewhere. |
| EXPECT_CALL( |
| *mock_image_fetcher_, |
| FetchImageAndData_( |
| Property(&GURL::query, Not(HasSubstr("check_seen=true"))), _, _, _)) |
| .WillOnce(PostFetchReply(gfx::Image())); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/false, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus:: |
| FAILURE_CONNECTION_ERROR)); |
| scoped_task_environment_.RunUntilIdle(); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldNotQueryGoogleServerIfInvalidScheme) { |
| const GURL kDummyFtpUrl("ftp://www.example.com"); |
| |
| EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyFtpUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus:: |
| FAILURE_TARGET_URL_SKIPPED)); |
| scoped_task_environment_.RunUntilIdle(); |
| EXPECT_THAT(histogram_tester_.GetAllSamples( |
| "Favicons.LargeIconService.DownloadedSize"), |
| IsEmpty()); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldNotQueryGoogleServerIfInvalidURL) { |
| const GURL kDummyInvalidUrl("htt"); |
| |
| EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyInvalidUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus:: |
| FAILURE_TARGET_URL_INVALID)); |
| scoped_task_environment_.RunUntilIdle(); |
| EXPECT_THAT(histogram_tester_.GetAllSamples( |
| "Favicons.LargeIconService.DownloadedSize"), |
| IsEmpty()); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldReportUnavailableIfFetchFromServerFails) { |
| const GURL kExpectedServerUrl( |
| "https://t0.gstatic.com/faviconV2?client=chrome&nfrp=2" |
| "&check_seen=true&size=61&min_size=42&max_size=256" |
| "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/"); |
| |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrl), |
| favicon_base::IconType::kTouchIcon, _)) |
| .WillOnce(PostBoolReplyToArg2(true)); |
| EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _)) |
| .Times(0); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| EXPECT_CALL(*mock_image_fetcher_, |
| FetchImageAndData_(kExpectedServerUrl, _, _, _)) |
| .WillOnce(PostFetchReply(gfx::Image())); |
| EXPECT_CALL(mock_favicon_service_, |
| UnableToDownloadFavicon(kExpectedServerUrl)); |
| |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus:: |
| FAILURE_CONNECTION_ERROR)); |
| scoped_task_environment_.RunUntilIdle(); |
| // Verify that download failure gets recorded. |
| histogram_tester_.ExpectUniqueSample( |
| "Favicons.LargeIconService.DownloadedSize", 0, /*expected_count=*/1); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldNotGetFromGoogleServerIfUnavailable) { |
| ON_CALL(mock_favicon_service_, |
| WasUnableToDownloadFavicon( |
| GURL("https://t0.gstatic.com/faviconV2?client=chrome&nfrp=2" |
| "&check_seen=true&size=61&min_size=42&max_size=256" |
| "&fallback_opts=TYPE,SIZE,URL&url=http://www.example.com/"))) |
| .WillByDefault(Return(true)); |
| |
| EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0); |
| EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0); |
| EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _)) |
| .Times(0); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus:: |
| FAILURE_HTTP_ERROR_CACHED)); |
| scoped_task_environment_.RunUntilIdle(); |
| EXPECT_THAT(histogram_tester_.GetAllSamples( |
| "Favicons.LargeIconService.DownloadedSize"), |
| IsEmpty()); |
| } |
| |
| TEST_F(LargeIconServiceTest, ShouldNotGetFromGoogleServerIfCannotSet) { |
| EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0); |
| EXPECT_CALL(mock_favicon_service_, |
| CanSetOnDemandFavicons(GURL(kDummyUrl), |
| favicon_base::IconType::kTouchIcon, _)) |
| .WillOnce(PostBoolReplyToArg2(false)); |
| |
| EXPECT_CALL(*mock_image_fetcher_, FetchImageAndData_(_, _, _, _)).Times(0); |
| EXPECT_CALL(mock_favicon_service_, SetOnDemandFavicons(_, _, _, _, _)) |
| .Times(0); |
| |
| base::MockCallback<favicon_base::GoogleFaviconServerCallback> callback; |
| large_icon_service_ |
| .GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForMobile( |
| GURL(kDummyUrl), |
| /*min_source_size_in_pixel=*/42, |
| /*desired_size_in_pixel=*/61), |
| /*may_page_url_be_private=*/true, TRAFFIC_ANNOTATION_FOR_TESTS, |
| callback.Get()); |
| |
| EXPECT_CALL(callback, Run(favicon_base::GoogleFaviconServerRequestStatus:: |
| FAILURE_ICON_EXISTS_IN_DB)); |
| scoped_task_environment_.RunUntilIdle(); |
| EXPECT_THAT(histogram_tester_.GetAllSamples( |
| "Favicons.LargeIconService.DownloadedSize"), |
| IsEmpty()); |
| } |
| |
| class LargeIconServiceGetterTest : public LargeIconServiceTest, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| LargeIconServiceGetterTest() {} |
| ~LargeIconServiceGetterTest() override {} |
| |
| void GetLargeIconOrFallbackStyleAndWaitForCallback( |
| const GURL& page_url, |
| int min_source_size_in_pixel, |
| int desired_size_in_pixel) { |
| // Switch over testing two analogous functions based on the bool param. |
| if (GetParam()) { |
| large_icon_service_.GetLargeIconRawBitmapOrFallbackStyleForPageUrl( |
| page_url, min_source_size_in_pixel, desired_size_in_pixel, |
| base::BindRepeating( |
| &LargeIconServiceGetterTest::RawBitmapResultCallback, |
| base::Unretained(this)), |
| &cancelable_task_tracker_); |
| } else { |
| large_icon_service_.GetLargeIconImageOrFallbackStyleForPageUrl( |
| page_url, min_source_size_in_pixel, desired_size_in_pixel, |
| base::BindRepeating(&LargeIconServiceGetterTest::ImageResultCallback, |
| base::Unretained(this)), |
| &cancelable_task_tracker_); |
| } |
| scoped_task_environment_.RunUntilIdle(); |
| } |
| |
| void RawBitmapResultCallback(const favicon_base::LargeIconResult& result) { |
| if (result.bitmap.is_valid()) { |
| returned_bitmap_size_ = |
| std::make_unique<gfx::Size>(result.bitmap.pixel_size); |
| } |
| StoreFallbackStyle(result.fallback_icon_style.get()); |
| } |
| |
| void ImageResultCallback(const favicon_base::LargeIconImageResult& result) { |
| if (!result.image.IsEmpty()) { |
| returned_bitmap_size_ = |
| std::make_unique<gfx::Size>(result.image.ToImageSkia()->size()); |
| ASSERT_TRUE(result.icon_url.is_valid()); |
| } |
| StoreFallbackStyle(result.fallback_icon_style.get()); |
| } |
| |
| void StoreFallbackStyle( |
| const favicon_base::FallbackIconStyle* fallback_style) { |
| if (fallback_style) { |
| returned_fallback_style_ = |
| std::make_unique<favicon_base::FallbackIconStyle>(*fallback_style); |
| } |
| } |
| |
| void InjectMockResult( |
| const GURL& page_url, |
| const favicon_base::FaviconRawBitmapResult& mock_result) { |
| ON_CALL(mock_favicon_service_, |
| GetLargestRawFaviconForPageURL(page_url, _, _, _, _)) |
| .WillByDefault(PostReply<5>(mock_result)); |
| } |
| |
| protected: |
| base::CancelableTaskTracker cancelable_task_tracker_; |
| |
| std::unique_ptr<favicon_base::FallbackIconStyle> returned_fallback_style_; |
| std::unique_ptr<gfx::Size> returned_bitmap_size_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(LargeIconServiceGetterTest); |
| }; |
| |
| TEST_P(LargeIconServiceGetterTest, SameSize) { |
| InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 24, kTestColor)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback( |
| GURL(kDummyUrl), |
| 24, // |min_source_size_in_pixel| |
| 24); // |desired_size_in_pixel| |
| EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_); |
| EXPECT_EQ(nullptr, returned_fallback_style_); |
| } |
| |
| TEST_P(LargeIconServiceGetterTest, ScaleDown) { |
| InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(32, 32, kTestColor)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24); |
| EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_); |
| EXPECT_EQ(nullptr, returned_fallback_style_); |
| } |
| |
| TEST_P(LargeIconServiceGetterTest, ScaleUp) { |
| InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(16, 16, kTestColor)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback( |
| GURL(kDummyUrl), |
| 14, // Lowered requirement so stored bitmap is admitted. |
| 24); |
| EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_); |
| EXPECT_EQ(nullptr, returned_fallback_style_); |
| } |
| |
| // |desired_size_in_pixel| == 0 means retrieve original image without scaling. |
| TEST_P(LargeIconServiceGetterTest, NoScale) { |
| InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 24, kTestColor)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 16, 0); |
| EXPECT_EQ(gfx::Size(24, 24), *returned_bitmap_size_); |
| EXPECT_EQ(nullptr, returned_fallback_style_); |
| } |
| |
| TEST_P(LargeIconServiceGetterTest, FallbackSinceIconTooSmall) { |
| InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(16, 16, kTestColor)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24); |
| EXPECT_EQ(nullptr, returned_bitmap_size_); |
| EXPECT_TRUE(HasBackgroundColor(*returned_fallback_style_, kTestColor)); |
| histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize", |
| 16, /*expected_count=*/1); |
| } |
| |
| TEST_P(LargeIconServiceGetterTest, FallbackSinceIconNotSquare) { |
| InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 32, kTestColor)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24); |
| EXPECT_EQ(nullptr, returned_bitmap_size_); |
| EXPECT_TRUE(HasBackgroundColor(*returned_fallback_style_, kTestColor)); |
| histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize", |
| 24, /*expected_count=*/1); |
| } |
| |
| TEST_P(LargeIconServiceGetterTest, FallbackSinceIconMissing) { |
| InjectMockResult(GURL(kDummyUrl), favicon_base::FaviconRawBitmapResult()); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 24); |
| EXPECT_EQ(nullptr, returned_bitmap_size_); |
| EXPECT_TRUE(returned_fallback_style_->is_default_background_color); |
| histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize", |
| 0, /*expected_count=*/1); |
| } |
| |
| TEST_P(LargeIconServiceGetterTest, FallbackSinceIconMissingNoScale) { |
| InjectMockResult(GURL(kDummyUrl), favicon_base::FaviconRawBitmapResult()); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 24, 0); |
| EXPECT_EQ(nullptr, returned_bitmap_size_); |
| EXPECT_TRUE(returned_fallback_style_->is_default_background_color); |
| histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize", |
| 0, /*expected_count=*/1); |
| } |
| |
| // 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_P(LargeIconServiceGetterTest, FallbackSinceTooPicky) { |
| InjectMockResult(GURL(kDummyUrl), CreateTestBitmapResult(24, 24, kTestColor)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(GURL(kDummyUrl), 32, 24); |
| EXPECT_EQ(nullptr, returned_bitmap_size_); |
| EXPECT_TRUE(HasBackgroundColor(*returned_fallback_style_, kTestColor)); |
| histogram_tester_.ExpectUniqueSample("Favicons.LargeIconService.FallbackSize", |
| 24, /*expected_count=*/1); |
| } |
| |
| // Tests UMA metric BlacklistedURLMismatch ignores unknown page URLs. |
| TEST_P(LargeIconServiceGetterTest, |
| ShouldNotRecordUrlMismatchesForUnknownPages) { |
| const std::string kUmaMetricName = |
| "Favicons.LargeIconService.BlacklistedURLMismatch"; |
| const GURL kUnknownPageUrl1("http://www.foo.com/path"); |
| const GURL kUnknownPageUrl2("http://www.bar.com/path"); |
| const GURL kUnknownPageUrl3("http://com/path"); |
| const GURL kUnknownIconUrl1("http://www.foo.com/favicon.ico"); |
| const GURL kUnknownIconUrl2("http://www.bar.com/favicon.ico"); |
| const GURL kUnknownIconUrl3("http://com/favicon.ico"); |
| const GURL kKnownIconUrl("http://www.google.com/favicon.ico"); |
| |
| // Only URLs in the list of known organizations contribute to the histogram, |
| // so neither of the sites below should be logged. |
| InjectMockResult(kUnknownPageUrl1, |
| CreateTestBitmapResultWithIconUrl(kUnknownIconUrl1)); |
| InjectMockResult(kUnknownPageUrl3, |
| CreateTestBitmapResultWithIconUrl(kUnknownIconUrl3)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kUnknownPageUrl1, 1, 0); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kUnknownPageUrl3, 1, 0); |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kUmaMetricName), IsEmpty()); |
| |
| // Even if there is a mismatch, it's irrelevant if none of the URLs are known. |
| InjectMockResult(kUnknownPageUrl1, |
| CreateTestBitmapResultWithIconUrl(kUnknownIconUrl2)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kUnknownPageUrl1, 1, 0); |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kUmaMetricName), IsEmpty()); |
| |
| // If a unknown site uses a known icon, it's still ignored. |
| InjectMockResult(kUnknownPageUrl1, |
| CreateTestBitmapResultWithIconUrl(kKnownIconUrl)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kUnknownPageUrl1, 1, 0); |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kUmaMetricName), IsEmpty()); |
| } |
| |
| // Tests UMA metric BlacklistedURLMismatch emits records for known page URLs. |
| TEST_P(LargeIconServiceGetterTest, ShouldRecordUrlMismatchesForKnownPages) { |
| const std::string kUmaMetricName = |
| "Favicons.LargeIconService.BlacklistedURLMismatch"; |
| const GURL kKnownPageUrl1("http://www.google.com/path"); |
| const GURL kKnownPageUrl2("http://www.youtube.com/path"); |
| const GURL kKnownIconUrl1("http://www.google.com/favicon.ico"); |
| const GURL kKnownIconUrl2("http://www.youtube.com/favicon.ico"); |
| const GURL kUnknownIconUrl("http://www.foo.com/favicon.ico"); |
| |
| // Mismatch between a known organization and an unknown one should contribute |
| // to bucket 0, although we're unsure if it's legit (false positives ok). |
| InjectMockResult(kKnownPageUrl1, |
| CreateTestBitmapResultWithIconUrl(kUnknownIconUrl)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kKnownPageUrl1, 1, 0); |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kUmaMetricName), |
| ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); |
| |
| // Matching pairs within known organizations should contribute to bucket 0. |
| InjectMockResult(kKnownPageUrl1, |
| CreateTestBitmapResultWithIconUrl(kKnownIconUrl1)); |
| InjectMockResult(kKnownPageUrl2, |
| CreateTestBitmapResultWithIconUrl(kKnownIconUrl2)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kKnownPageUrl1, 1, 0); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kKnownPageUrl2, 1, 0); |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kUmaMetricName), |
| ElementsAre(base::Bucket(/*min=*/0, /*count=*/3))); |
| |
| // Mismatch between a known organization and another known one should |
| // contribute to bucket 1. |
| InjectMockResult(kKnownPageUrl1, |
| CreateTestBitmapResultWithIconUrl(kKnownIconUrl2)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kKnownPageUrl1, 1, 0); |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kUmaMetricName), |
| ElementsAre(base::Bucket(/*min=*/0, /*count=*/3), |
| base::Bucket(/*min=*/1, /*count=*/1))); |
| } |
| |
| // Tests UMA metric BlacklistedURLMismatch treats different URLs corresponding |
| // to the same organization as matches. |
| TEST_P(LargeIconServiceGetterTest, ShouldRecordMatchesDespiteDifferentUrls) { |
| const std::string kUmaMetricName = |
| "Favicons.LargeIconService.BlacklistedURLMismatch"; |
| const GURL kKnownPageUrl("http://www.google.de/path"); |
| const GURL kKnownIconUrl("http://www.google.com/favicon.ico"); |
| |
| // Matching pairs within known organizations should contribute to bucket 0. |
| InjectMockResult(kKnownPageUrl, |
| CreateTestBitmapResultWithIconUrl(kKnownIconUrl)); |
| GetLargeIconOrFallbackStyleAndWaitForCallback(kKnownPageUrl, 1, 0); |
| EXPECT_THAT(histogram_tester_.GetAllSamples(kUmaMetricName), |
| ElementsAre(base::Bucket(/*min=*/0, /*count=*/1))); |
| } |
| |
| // Every test will appear with suffix /0 (param false) and /1 (param true), e.g. |
| // LargeIconServiceGetterTest.FallbackSinceTooPicky/0: get image. |
| // LargeIconServiceGetterTest.FallbackSinceTooPicky/1: get raw bitmap. |
| INSTANTIATE_TEST_CASE_P(, // Empty instatiation name. |
| LargeIconServiceGetterTest, |
| ::testing::Values(false, true)); |
| |
| TEST(LargeIconServiceOrganizationNameTest, ShouldGetOrganizationNameForUma) { |
| EXPECT_EQ("", LargeIconServiceImpl::GetOrganizationNameForUma(GURL())); |
| EXPECT_EQ("", |
| LargeIconServiceImpl::GetOrganizationNameForUma(GURL("http://"))); |
| EXPECT_EQ("", LargeIconServiceImpl::GetOrganizationNameForUma(GURL("com"))); |
| EXPECT_EQ( |
| "", LargeIconServiceImpl::GetOrganizationNameForUma(GURL("http://com"))); |
| EXPECT_EQ("", LargeIconServiceImpl::GetOrganizationNameForUma( |
| GURL("http://google"))); |
| EXPECT_EQ("google", LargeIconServiceImpl::GetOrganizationNameForUma( |
| GURL("http://google.com"))); |
| EXPECT_EQ("google", LargeIconServiceImpl::GetOrganizationNameForUma( |
| GURL("http://google.de"))); |
| EXPECT_EQ("google", LargeIconServiceImpl::GetOrganizationNameForUma( |
| GURL("http://foo.google.com"))); |
| } |
| |
| } // namespace |
| } // namespace favicon |