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 = default; |
| 77 |
| 78 void SetImageFetcherDelegate(image_fetcher::ImageFetcherDelegate*) override { |
| 79 NOTREACHED(); |
| 80 } |
| 81 |
| 82 void SetDataUseServiceName(DataUseServiceName) override { |
| 83 // Ignored. |
| 84 } |
| 85 |
| 86 void SetImageDownloadLimit(base::Optional<int64_t>) override { |
| 87 // Ignored. |
| 88 } |
| 89 |
| 90 void SetDesiredImageFrameSize(const gfx::Size&) override { |
| 91 // Ignored. |
| 92 } |
| 93 |
| 94 void StartOrQueueNetworkRequest( |
| 95 const std::string& id, |
| 96 const GURL& url, |
| 97 const ImageFetcherCallback& callback) override { |
| 98 // For simplicity, the fake doesn't support multiple concurrent requests. |
| 99 DCHECK(!HasPendingRequest()); |
| 100 |
| 101 pending_id_ = id; |
| 102 pending_url_ = url; |
| 103 pending_callback_ = callback; |
| 104 } |
| 105 |
| 106 image_fetcher::ImageDecoder* GetImageDecoder() override { |
| 107 NOTREACHED(); |
| 108 return nullptr; |
| 109 } |
| 110 |
| 111 bool HasPendingRequest() const { return !pending_callback_.is_null(); } |
| 112 |
| 113 const GURL& pending_url() const { return pending_url_; } |
| 114 |
| 115 void RespondToPendingRequest(const gfx::Image& image) { |
| 116 DCHECK(HasPendingRequest()); |
| 117 |
| 118 RequestMetadata metadata; |
| 119 metadata.http_response_code = 200; |
| 120 pending_callback_.Run(pending_id_, image, metadata); |
| 121 |
| 122 pending_id_.clear(); |
| 123 pending_url_ = GURL(); |
| 124 pending_callback_.Reset(); |
| 125 } |
| 126 |
| 127 private: |
| 128 std::string pending_id_; |
| 129 GURL pending_url_; |
| 130 ImageFetcherCallback pending_callback_; |
| 131 }; |
| 132 |
63 DoodleConfig CreateConfig(DoodleType type) { | 133 DoodleConfig CreateConfig(DoodleType type) { |
64 return DoodleConfig(type, DoodleImage(GURL("https://doodle.com/image.jpg"))); | 134 return DoodleConfig(type, DoodleImage(GURL("https://doodle.com/image.jpg"))); |
65 } | 135 } |
66 | 136 |
| 137 MATCHER(IsEmptyImage, "") { |
| 138 return arg.IsEmpty(); |
| 139 } |
| 140 |
67 } // namespace | 141 } // namespace |
68 | 142 |
69 class DoodleServiceTest : public testing::Test { | 143 class DoodleServiceTest : public testing::Test { |
70 public: | 144 public: |
71 DoodleServiceTest() | 145 DoodleServiceTest() |
72 : task_runner_(new base::TestMockTimeTaskRunner()), | 146 : task_runner_(new base::TestMockTimeTaskRunner()), |
73 task_runner_handle_(task_runner_), | 147 task_runner_handle_(task_runner_), |
74 tick_clock_(task_runner_->GetMockTickClock()), | 148 tick_clock_(task_runner_->GetMockTickClock()), |
75 fetcher_(nullptr), | 149 fetcher_(nullptr), |
76 expiry_timer_(nullptr) { | 150 expiry_timer_(nullptr) { |
77 DoodleService::RegisterProfilePrefs(pref_service_.registry()); | 151 DoodleService::RegisterProfilePrefs(pref_service_.registry()); |
78 | 152 |
79 task_runner_->FastForwardBy(base::TimeDelta::FromHours(12345)); | 153 task_runner_->FastForwardBy(base::TimeDelta::FromHours(12345)); |
80 | 154 |
81 // Set the minimum refresh interval to 0 by default, so tests don't have to | 155 // 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. | 156 // worry about it. The tests that care set it explicitly. |
83 RecreateServiceWithZeroRefreshInterval(); | 157 RecreateServiceWithZeroRefreshInterval(); |
84 } | 158 } |
85 | 159 |
86 void DestroyService() { service_ = nullptr; } | 160 void TearDown() override { DestroyService(); } |
| 161 |
| 162 void DestroyService() { |
| 163 if (image_fetcher_) { |
| 164 // Make sure we didn't receive an unexpected image request. |
| 165 ASSERT_FALSE(image_fetcher_->HasPendingRequest()); |
| 166 } |
| 167 |
| 168 fetcher_ = nullptr; |
| 169 expiry_timer_ = nullptr; |
| 170 image_fetcher_ = nullptr; |
| 171 |
| 172 service_ = nullptr; |
| 173 } |
87 | 174 |
88 void RecreateServiceWithZeroRefreshInterval() { | 175 void RecreateServiceWithZeroRefreshInterval() { |
89 RecreateService(/*min_refresh_interval=*/base::TimeDelta()); | 176 RecreateService(/*min_refresh_interval=*/base::TimeDelta()); |
90 } | 177 } |
91 | 178 |
92 void RecreateService(base::Optional<base::TimeDelta> refresh_interval) { | 179 void RecreateService(base::Optional<base::TimeDelta> refresh_interval) { |
93 auto expiry_timer = base::MakeUnique<base::OneShotTimer>(tick_clock_.get()); | 180 auto expiry_timer = base::MakeUnique<base::OneShotTimer>(tick_clock_.get()); |
94 expiry_timer->SetTaskRunner(task_runner_); | 181 expiry_timer->SetTaskRunner(task_runner_); |
95 expiry_timer_ = expiry_timer.get(); | 182 expiry_timer_ = expiry_timer.get(); |
96 | 183 |
97 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); | 184 auto fetcher = base::MakeUnique<FakeDoodleFetcher>(); |
98 fetcher_ = fetcher.get(); | 185 fetcher_ = fetcher.get(); |
99 | 186 |
| 187 auto image_fetcher = base::MakeUnique<FakeImageFetcher>(); |
| 188 image_fetcher_ = image_fetcher.get(); |
| 189 |
100 service_ = base::MakeUnique<DoodleService>( | 190 service_ = base::MakeUnique<DoodleService>( |
101 &pref_service_, std::move(fetcher), std::move(expiry_timer), | 191 &pref_service_, std::move(fetcher), std::move(expiry_timer), |
102 task_runner_->GetMockClock(), task_runner_->GetMockTickClock(), | 192 task_runner_->GetMockClock(), task_runner_->GetMockTickClock(), |
103 refresh_interval); | 193 refresh_interval, std::move(image_fetcher)); |
104 } | 194 } |
105 | 195 |
106 DoodleService* service() { return service_.get(); } | 196 DoodleService* service() { return service_.get(); } |
107 FakeDoodleFetcher* fetcher() { return fetcher_; } | 197 FakeDoodleFetcher* fetcher() { return fetcher_; } |
| 198 FakeImageFetcher* image_fetcher() { return image_fetcher_; } |
108 | 199 |
109 base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); } | 200 base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); } |
110 | 201 |
111 private: | 202 private: |
112 TestingPrefServiceSimple pref_service_; | 203 TestingPrefServiceSimple pref_service_; |
113 | 204 |
114 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; | 205 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; |
115 base::ThreadTaskRunnerHandle task_runner_handle_; | 206 base::ThreadTaskRunnerHandle task_runner_handle_; |
116 std::unique_ptr<base::TickClock> tick_clock_; | 207 std::unique_ptr<base::TickClock> tick_clock_; |
117 | 208 |
118 std::unique_ptr<DoodleService> service_; | 209 std::unique_ptr<DoodleService> service_; |
119 | 210 |
120 // Weak, owned by the service. | 211 // Weak, owned by the service. |
121 FakeDoodleFetcher* fetcher_; | 212 FakeDoodleFetcher* fetcher_; |
122 base::OneShotTimer* expiry_timer_; | 213 base::OneShotTimer* expiry_timer_; |
| 214 FakeImageFetcher* image_fetcher_; |
123 }; | 215 }; |
124 | 216 |
125 TEST_F(DoodleServiceTest, FetchesConfigOnRefresh) { | 217 TEST_F(DoodleServiceTest, FetchesConfigOnRefresh) { |
126 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 218 ASSERT_THAT(service()->config(), Eq(base::nullopt)); |
127 | 219 |
128 // Request a refresh of the doodle config. | 220 // Request a refresh of the doodle config. |
129 service()->Refresh(); | 221 service()->Refresh(); |
130 // The request should have arrived at the fetcher. | 222 // The request should have arrived at the fetcher. |
131 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); | 223 EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u)); |
132 | 224 |
(...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
590 | 682 |
591 // Fast-forward time so that the config expires. | 683 // Fast-forward time so that the config expires. |
592 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); | 684 task_runner()->FastForwardBy(base::TimeDelta::FromHours(1)); |
593 ASSERT_THAT(service()->config(), Eq(base::nullopt)); | 685 ASSERT_THAT(service()->config(), Eq(base::nullopt)); |
594 | 686 |
595 // This should not have resulted in any metrics being emitted. | 687 // This should not have resulted in any metrics being emitted. |
596 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); | 688 histograms.ExpectTotalCount("Doodle.ConfigDownloadOutcome", 0); |
597 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); | 689 histograms.ExpectTotalCount("Doodle.ConfigDownloadTime", 0); |
598 } | 690 } |
599 | 691 |
| 692 TEST_F(DoodleServiceTest, GetImageWithEmptyConfigReturnsImmediately) { |
| 693 ASSERT_THAT(service()->config(), Eq(base::nullopt)); |
| 694 |
| 695 base::MockCallback<DoodleService::ImageCallback> callback; |
| 696 EXPECT_CALL(callback, Run(IsEmptyImage())); |
| 697 |
| 698 service()->GetImage(callback.Get()); |
| 699 } |
| 700 |
| 701 TEST_F(DoodleServiceTest, GetImageFetchesLargeImage) { |
| 702 service()->Refresh(); |
| 703 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); |
| 704 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, |
| 705 base::TimeDelta::FromHours(1), config); |
| 706 ASSERT_THAT(service()->config(), Eq(config)); |
| 707 |
| 708 base::MockCallback<DoodleService::ImageCallback> callback; |
| 709 service()->GetImage(callback.Get()); |
| 710 |
| 711 EXPECT_EQ(config.large_image.url, image_fetcher()->pending_url()); |
| 712 |
| 713 EXPECT_CALL(callback, Run(Not(IsEmptyImage()))); |
| 714 gfx::Image image = gfx::test::CreateImage(1, 1); |
| 715 ASSERT_TRUE(image_fetcher()->HasPendingRequest()); |
| 716 image_fetcher()->RespondToPendingRequest(image); |
| 717 } |
| 718 |
| 719 TEST_F(DoodleServiceTest, GetImageFetchesCTAImage) { |
| 720 service()->Refresh(); |
| 721 DoodleConfig config = CreateConfig(DoodleType::SIMPLE); |
| 722 // Set a CTA image, which should take precedence over the regular image. |
| 723 config.large_image.is_animated_gif = true; |
| 724 config.large_cta_image = DoodleImage(GURL("https://doodle.com/cta.jpg")); |
| 725 config.large_cta_image->is_cta = true; |
| 726 fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, |
| 727 base::TimeDelta::FromHours(1), config); |
| 728 ASSERT_THAT(service()->config(), Eq(config)); |
| 729 |
| 730 base::MockCallback<DoodleService::ImageCallback> callback; |
| 731 service()->GetImage(callback.Get()); |
| 732 |
| 733 // If the doodle has a CTA image, that should loaded instead of the regular |
| 734 // large image. |
| 735 EXPECT_EQ(config.large_cta_image->url, image_fetcher()->pending_url()); |
| 736 |
| 737 EXPECT_CALL(callback, Run(Not(IsEmptyImage()))); |
| 738 gfx::Image image = gfx::test::CreateImage(1, 1); |
| 739 ASSERT_TRUE(image_fetcher()->HasPendingRequest()); |
| 740 image_fetcher()->RespondToPendingRequest(image); |
| 741 } |
| 742 |
600 } // namespace doodle | 743 } // namespace doodle |
OLD | NEW |