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

Side by Side Diff: components/doodle/doodle_fetcher_unittest.cc

Issue 2660883002: Introduce a Doodle Fetcher for NTP (Closed)
Patch Set: Change interface for |FetchDoodle| 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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/doodle/doodle_fetcher.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/json/json_reader.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/test/test_mock_time_task_runner.h"
14 #include "base/threading/sequenced_task_runner_handle.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "base/values.h"
17 #include "components/google/core/browser/google_url_tracker.h"
18 #include "net/http/http_status_code.h"
19 #include "net/url_request/test_url_fetcher_factory.h"
20 #include "net/url_request/url_request_status.h"
21 #include "net/url_request/url_request_test_util.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 using testing::Eq;
26 using testing::IsEmpty;
27
28 namespace doodle {
29
30 namespace {
31 const char kSampleResponse[] = R"json()]}'{
Marc Treib 2017/02/01 11:53:14 nitty nit: I'd insert a newline after R"json( to m
Marc Treib 2017/02/01 14:08:05 Hm, on second thought: That would put an extra new
fhorschig 2017/02/03 13:21:05 I guess it would be weird to add code that just un
Marc Treib 2017/02/03 15:45:48 Well, we could decide that it's okay if the respon
fhorschig 2017/02/08 09:10:23 Acknowledged.
32 "ddljson": {
33 "alt_text":"Mouseover Text",
34 "doodle_type":"SIMPLE",
35 "id":0,
36 "search_url":"/search?q\u003dtest",
37 "share_text":"Share Text #GoogleDoodle\nhttps://g.co/doodle/8hfqzq",
38 "short_link":"//g.co/doodle/8hfqzq",
39 "show_now_header_search_affordance":false,
40 "show_now_header_share_button":true,
41 "target_url":"/search?q\u003dtest\u0026sa\u003dX\u0026ved\u003d0ahUKEwjm",
42 "time_to_live_ms":514220215000,
43 "large_image": {
44 "background_color":"#ffffff",
45 "height":225,
46 "image_id":0,
47 "is_animated_gif":true,
48 "is_cta":false,
49 "slot":1,
50 "url":"/logos/doodles/2015/new-years-eve-2015-5985438795825152-hp.gif",
51 "width":489
52 },
53 "large_cta_image": {
54 "background_color":"#ffffff",
55 "height":225,
56 "image_id":0,
57 "is_animated_gif":true,
58 "is_cta":true,
59 "slot":8,
60 "url":"/logos/doodles/2015/new-years-eve-2015-5985438795825152-cta.gif",
61 "width":489
62 },
63 "transparent_large_image": {
64 "background_color":"",
65 "height":225,
66 "image_id":0,
67 "is_animated_gif":false,
68 "is_cta":false,
69 "slot":2,
70 "url":"/logos/doodles/2015/new-years-eve-2015-5985438795825152-thp.png",
71 "width":510
72 }
73 }})json";
Marc Treib 2017/02/01 11:53:14 similar here: newline after the {{
fhorschig 2017/02/03 13:21:05 Done.
74
75 const char kBaseUrl[] = "https://www.google.com/";
76
77 std::string AbsoluteUrl(const std::string& relative_url) {
Marc Treib 2017/02/01 11:53:14 MakeAbsoluteUrl, or just Resolve?
fhorschig 2017/02/03 13:21:05 I am ashamed. Changed.
78 return GURL(kBaseUrl).Resolve(relative_url).spec();
79 }
80
81 void ParseJson(
82 const std::string& json,
83 const base::Callback<void(std::unique_ptr<base::Value> json)>& success,
84 const base::Callback<void(const std::string&)>& error) {
85 base::JSONReader json_reader;
86 std::unique_ptr<base::Value> value = json_reader.ReadToValue(json);
87 if (value) {
88 success.Run(std::move(value));
89 } else {
90 error.Run(json_reader.GetErrorMessage());
91 }
92 }
93
94 } // namespace
95
96 class DoodleFetcherTest : public ::testing::Test {
97 public:
98 DoodleFetcherTest()
99 : url_(GURL(kBaseUrl).Resolve(kDoodleConfigPath)),
100 url_fetcher_factory_(nullptr),
Marc Treib 2017/02/01 11:53:14 Add a comment on what the nullptr is? (I first int
fhorschig 2017/02/03 13:21:05 Comment added. I will probably use a failing fake
Marc Treib 2017/02/03 15:45:48 FWIW, there's two different versions of URLFetcher
fhorschig 2017/02/08 09:10:23 Done.
101 mock_task_runner_(new base::TestMockTimeTaskRunner()),
Marc Treib 2017/02/01 11:53:14 Hm, I'm not very familiar with this, but it looks
fhorschig 2017/02/03 13:21:05 The URLFetcher posts tasks and needs a context. Th
Marc Treib 2017/02/03 15:45:48 Couldn't you just instantiate a SimpleTestClock yo
fhorschig 2017/02/08 09:10:23 As discussed, tests redesigned to use TestUrlFetch
102 mock_task_runner_handle_(mock_task_runner_),
103 context_getter(
104 new net::TestURLRequestContextGetter(mock_task_runner_.get())) {
105 // Random difference to 0 ensures that expiry_dates are really relative.
106 mock_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(80082));
107 }
108
109 void RespondWithData(const std::string& data) {
Marc Treib 2017/02/01 11:53:14 nit: Maybe SetResponse or something like that? IMO
fhorschig 2017/02/03 13:21:05 Done.
110 url_fetcher_factory_.SetFakeResponse(url_, data, net::HTTP_OK,
111 net::URLRequestStatus::SUCCESS);
112 }
113
114 void RespondWithError() {
115 url_fetcher_factory_.SetFakeResponse(url_, "", net::HTTP_NOT_FOUND,
116 net::URLRequestStatus::FAILED);
117 }
118
119 DoodleState FetchDoodle(base::Optional<DoodleConfig>* config) {
120 DoodleState state;
121 std::unique_ptr<DoodleFetcher> fetcher = CreateDoodleFetcher();
122 TriggerFetch(fetcher.get(), &state, config);
123 WaitForCallbacksToReturn();
124 return state;
125 }
126
127 std::unique_ptr<DoodleFetcher> CreateDoodleFetcher() {
128 // TODO(fhorschig): Create Fake for GoogleUrlTracker.
129 std::unique_ptr<DoodleFetcher> fetcher = base::MakeUnique<DoodleFetcher>(
130 context_getter.get(),
131 /*google_url_tracker=*/nullptr, base::Bind(ParseJson));
132 fetcher->SetClockForTesting(mock_task_runner_->GetMockClock());
133 return fetcher;
134 }
135
136 void TriggerFetch(DoodleFetcher* fetcher,
137 DoodleState* state,
138 base::Optional<DoodleConfig>* config) {
139 fetcher->FetchDoodle(base::BindOnce(
140 [](DoodleState* state_out, base::Optional<DoodleConfig>* config_out,
141 DoodleState state, base::Optional<DoodleConfig> config) {
142 *state_out = std::move(state);
143 *config_out = std::move(config);
144 },
145 state, config));
146 }
147
148 base::Time TimeFromNow(uint64_t milliseconds) {
149 return mock_task_runner_->GetMockClock()->Now() +
150 base::TimeDelta::FromMilliseconds(milliseconds);
151 }
152
153 void WaitForCallbacksToReturn() {
154 mock_task_runner_->FastForwardUntilNoTasksRemain();
155 }
156
157 size_t NumberOfPendingTasks() {
158 return mock_task_runner_->GetPendingTaskCount();
159 }
160
161 private:
162 GURL url_;
163 net::FakeURLFetcherFactory url_fetcher_factory_;
164 scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
165 base::ThreadTaskRunnerHandle mock_task_runner_handle_;
Marc Treib 2017/02/01 11:53:14 Is this needed?
fhorschig 2017/02/03 13:21:05 Yes. (Its constructor replace the base::ThreadTask
166 scoped_refptr<net::TestURLRequestContextGetter> context_getter;
167 };
168
169 TEST_F(DoodleFetcherTest, ReturnsFromFetchWithoutError) {
170 RespondWithData(kSampleResponse);
171
172 base::Optional<DoodleConfig> response;
173 EXPECT_THAT(FetchDoodle(&response), Eq(DoodleState::AVAILABLE));
174 EXPECT_TRUE(response.has_value());
175 }
176
177 TEST_F(DoodleFetcherTest, ReturnsFrom404FetchWithError) {
178 RespondWithError();
179
180 base::Optional<DoodleConfig> response;
181 EXPECT_THAT(FetchDoodle(&response), Eq(DoodleState::DOWNLOAD_ERROR));
182 EXPECT_FALSE(response.has_value());
183 }
184
185 TEST_F(DoodleFetcherTest, ReturnsErrorForInvalidJson) {
186 RespondWithData("{}");
187
188 base::Optional<DoodleConfig> response;
189 EXPECT_THAT(FetchDoodle(&response), Eq(DoodleState::PARSING_ERROR));
190 EXPECT_FALSE(response.has_value());
191 }
192
193 TEST_F(DoodleFetcherTest, ResponseContainsValidBaseInformation) {
194 RespondWithData(kSampleResponse);
195
196 base::Optional<DoodleConfig> response;
197 EXPECT_THAT(FetchDoodle(&response), Eq(DoodleState::AVAILABLE));
198 ASSERT_TRUE(response.has_value());
199 DoodleConfig config = response.value();
200
201 EXPECT_TRUE(config.search_url.is_valid());
202 EXPECT_THAT(config.search_url, Eq(AbsoluteUrl("/search?q\u003dtest")));
203 EXPECT_THAT(config.fullpage_interactive_url, Eq(GURL()));
204 EXPECT_TRUE(config.target_url.is_valid());
205 EXPECT_THAT(config.target_url,
206 Eq(AbsoluteUrl("/search?q\u003dtest\u0026sa\u003dX\u0026ved\u003d"
207 "0ahUKEwjm")));
208 EXPECT_THAT(config.doodle_type, Eq(DoodleType::SIMPLE));
209 EXPECT_THAT(config.alt_text, Eq("Mouseover Text"));
210
211 EXPECT_THAT(config.expiry_date, Eq(TimeFromNow(514220215000)));
212 }
213
214 TEST_F(DoodleFetcherTest, EmptyResponsesCausesNoDoodleState) {
215 RespondWithData("{\"ddljson\":{}}");
216
217 base::Optional<DoodleConfig> response;
218 EXPECT_THAT(FetchDoodle(&response), Eq(DoodleState::NO_DOODLE));
219 EXPECT_FALSE(response.has_value());
220 }
221
222 TEST_F(DoodleFetcherTest, ResponseContainsExactlyTheSampleImages) {
223 RespondWithData(kSampleResponse);
224
225 base::Optional<DoodleConfig> response;
226 EXPECT_THAT(FetchDoodle(&response), Eq(DoodleState::AVAILABLE));
227 ASSERT_TRUE(response.has_value());
228 DoodleConfig config = response.value();
229
230 EXPECT_TRUE(config.transparent_large_image.url.is_valid());
231 EXPECT_THAT(config.transparent_large_image.url.spec(),
232 Eq(AbsoluteUrl("/logos/doodles/2015/new-years-eve-2015-5985438795"
233 "825152-thp.png")));
234 EXPECT_THAT(config.transparent_large_image.width, Eq(510));
235 EXPECT_THAT(config.transparent_large_image.height, Eq(225));
236 EXPECT_FALSE(config.transparent_large_image.is_animated_gif);
237 EXPECT_FALSE(config.transparent_large_image.is_cta);
238
239 EXPECT_TRUE(config.large_image.url.is_valid());
240 EXPECT_THAT(config.large_image.url.spec(),
241 Eq(AbsoluteUrl("/logos/doodles/2015/new-years-eve-2015-5985438795"
242 "825152-hp.gif")));
243 EXPECT_THAT(config.large_image.width, Eq(489));
244 EXPECT_THAT(config.large_image.height, Eq(225));
245 EXPECT_TRUE(config.large_image.is_animated_gif);
246 EXPECT_FALSE(config.large_image.is_cta);
247
248 EXPECT_TRUE(config.large_cta_image.url.is_valid());
249 EXPECT_THAT(config.large_cta_image.url.spec(),
250 Eq(AbsoluteUrl("/logos/doodles/2015/new-years-eve-2015-5985438795"
251 "825152-cta.gif")));
252 EXPECT_THAT(config.large_cta_image.width, Eq(489));
253 EXPECT_THAT(config.large_cta_image.height, Eq(225));
254 EXPECT_TRUE(config.large_cta_image.is_animated_gif);
255 EXPECT_TRUE(config.large_cta_image.is_cta);
256 }
257
258 TEST_F(DoodleFetcherTest, RespondsToMultipleRequestsWithSameResponse) {
259 RespondWithData(kSampleResponse);
260
261 DoodleState state1;
262 DoodleState state2;
263 base::Optional<DoodleConfig> response1;
264 base::Optional<DoodleConfig> response2;
265 std::unique_ptr<DoodleFetcher> fetcher = CreateDoodleFetcher();
266
267 TriggerFetch(fetcher.get(), &state1, &response1);
268 TriggerFetch(fetcher.get(), &state2, &response2);
269
270 EXPECT_THAT(NumberOfPendingTasks(), Eq(1ul));
271 WaitForCallbacksToReturn();
272
273 EXPECT_THAT(state1, Eq(DoodleState::AVAILABLE));
274 EXPECT_THAT(state2, Eq(DoodleState::AVAILABLE));
275 EXPECT_TRUE(response1.has_value());
276 EXPECT_TRUE(response2.has_value());
277 }
278
279 } // namespace doodle
OLDNEW
« components/doodle/doodle_fetcher.cc ('K') | « components/doodle/doodle_fetcher.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698