Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/doodle/doodle_service.h" | 5 #include "components/doodle/doodle_service.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 #include <utility> | 8 #include <utility> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/memory/ref_counted.h" | 13 #include "base/memory/ref_counted.h" |
| 14 #include "base/test/histogram_tester.h" | 14 #include "base/test/histogram_tester.h" |
| 15 #include "base/test/mock_callback.h" | |
| 15 #include "base/test/simple_test_tick_clock.h" | 16 #include "base/test/simple_test_tick_clock.h" |
| 16 #include "base/test/test_mock_time_task_runner.h" | 17 #include "base/test/test_mock_time_task_runner.h" |
| 17 #include "base/threading/thread_task_runner_handle.h" | 18 #include "base/threading/thread_task_runner_handle.h" |
| 18 #include "base/time/time.h" | 19 #include "base/time/time.h" |
| 20 #include "components/image_fetcher/core/image_fetcher.h" | |
| 21 #include "components/image_fetcher/core/request_metadata.h" | |
| 19 #include "components/prefs/testing_pref_service.h" | 22 #include "components/prefs/testing_pref_service.h" |
| 20 #include "testing/gmock/include/gmock/gmock.h" | 23 #include "testing/gmock/include/gmock/gmock.h" |
| 21 #include "testing/gtest/include/gtest/gtest.h" | 24 #include "testing/gtest/include/gtest/gtest.h" |
| 25 #include "ui/gfx/geometry/size.h" | |
| 26 #include "ui/gfx/image/image.h" | |
| 27 #include "ui/gfx/image/image_unittest_util.h" | |
| 22 | 28 |
| 29 using image_fetcher::ImageFetcher; | |
| 30 using image_fetcher::RequestMetadata; | |
| 31 using testing::_; | |
| 23 using testing::Eq; | 32 using testing::Eq; |
| 24 using testing::StrictMock; | 33 using testing::StrictMock; |
| 34 using testing::Not; | |
| 25 | 35 |
| 26 namespace doodle { | 36 namespace doodle { |
| 27 | 37 |
| 28 namespace { | 38 namespace { |
| 29 | 39 |
| 30 class FakeDoodleFetcher : public DoodleFetcher { | 40 class FakeDoodleFetcher : public DoodleFetcher { |
| 31 public: | 41 public: |
| 32 FakeDoodleFetcher() = default; | 42 FakeDoodleFetcher() = default; |
| 33 ~FakeDoodleFetcher() override = default; | 43 ~FakeDoodleFetcher() override = default; |
| 34 | 44 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 53 std::vector<FinishedCallback> callbacks_; | 63 std::vector<FinishedCallback> callbacks_; |
| 54 }; | 64 }; |
| 55 | 65 |
| 56 class MockDoodleObserver : public DoodleService::Observer { | 66 class MockDoodleObserver : public DoodleService::Observer { |
| 57 public: | 67 public: |
| 58 MOCK_METHOD1(OnDoodleConfigUpdated, | 68 MOCK_METHOD1(OnDoodleConfigUpdated, |
| 59 void(const base::Optional<DoodleConfig>&)); | 69 void(const base::Optional<DoodleConfig>&)); |
| 60 MOCK_METHOD1(OnDoodleConfigRevalidated, void(bool)); | 70 MOCK_METHOD1(OnDoodleConfigRevalidated, void(bool)); |
| 61 }; | 71 }; |
| 62 | 72 |
| 73 class FakeImageFetcher : public ImageFetcher { | |
| 74 public: | |
| 75 FakeImageFetcher() = default; | |
| 76 ~FakeImageFetcher() override { | |
| 77 // To make sure we didn't receive an unexpected image request. | |
| 78 CHECK(!HasPendingRequest()); | |
|
fhorschig
2017/05/15 13:54:35
s/CHECK/DCHECK?
Also: do we really want a crash he
Marc Treib
2017/05/15 14:25:22
CHECK or DCHECK doesn't really make a difference,
| |
| 79 } | |
| 80 | |
| 81 void SetImageFetcherDelegate(image_fetcher::ImageFetcherDelegate*) override { | |
|
fhorschig
2017/05/15 13:54:35
Why not "// Ignored"?
(I am not sure, a test shoul
Marc Treib
2017/05/15 14:25:22
Well, if someone calls this, then they expect some
| |
| 82 NOTREACHED(); | |
| 83 } | |
| 84 | |
| 85 void SetDataUseServiceName(DataUseServiceName) override { | |
| 86 // Ignored. | |
| 87 } | |
| 88 | |
| 89 void SetImageDownloadLimit(base::Optional<int64_t>) override { | |
| 90 // Ignored. | |
| 91 } | |
| 92 | |
| 93 void SetDesiredImageFrameSize(const gfx::Size&) override { NOTREACHED(); } | |
|
fhorschig
2017/05/15 13:54:35
Again, why not "// Ignored"?
Marc Treib
2017/05/15 14:25:22
Eh.. same argument as above, though I guess here i
| |
| 94 | |
| 95 void StartOrQueueNetworkRequest( | |
| 96 const std::string& id, | |
| 97 const GURL& url, | |
| 98 const ImageFetcherCallback& callback) override { | |
| 99 // For simplicity, the fake doesn't support multiple concurrent requests. | |
| 100 CHECK(!HasPendingRequest()); | |
|
fhorschig
2017/05/15 13:54:35
nit: s/CHECK/DCHECK?
Marc Treib
2017/05/15 14:25:22
Done.
| |
| 101 | |
| 102 pending_id_ = id; | |
| 103 pending_url_ = url; | |
| 104 pending_callback_ = callback; | |
| 105 } | |
| 106 | |
| 107 image_fetcher::ImageDecoder* GetImageDecoder() override { | |
| 108 NOTREACHED(); | |
| 109 return nullptr; | |
| 110 } | |
| 111 | |
| 112 bool HasPendingRequest() const { return !pending_callback_.is_null(); } | |
| 113 | |
| 114 const GURL& pending_url() const { return pending_url_; } | |
| 115 | |
| 116 void RespondToPendingRequest(const gfx::Image& image) { | |
| 117 CHECK(HasPendingRequest()); | |
|
fhorschig
2017/05/15 13:54:35
nit: s/CHECK/DCHECK?
Marc Treib
2017/05/15 14:25:22
Done.
| |
| 118 | |
| 119 RequestMetadata metadata; | |
| 120 metadata.http_response_code = 200; | |
| 121 pending_callback_.Run(pending_id_, image, metadata); | |
| 122 | |
| 123 pending_id_.clear(); | |
| 124 pending_url_ = GURL(); | |
| 125 pending_callback_.Reset(); | |
| 126 } | |
| 127 | |
| 128 private: | |
| 129 std::string pending_id_; | |
| 130 GURL pending_url_; | |
| 131 ImageFetcherCallback pending_callback_; | |
| 132 }; | |
| 133 | |
| 63 DoodleConfig CreateConfig(DoodleType type) { | 134 DoodleConfig CreateConfig(DoodleType type) { |
| 64 return DoodleConfig(type, DoodleImage(GURL("https://doodle.com/image.jpg"))); | 135 return DoodleConfig(type, DoodleImage(GURL("https://doodle.com/image.jpg"))); |
| 65 } | 136 } |
| 66 | 137 |
| 138 MATCHER(IsEmptyImage, "") { | |
| 139 return arg.IsEmpty(); | |
| 140 } | |
| 141 | |
| 67 } // namespace | 142 } // namespace |
| 68 | 143 |
| 69 class DoodleServiceTest : public testing::Test { | 144 class DoodleServiceTest : public testing::Test { |
| 70 public: | 145 public: |
| 71 DoodleServiceTest() | 146 DoodleServiceTest() |
| 72 : task_runner_(new base::TestMockTimeTaskRunner()), | 147 : task_runner_(new base::TestMockTimeTaskRunner()), |
| 73 task_runner_handle_(task_runner_), | 148 task_runner_handle_(task_runner_), |
| 74 tick_clock_(task_runner_->GetMockTickClock()), | 149 tick_clock_(task_runner_->GetMockTickClock()), |
| 75 fetcher_(nullptr), | 150 fetcher_(nullptr), |
| 76 expiry_timer_(nullptr) { | 151 expiry_timer_(nullptr) { |
| 77 DoodleService::RegisterProfilePrefs(pref_service_.registry()); | 152 DoodleService::RegisterProfilePrefs(pref_service_.registry()); |
| 78 | 153 |
| 79 task_runner_->FastForwardBy(base::TimeDelta::FromHours(12345)); | 154 task_runner_->FastForwardBy(base::TimeDelta::FromHours(12345)); |
| 80 | 155 |
| 81 // Set the minimum refresh interval to 0 by default, so tests don't have to | 156 // Set the minimum refresh interval to 0 by default, so tests don't have to |
| 82 // worry about it. The tests that care set it explicitly. | 157 // worry about it. The tests that care set it explicitly. |
| 83 RecreateServiceWithZeroRefreshInterval(); | 158 RecreateServiceWithZeroRefreshInterval(); |
| 84 } | 159 } |
| 85 | 160 |
| 86 void DestroyService() { service_ = nullptr; } | 161 void DestroyService() { |
| 162 fetcher_ = nullptr; | |
| 163 expiry_timer_ = nullptr; | |
| 164 image_fetcher_ = nullptr; | |
|
fhorschig
2017/05/15 13:54:35
This could be a good place to ASSERT HasPendingReq
Marc Treib
2017/05/15 14:25:22
Yup, moved the ASSERT here. IMO the name is fine t
| |
| 165 | |
| 166 service_ = nullptr; | |
| 167 } | |
| 87 | 168 |
| 88 void RecreateServiceWithZeroRefreshInterval() { | 169 void RecreateServiceWithZeroRefreshInterval() { |
| 89 RecreateService(/*min_refresh_interval=*/base::TimeDelta()); | 170 RecreateService(/*min_refresh_interval=*/base::TimeDelta()); |
| 90 } | 171 } |
| 91 | 172 |
| 92 void RecreateService(base::Optional<base::TimeDelta> refresh_interval) { | 173 void RecreateService(base::Optional<base::TimeDelta> refresh_interval) { |
| 93 auto expiry_timer = base::MakeUnique<base::OneShotTimer>(tick_clock_.get()); | 174 auto expiry_timer = base::MakeUnique<base::OneShotTimer>(tick_clock_.get()); |
| 94 expiry_timer->SetTaskRunner(task_runner_); | 175 expiry_timer->SetTaskRunner(task_runner_); |
| 95 expiry_timer_ = expiry_timer.get(); | 176 expiry_timer_ = expiry_timer.get(); |
| 96 | 177 |
| 97 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); | 178 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); |
| 98 fetcher_ = fetcher.get(); | 179 fetcher_ = fetcher.get(); |
| 99 | 180 |
| 181 auto image_fetcher = base::MakeUnique<FakeImageFetcher>(); | |
| 182 image_fetcher_ = image_fetcher.get(); | |
| 183 | |
| 100 service_ = base::MakeUnique<DoodleService>( | 184 service_ = base::MakeUnique<DoodleService>( |
| 101 &pref_service_, std::move(fetcher), std::move(expiry_timer), | 185 &pref_service_, std::move(fetcher), std::move(expiry_timer), |
| 102 task_runner_->GetMockClock(), task_runner_->GetMockTickClock(), | 186 task_runner_->GetMockClock(), task_runner_->GetMockTickClock(), |
| 103 refresh_interval); | 187 refresh_interval, std::move(image_fetcher)); |
| 104 } | 188 } |
| 105 | 189 |
| 106 DoodleService* service() { return service_.get(); } | 190 DoodleService* service() { return service_.get(); } |
| 107 FakeDoodleFetcher* fetcher() { return fetcher_; } | 191 FakeDoodleFetcher* fetcher() { return fetcher_; } |
| 192 FakeImageFetcher* image_fetcher() { return image_fetcher_; } | |
| 108 | 193 |
| 109 base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); } | 194 base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); } |
| 110 | 195 |
| 111 private: | 196 private: |
| 112 TestingPrefServiceSimple pref_service_; | 197 TestingPrefServiceSimple pref_service_; |
| 113 | 198 |
| 114 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; | 199 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; |
| 115 base::ThreadTaskRunnerHandle task_runner_handle_; | 200 base::ThreadTaskRunnerHandle task_runner_handle_; |
| 116 std::unique_ptr<base::TickClock> tick_clock_; | 201 std::unique_ptr<base::TickClock> tick_clock_; |
| 117 | 202 |
| 118 std::unique_ptr<DoodleService> service_; | 203 std::unique_ptr<DoodleService> service_; |
| 119 | 204 |
| 120 // Weak, owned by the service. | 205 // Weak, owned by the service. |
| 121 FakeDoodleFetcher* fetcher_; | 206 FakeDoodleFetcher* fetcher_; |
| 122 base::OneShotTimer* expiry_timer_; | 207 base::OneShotTimer* expiry_timer_; |
| 208 FakeImageFetcher* image_fetcher_; | |
| 123 }; | 209 }; |
| 124 | 210 |
| 125 TEST_F(DoodleServiceTest, FetchesConfigOnRefresh) { | 211 TEST_F(DoodleServiceTest, FetchesConfigOnRefresh) { |
| 126 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 212 ASSERT_THAT(service()->config(), Eq(base::nullopt)); |
| 127 | 213 |
| 128 // Request a refresh of the doodle config. | 214 // Request a refresh of the doodle config. |
| 129 service()->Refresh(); | 215 service()->Refresh(); |
| 130 // The request should have arrived at the fetcher. | 216 // The request should have arrived at the fetcher. |
| 131 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 217 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
| 132 | 218 |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 590 | 676 |
| 591 // Fast-forward time so that the config expires. | 677 // Fast-forward time so that the config expires. |
| 592 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); | 678 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); |
| 593 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 679 ASSERT_THAT(service()->config(), Eq(base::nullopt)); |
| 594 | 680 |
| 595 // This should not have resulted in any metrics being emitted. | 681 // This should not have resulted in any metrics being emitted. |
| 596 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 682 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); |
| 597 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 683 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); |
| 598 } | 684 } |
| 599 | 685 |
| 686 TEST_F(DoodleServiceTest, GetImageWithEmptyConfigReturnsImmediately) { | |
| 687 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | |
| 688 | |
| 689 base::MockCallback<DoodleService::ImageCallback> callback; | |
| 690 EXPECT_CALL(callback, Run(IsEmptyImage())); | |
| 691 | |
| 692 service()->GetImage(callback.Get()); | |
|
fhorschig
2017/05/15 13:54:35
ASSERT_FALSE(HasPendingRequest) might ensure this
Marc Treib
2017/05/15 14:25:22
Er.. what?
Anyway, that assert happens during Tea
| |
| 693 } | |
| 694 | |
| 695 TEST_F(DoodleServiceTest, GetImageFetchesLargeImage) { | |
| 696 service()->Refresh(); | |
| 697 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | |
| 698 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | |
| 699 base::TimeDelta::FromHours(1), config); | |
| 700 ASSERT_THAT(service()->config(), Eq(config)); | |
| 701 | |
| 702 base::MockCallback<DoodleService::ImageCallback> callback; | |
| 703 service()->GetImage(callback.Get()); | |
| 704 | |
| 705 EXPECT_EQ(config.large_image.url, image_fetcher()->pending_url()); | |
| 706 | |
| 707 EXPECT_CALL(callback, Run(Not(IsEmptyImage()))); | |
| 708 gfx::Image image = gfx::test::CreateImage(1, 1); | |
| 709 image_fetcher()->RespondToPendingRequest(image); | |
|
fhorschig
2017/05/15 13:54:35
ASSERT_TRUE(HasPendingRequest) before that would h
Marc Treib
2017/05/15 14:25:22
Done.
| |
| 710 } | |
| 711 | |
| 712 TEST_F(DoodleServiceTest, GetImageFetchesCTAImage) { | |
| 713 service()->Refresh(); | |
| 714 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); | |
| 715 // Set a CTA image, which should take precedence over the regular image. | |
| 716 config.large_image.is_animated_gif = true; | |
| 717 config.large_cta_image = DoodleImage(GURL("https://doodle.com/cta.jpg")); | |
| 718 config.large_cta_image->is_cta = true; | |
| 719 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, | |
| 720 base::TimeDelta::FromHours(1), config); | |
| 721 ASSERT_THAT(service()->config(), Eq(config)); | |
| 722 | |
| 723 base::MockCallback<DoodleService::ImageCallback> callback; | |
| 724 service()->GetImage(callback.Get()); | |
| 725 | |
| 726 // If the doodle has a CTA image, that should loaded instead of the regular | |
| 727 // large image. | |
| 728 EXPECT_EQ(config.large_cta_image->url, image_fetcher()->pending_url()); | |
| 729 | |
| 730 EXPECT_CALL(callback, Run(Not(IsEmptyImage()))); | |
| 731 gfx::Image image = gfx::test::CreateImage(1, 1); | |
| 732 image_fetcher()->RespondToPendingRequest(image); | |
|
fhorschig
2017/05/15 13:54:35
again ASSERT_FALSE(HasPendingRequest)?
Marc Treib
2017/05/15 14:25:22
ASSERT_TRUE :) but done.
| |
| 733 } | |
| 734 | |
| 600 } // namespace doodle | 735 } // namespace doodle |
| OLD | NEW |