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

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

Issue 2660883002: Introduce a Doodle Fetcher for NTP (Closed)
Patch Set: Set TTL to 30 days 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
« no previous file with comments | « components/doodle/doodle_fetcher.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/command_line.h"
12 #include "base/json/json_reader.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/test/simple_test_clock.h"
16 #include "base/values.h"
17 #include "components/google/core/browser/google_switches.h"
18 #include "components/google/core/browser/google_url_tracker.h"
19 #include "net/http/http_status_code.h"
20 #include "net/url_request/test_url_fetcher_factory.h"
21 #include "net/url_request/url_request_status.h"
22 #include "net/url_request/url_request_test_util.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 using testing::Eq;
27
28 namespace doodle {
29
30 namespace {
31
32 const char kDoodleConfigPath[] = "/async/ddljson";
33
34 const char kSampleResponse[] = R"json()]}'{
35 "ddljson": {
36 "alt_text":"Mouseover Text",
37 "doodle_type":"SIMPLE",
38 "id":0,
39 "interactive_html":"\u003cstyle\u003e\u003c\/style\u003e",
40 "interactive_js":"(function(){}).call(this);",
41 "search_url":"/search?q\u003dtest",
42 "share_text":"Share Text #GoogleDoodle\nhttps://g.co/doodle/8hfqzq",
43 "short_link":"//g.co/doodle/8hfqzq",
44 "show_now_header_search_affordance":false,
45 "show_now_header_share_button":true,
46 "target_url":"/search?q\u003dtest\u0026sa\u003dX\u0026ved\u003d0ahUKEwjm",
47 "time_to_live_ms":55000,
48 "large_image": {
49 "background_color":"#ffffff",
50 "height":225,
51 "image_id":0,
52 "is_animated_gif":true,
53 "is_cta":false,
54 "slot":1,
55 "url":"/logos/doodles/2015/new-years-eve-2015-5985438795825152-hp.gif",
56 "width":489
57 },
58 "large_cta_image": {
59 "background_color":"#ffffff",
60 "height":225,
61 "image_id":0,
62 "is_animated_gif":true,
63 "is_cta":true,
64 "slot":8,
65 "url":"/logos/doodles/2015/new-years-eve-2015-5985438795825152-cta.gif",
66 "width":489
67 },
68 "transparent_large_image": {
69 "background_color":"",
70 "height":225,
71 "image_id":0,
72 "is_animated_gif":false,
73 "is_cta":false,
74 "slot":2,
75 "url":"/logos/doodles/2015/new-years-eve-2015-5985438795825152-thp.png",
76 "width":510
77 }
78 }})json";
79
80 const char kMinimalAvailableDoodleResponse[] = R"json({"ddljson": {
81 "large_image": {
82 "url":"/logos/doodles/2015/new-years-eve-2015-5985438795825152-hp.gif"
83 }}})json";
84
85 const char kMalformedImageUrlResponse[] = R"json({"ddljson": {
86 "time_to_live_ms":55000,
87 "large_image": {
88 "height":225,
89 "width":489
90 }}})json";
91
92 // Required to instantiate a GoogleUrlTracker in UNIT_TEST_MODE.
93 class GoogleURLTrackerClientStub : public GoogleURLTrackerClient {
94 public:
95 GoogleURLTrackerClientStub() {}
96 ~GoogleURLTrackerClientStub() override {}
97
98 bool IsBackgroundNetworkingEnabled() override { return true; }
99
100 PrefService* GetPrefs() override { return nullptr; }
101
102 net::URLRequestContextGetter* GetRequestContext() override { return nullptr; }
103
104 private:
105 DISALLOW_COPY_AND_ASSIGN(GoogleURLTrackerClientStub);
106 };
107
108 std::string Resolve(const std::string& relative_url) {
109 return GURL(GoogleURLTracker::kDefaultGoogleHomepage)
110 .Resolve(relative_url)
111 .spec();
112 }
113
114 void ParseJson(
115 const std::string& json,
116 const base::Callback<void(std::unique_ptr<base::Value> json)>& success,
117 const base::Callback<void(const std::string&)>& error) {
118 base::JSONReader json_reader;
119 std::unique_ptr<base::Value> value = json_reader.ReadToValue(json);
120 if (value) {
121 success.Run(std::move(value));
122 } else {
123 error.Run(json_reader.GetErrorMessage());
124 }
125 }
126
127 } // namespace
128
129 class DoodleFetcherTest : public testing::Test {
130 public:
131 DoodleFetcherTest()
132 : url_(GURL(GoogleURLTracker::kDefaultGoogleHomepage)),
133 context_getter(
134 new net::TestURLRequestContextGetter(message_loop_.task_runner())),
135 google_url_tracker_(base::MakeUnique<GoogleURLTrackerClientStub>(),
136 GoogleURLTracker::UNIT_TEST_MODE),
137 doodle_fetcher_(context_getter.get(),
138 &google_url_tracker_,
139 base::Bind(ParseJson)) {
140 // Random difference to 0 ensures that expiry_dates are really relative.
Marc Treib 2017/02/09 10:36:25 s/ensures/ensure/
fhorschig 2017/02/09 13:23:56 Done.
141 auto clock = base::MakeUnique<base::SimpleTestClock>();
142 clock_ = clock.get();
143 clock_->Advance(base::TimeDelta::FromMilliseconds(1123581321));
144 doodle_fetcher_.SetClockForTesting(std::move(clock));
145 }
146
147 void RespondWithData(const std::string& data) {
148 RespondToFetcherWithData(GetRunningFetcher(), data);
149 }
150
151 void RespondToFetcherWithData(net::TestURLFetcher* url_fetcher,
152 const std::string& data) {
153 url_fetcher->set_status(net::URLRequestStatus());
154 url_fetcher->set_response_code(net::HTTP_OK);
155 url_fetcher->SetResponseString(data);
156 // Call the URLFetcher delegate to continue the test.
157 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
158 }
159
160 void RespondWithError(int error_code) {
161 net::TestURLFetcher* url_fetcher = GetRunningFetcher();
162 url_fetcher->set_status(net::URLRequestStatus::FromError(error_code));
163 url_fetcher->SetResponseString("");
164 // Call the URLFetcher delegate to continue the test.
165 url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
166 }
167
168 net::TestURLFetcher* GetRunningFetcher() {
169 // All created TestURLFetchers have ID 0 by default.
170 net::TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
171 DCHECK(url_fetcher);
172 return url_fetcher;
173 }
174
175 DoodleFetcher::FinishedCallback CreateResponseSavingCallback(
176 DoodleState* state_out,
177 base::Optional<DoodleConfig>* config_out) {
178 return base::BindOnce(
179 [](DoodleState* state_out, base::Optional<DoodleConfig>* config_out,
180 DoodleState state, const base::Optional<DoodleConfig>& config) {
181 if (state_out) {
182 *state_out = state;
183 }
184 if (config_out) {
185 *config_out = config;
186 }
187 },
188 state_out, config_out);
189 }
190
191 DoodleFetcher* doodle_fetcher() { return &doodle_fetcher_; }
192
193 GURL GoogleTrackerBaseUrl() { return GURL(google_url_tracker_.google_url()); }
Marc Treib 2017/02/09 10:36:24 GetGoogleBaseURL? Also .google_url() is already a
fhorschig 2017/02/09 13:23:56 Thanks.
194
195 base::Time TimeFromNow(uint64_t milliseconds) {
196 return clock_->Now() + base::TimeDelta::FromMilliseconds(milliseconds);
197 }
198
199 private:
200 base::MessageLoop message_loop_;
201 GURL url_;
202 net::TestURLFetcherFactory url_fetcher_factory_;
203 base::SimpleTestClock* clock_; // Owned by the doodle_fetcher.
204 scoped_refptr<net::TestURLRequestContextGetter> context_getter;
205 GoogleURLTracker google_url_tracker_;
206 DoodleFetcher doodle_fetcher_;
207 };
208
209 TEST_F(DoodleFetcherTest, ReturnsFromFetchWithoutError) {
210 DoodleState state(DoodleState::NO_DOODLE);
211 base::Optional<DoodleConfig> response;
212
213 doodle_fetcher()->FetchDoodle(
214 CreateResponseSavingCallback(&state, &response));
215 RespondWithData(kSampleResponse);
216
217 EXPECT_THAT(state, Eq(DoodleState::AVAILABLE));
218 EXPECT_TRUE(response.has_value());
219 }
220
221 TEST_F(DoodleFetcherTest, ReturnsFrom404FetchWithError) {
222 DoodleState state(DoodleState::NO_DOODLE);
223 base::Optional<DoodleConfig> response;
224
225 doodle_fetcher()->FetchDoodle(
226 CreateResponseSavingCallback(&state, &response));
227 RespondWithError(net::ERR_FILE_NOT_FOUND);
228
229 EXPECT_THAT(state, Eq(DoodleState::DOWNLOAD_ERROR));
230 EXPECT_FALSE(response.has_value());
231 }
232
233 TEST_F(DoodleFetcherTest, ReturnsErrorForInvalidJson) {
234 DoodleState state(DoodleState::NO_DOODLE);
235 base::Optional<DoodleConfig> response;
236
237 doodle_fetcher()->FetchDoodle(
238 CreateResponseSavingCallback(&state, &response));
239 RespondWithData("{}");
Marc Treib 2017/02/09 10:36:24 nit: I think this is technically valid json :D
fhorschig 2017/02/09 13:23:56 Invalid with regards to our specification ;) You
240
241 EXPECT_THAT(state, Eq(DoodleState::PARSING_ERROR));
242 EXPECT_FALSE(response.has_value());
243 }
244
245 TEST_F(DoodleFetcherTest, ResponseContainsValidBaseInformation) {
246 DoodleState state(DoodleState::NO_DOODLE);
247 base::Optional<DoodleConfig> response;
248
249 doodle_fetcher()->FetchDoodle(
250 CreateResponseSavingCallback(&state, &response));
251 RespondWithData(kSampleResponse);
252
253 EXPECT_THAT(state, Eq(DoodleState::AVAILABLE));
254 ASSERT_TRUE(response.has_value());
255 DoodleConfig config = response.value();
256
257 EXPECT_TRUE(config.search_url.is_valid());
258 EXPECT_THAT(config.search_url, Eq(Resolve("/search?q\u003dtest")));
Marc Treib 2017/02/09 10:36:24 This is another case where the test "input" (kSamp
fhorschig 2017/02/09 13:23:56 Cropped JSON strings inlined. They are sufficientl
259 EXPECT_TRUE(config.fullpage_interactive_url.is_empty());
260 EXPECT_TRUE(config.target_url.is_valid());
261 EXPECT_THAT(config.target_url,
262 Eq(Resolve("/search?q\u003dtest\u0026sa\u003dX\u0026ved\u003d"
263 "0ahUKEwjm")));
264 EXPECT_THAT(config.doodle_type, Eq(DoodleType::SIMPLE));
265 EXPECT_THAT(config.alt_text, Eq("Mouseover Text"));
266 EXPECT_THAT(config.interactive_html,
267 Eq("\u003cstyle\u003e\u003c/style\u003e"));
268 EXPECT_THAT(config.interactive_js, Eq("(function(){}).call(this);"));
269
270 EXPECT_THAT(config.expiry_date, Eq(TimeFromNow(55000)));
271 }
272
273 TEST_F(DoodleFetcherTest, ResponseContainsExpiresWithinThirtyDays) {
Marc Treib 2017/02/09 10:36:24 I don't understand the test name
fhorschig 2017/02/09 13:23:56 Me neither. Renamed.
274 DoodleState state(DoodleState::NO_DOODLE);
275 base::Optional<DoodleConfig> response;
276
277 doodle_fetcher()->FetchDoodle(
278 CreateResponseSavingCallback(&state, &response));
279 RespondWithData(R"json({"ddljson": {
280 "time_to_live_ms":5184000000,
281 "large_image": {"url":"/logos/doodles/2015/some.gif"}
282 }})json"); // 60 days
283
284 EXPECT_THAT(state, Eq(DoodleState::AVAILABLE));
285 ASSERT_TRUE(response.has_value());
286 EXPECT_THAT(response.value().expiry_date,
287 Eq(TimeFromNow(30ul * 24 * 60 * 60 * 1000 /* ms */))); // 30 days
288 }
289
290 TEST_F(DoodleFetcherTest, AvailableDoodleExpiresNowWithNegativeTTL) {
291 DoodleState state(DoodleState::NO_DOODLE);
292 base::Optional<DoodleConfig> response;
293
294 doodle_fetcher()->FetchDoodle(
295 CreateResponseSavingCallback(&state, &response));
296 RespondWithData(R"json({"ddljson": {
297 "time_to_live_ms":-1,
298 "large_image": {"url":"/logos/doodles/2015/some.gif"}
299 }})json");
300
301 EXPECT_THAT(state, Eq(DoodleState::AVAILABLE));
302 ASSERT_TRUE(response.has_value());
303 EXPECT_THAT(response.value().expiry_date, Eq(TimeFromNow(0)));
304 }
305
306 TEST_F(DoodleFetcherTest, AvailableDoodleExpiresNowWithoutValidTTL) {
307 DoodleState state(DoodleState::NO_DOODLE);
308 base::Optional<DoodleConfig> response;
309
310 doodle_fetcher()->FetchDoodle(
311 CreateResponseSavingCallback(&state, &response));
312 RespondWithData(kMinimalAvailableDoodleResponse);
313
314 EXPECT_THAT(state, Eq(DoodleState::AVAILABLE));
315 ASSERT_TRUE(response.has_value());
316 EXPECT_THAT(response.value().expiry_date, Eq(TimeFromNow(0)));
317 }
318
319 TEST_F(DoodleFetcherTest, ExpectToReturnNoDoodleForMalfomedImageUrls) {
Marc Treib 2017/02/09 10:36:24 ReturnsNoDoodleForMalfomedImageUrls ?
fhorschig 2017/02/09 13:23:56 Done.
320 DoodleState state(DoodleState::AVAILABLE);
321 base::Optional<DoodleConfig> response;
322
323 doodle_fetcher()->FetchDoodle(
324 CreateResponseSavingCallback(&state, &response));
325 RespondWithData(kMalformedImageUrlResponse);
326
327 EXPECT_THAT(state, Eq(DoodleState::NO_DOODLE));
328 EXPECT_FALSE(response.has_value());
329 }
330
331 TEST_F(DoodleFetcherTest, EmptyResponsesCausesNoDoodleState) {
332 DoodleState state(DoodleState::AVAILABLE);
333 base::Optional<DoodleConfig> response;
334
335 doodle_fetcher()->FetchDoodle(
336 CreateResponseSavingCallback(&state, &response));
337 RespondWithData("{\"ddljson\":{}}");
338
339 EXPECT_THAT(state, Eq(DoodleState::NO_DOODLE));
340 EXPECT_FALSE(response.has_value());
341 }
342
343 TEST_F(DoodleFetcherTest, ResponseContainsExactlyTheSampleImages) {
344 DoodleState state(DoodleState::NO_DOODLE);
345 base::Optional<DoodleConfig> response;
346
347 doodle_fetcher()->FetchDoodle(
348 CreateResponseSavingCallback(&state, &response));
349 RespondWithData(kSampleResponse);
350
351 EXPECT_THAT(state, Eq(DoodleState::AVAILABLE));
352 ASSERT_TRUE(response.has_value());
353 DoodleConfig config = response.value();
354
355 EXPECT_TRUE(config.transparent_large_image.url.is_valid());
356 EXPECT_THAT(config.transparent_large_image.url.spec(),
357 Eq(Resolve("/logos/doodles/2015/new-years-eve-2015-5985438795"
358 "825152-thp.png")));
359 EXPECT_THAT(config.transparent_large_image.width, Eq(510));
360 EXPECT_THAT(config.transparent_large_image.height, Eq(225));
361 EXPECT_FALSE(config.transparent_large_image.is_animated_gif);
362 EXPECT_FALSE(config.transparent_large_image.is_cta);
363
364 EXPECT_TRUE(config.large_image.url.is_valid());
365 EXPECT_THAT(config.large_image.url.spec(),
366 Eq(Resolve("/logos/doodles/2015/new-years-eve-2015-5985438795"
367 "825152-hp.gif")));
368 EXPECT_THAT(config.large_image.width, Eq(489));
369 EXPECT_THAT(config.large_image.height, Eq(225));
370 EXPECT_TRUE(config.large_image.is_animated_gif);
371 EXPECT_FALSE(config.large_image.is_cta);
372
373 EXPECT_TRUE(config.large_cta_image.url.is_valid());
374 EXPECT_THAT(config.large_cta_image.url.spec(),
375 Eq(Resolve("/logos/doodles/2015/new-years-eve-2015-5985438795"
376 "825152-cta.gif")));
377 EXPECT_THAT(config.large_cta_image.width, Eq(489));
378 EXPECT_THAT(config.large_cta_image.height, Eq(225));
379 EXPECT_TRUE(config.large_cta_image.is_animated_gif);
380 EXPECT_TRUE(config.large_cta_image.is_cta);
381 }
382
383 TEST_F(DoodleFetcherTest, RespondsToMultipleRequestsWithSameFetcher) {
384 doodle_fetcher()->FetchDoodle(
385 CreateResponseSavingCallback(/*state=*/nullptr, /*response=*/nullptr));
386
387 net::URLFetcher* first_created_fetcher = GetRunningFetcher();
388
389 doodle_fetcher()->FetchDoodle(
390 CreateResponseSavingCallback(/*state=*/nullptr, /*response=*/nullptr));
391
392 net::URLFetcher* second_created_fetcher = GetRunningFetcher();
393
394 EXPECT_THAT(first_created_fetcher, Eq(second_created_fetcher));
Marc Treib 2017/02/09 10:36:24 Maybe add a test that both callbacks get called?
fhorschig 2017/02/09 13:23:56 Done.
395 }
396
397 TEST_F(DoodleFetcherTest, ReceivesBaseUrlFromTracker) {
398 doodle_fetcher()->FetchDoodle(
399 CreateResponseSavingCallback(/*state=*/nullptr, /*response=*/nullptr));
400
401 EXPECT_THAT(GetRunningFetcher()->GetOriginalURL(),
402 Eq(GoogleTrackerBaseUrl().Resolve(kDoodleConfigPath)));
403 }
404
405 TEST_F(DoodleFetcherTest, OverridesBaseUrlWithCommandLineArgument) {
406 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
407 switches::kGoogleBaseURL, "http://www.google.kz");
408
409 doodle_fetcher()->FetchDoodle(
410 CreateResponseSavingCallback(/*state=*/nullptr, /*response=*/nullptr));
411
412 EXPECT_THAT(GetRunningFetcher()->GetOriginalURL(),
413 Eq(GURL("http://www.google.kz").Resolve(kDoodleConfigPath)));
414 }
415
416 } // namespace doodle
OLDNEW
« no previous file with comments | « components/doodle/doodle_fetcher.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698