Don't preload rarely seen large images
[chromium-blink-merge.git] / components / search_provider_logos / logo_tracker_unittest.cc
blobe84994b9ccf5d4c0de6e3b1359a7d80bee9cb12e
1 // Copyright 2014 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.
5 #include "components/search_provider_logos/logo_tracker.h"
7 #include <stdint.h>
8 #include <vector>
10 #include "base/base64.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/files/file_path.h"
14 #include "base/json/json_writer.h"
15 #include "base/location.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/scoped_vector.h"
18 #include "base/run_loop.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/string_piece.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/test/simple_test_clock.h"
23 #include "base/thread_task_runner_handle.h"
24 #include "base/time/time.h"
25 #include "base/values.h"
26 #include "components/search_provider_logos/google_logo_api.h"
27 #include "net/base/url_util.h"
28 #include "net/http/http_response_headers.h"
29 #include "net/http/http_status_code.h"
30 #include "net/url_request/test_url_fetcher_factory.h"
31 #include "net/url_request/url_request_status.h"
32 #include "net/url_request/url_request_test_util.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "ui/gfx/image/image.h"
37 using ::testing::_;
38 using ::testing::AnyNumber;
39 using ::testing::AtMost;
40 using ::testing::InSequence;
41 using ::testing::Invoke;
42 using ::testing::Mock;
43 using ::testing::NiceMock;
44 using ::testing::Return;
46 namespace search_provider_logos {
48 namespace {
50 bool AreImagesSameSize(const SkBitmap& bitmap1, const SkBitmap& bitmap2) {
51 return bitmap1.width() == bitmap2.width() &&
52 bitmap1.height() == bitmap2.height();
55 scoped_refptr<base::RefCountedString> EncodeBitmapAsPNG(
56 const SkBitmap& bitmap) {
57 scoped_refptr<base::RefCountedMemory> png_bytes =
58 gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes();
59 scoped_refptr<base::RefCountedString> str = new base::RefCountedString();
60 str->data().assign(png_bytes->front_as<char>(), png_bytes->size());
61 return str;
64 std::string EncodeBitmapAsPNGBase64(const SkBitmap& bitmap) {
65 scoped_refptr<base::RefCountedString> png_bytes = EncodeBitmapAsPNG(bitmap);
66 std::string encoded_image_base64;
67 base::Base64Encode(png_bytes->data(), &encoded_image_base64);
68 return encoded_image_base64;
71 SkBitmap MakeBitmap(int width, int height) {
72 SkBitmap bitmap;
73 bitmap.allocN32Pixels(width, height);
74 bitmap.eraseColor(SK_ColorBLUE);
75 return bitmap;
78 EncodedLogo EncodeLogo(const Logo& logo) {
79 EncodedLogo encoded_logo;
80 encoded_logo.encoded_image = EncodeBitmapAsPNG(logo.image);
81 encoded_logo.metadata = logo.metadata;
82 return encoded_logo;
85 Logo DecodeLogo(const EncodedLogo& encoded_logo) {
86 Logo logo;
87 logo.image = gfx::Image::CreateFrom1xPNGBytes(
88 encoded_logo.encoded_image->front(),
89 encoded_logo.encoded_image->size()).AsBitmap();
90 logo.metadata = encoded_logo.metadata;
91 return logo;
94 Logo GetSampleLogo(const GURL& logo_url, base::Time response_time) {
95 Logo logo;
96 logo.image = MakeBitmap(2, 5);
97 logo.metadata.can_show_after_expiration = false;
98 logo.metadata.expiration_time =
99 response_time + base::TimeDelta::FromHours(19);
100 logo.metadata.fingerprint = "8bc33a80";
101 logo.metadata.source_url = logo_url.spec();
102 logo.metadata.on_click_url = "http://www.google.com/search?q=potato";
103 logo.metadata.alt_text = "A logo about potatoes";
104 logo.metadata.animated_url = "http://www.google.com/logos/doodle.png";
105 logo.metadata.mime_type = "image/png";
106 return logo;
109 Logo GetSampleLogo2(const GURL& logo_url, base::Time response_time) {
110 Logo logo;
111 logo.image = MakeBitmap(4, 3);
112 logo.metadata.can_show_after_expiration = true;
113 logo.metadata.expiration_time = base::Time();
114 logo.metadata.fingerprint = "71082741021409127";
115 logo.metadata.source_url = logo_url.spec();
116 logo.metadata.on_click_url = "http://example.com/page25";
117 logo.metadata.alt_text = "The logo for example.com";
118 logo.metadata.mime_type = "image/jpeg";
119 return logo;
122 std::string MakeServerResponse(
123 const SkBitmap& image,
124 const std::string& on_click_url,
125 const std::string& alt_text,
126 const std::string& animated_url,
127 const std::string& mime_type,
128 const std::string& fingerprint,
129 base::TimeDelta time_to_live) {
130 base::DictionaryValue dict;
131 if (!image.isNull())
132 dict.SetString("update.logo.data", EncodeBitmapAsPNGBase64(image));
134 dict.SetString("update.logo.target", on_click_url);
135 dict.SetString("update.logo.alt", alt_text);
136 if (!animated_url.empty())
137 dict.SetString("update.logo.url", animated_url);
138 if (!mime_type.empty())
139 dict.SetString("update.logo.mime_type", mime_type);
140 dict.SetString("update.logo.fingerprint", fingerprint);
141 if (time_to_live.ToInternalValue() != 0)
142 dict.SetInteger("update.logo.time_to_live",
143 static_cast<int>(time_to_live.InMilliseconds()));
145 std::string output;
146 base::JSONWriter::Write(dict, &output);
147 return output;
150 std::string MakeServerResponse(const Logo& logo, base::TimeDelta time_to_live) {
151 return MakeServerResponse(logo.image,
152 logo.metadata.on_click_url,
153 logo.metadata.alt_text,
154 logo.metadata.animated_url,
155 logo.metadata.mime_type,
156 logo.metadata.fingerprint,
157 time_to_live);
160 void ExpectLogosEqual(const Logo* expected_logo,
161 const Logo* actual_logo) {
162 if (!expected_logo) {
163 ASSERT_FALSE(actual_logo);
164 return;
166 ASSERT_TRUE(actual_logo);
167 EXPECT_TRUE(AreImagesSameSize(expected_logo->image, actual_logo->image));
168 EXPECT_EQ(expected_logo->metadata.on_click_url,
169 actual_logo->metadata.on_click_url);
170 EXPECT_EQ(expected_logo->metadata.source_url,
171 actual_logo->metadata.source_url);
172 EXPECT_EQ(expected_logo->metadata.animated_url,
173 actual_logo->metadata.animated_url);
174 EXPECT_EQ(expected_logo->metadata.alt_text,
175 actual_logo->metadata.alt_text);
176 EXPECT_EQ(expected_logo->metadata.mime_type,
177 actual_logo->metadata.mime_type);
178 EXPECT_EQ(expected_logo->metadata.fingerprint,
179 actual_logo->metadata.fingerprint);
180 EXPECT_EQ(expected_logo->metadata.can_show_after_expiration,
181 actual_logo->metadata.can_show_after_expiration);
184 void ExpectLogosEqual(const Logo* expected_logo,
185 const EncodedLogo* actual_encoded_logo) {
186 Logo actual_logo;
187 if (actual_encoded_logo)
188 actual_logo = DecodeLogo(*actual_encoded_logo);
189 ExpectLogosEqual(expected_logo, actual_encoded_logo ? &actual_logo : NULL);
192 ACTION_P(ExpectLogosEqualAction, expected_logo) {
193 ExpectLogosEqual(expected_logo, arg0);
196 class MockLogoCache : public LogoCache {
197 public:
198 MockLogoCache() : LogoCache(base::FilePath()) {
199 // Delegate actions to the *Internal() methods by default.
200 ON_CALL(*this, UpdateCachedLogoMetadata(_)).WillByDefault(
201 Invoke(this, &MockLogoCache::UpdateCachedLogoMetadataInternal));
202 ON_CALL(*this, GetCachedLogoMetadata()).WillByDefault(
203 Invoke(this, &MockLogoCache::GetCachedLogoMetadataInternal));
204 ON_CALL(*this, SetCachedLogo(_))
205 .WillByDefault(Invoke(this, &MockLogoCache::SetCachedLogoInternal));
208 MOCK_METHOD1(UpdateCachedLogoMetadata, void(const LogoMetadata& metadata));
209 MOCK_METHOD0(GetCachedLogoMetadata, const LogoMetadata*());
210 MOCK_METHOD1(SetCachedLogo, void(const EncodedLogo* logo));
211 // GetCachedLogo() can't be mocked since it returns a scoped_ptr, which is
212 // non-copyable. Instead create a method that's pinged when GetCachedLogo() is
213 // called.
214 MOCK_METHOD0(OnGetCachedLogo, void());
216 void EncodeAndSetCachedLogo(const Logo& logo) {
217 EncodedLogo encoded_logo = EncodeLogo(logo);
218 SetCachedLogo(&encoded_logo);
221 void ExpectSetCachedLogo(const Logo* expected_logo) {
222 Mock::VerifyAndClearExpectations(this);
223 EXPECT_CALL(*this, SetCachedLogo(_))
224 .WillOnce(ExpectLogosEqualAction(expected_logo));
227 void UpdateCachedLogoMetadataInternal(const LogoMetadata& metadata) {
228 ASSERT_TRUE(logo_.get());
229 ASSERT_TRUE(metadata_.get());
230 EXPECT_EQ(metadata_->fingerprint, metadata.fingerprint);
231 metadata_.reset(new LogoMetadata(metadata));
232 logo_->metadata = metadata;
235 virtual const LogoMetadata* GetCachedLogoMetadataInternal() {
236 return metadata_.get();
239 virtual void SetCachedLogoInternal(const EncodedLogo* logo) {
240 logo_.reset(logo ? new EncodedLogo(*logo) : NULL);
241 metadata_.reset(logo ? new LogoMetadata(logo->metadata) : NULL);
244 scoped_ptr<EncodedLogo> GetCachedLogo() override {
245 OnGetCachedLogo();
246 return make_scoped_ptr(logo_ ? new EncodedLogo(*logo_) : NULL);
249 private:
250 scoped_ptr<LogoMetadata> metadata_;
251 scoped_ptr<EncodedLogo> logo_;
254 class MockLogoObserver : public LogoObserver {
255 public:
256 virtual ~MockLogoObserver() {}
258 void ExpectNoLogo() {
259 Mock::VerifyAndClearExpectations(this);
260 EXPECT_CALL(*this, OnLogoAvailable(_, _)).Times(0);
261 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
264 void ExpectCachedLogo(const Logo* expected_cached_logo) {
265 Mock::VerifyAndClearExpectations(this);
266 EXPECT_CALL(*this, OnLogoAvailable(_, true))
267 .WillOnce(ExpectLogosEqualAction(expected_cached_logo));
268 EXPECT_CALL(*this, OnLogoAvailable(_, false)).Times(0);
269 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
272 void ExpectFreshLogo(const Logo* expected_fresh_logo) {
273 Mock::VerifyAndClearExpectations(this);
274 EXPECT_CALL(*this, OnLogoAvailable(_, true)).Times(0);
275 EXPECT_CALL(*this, OnLogoAvailable(NULL, true));
276 EXPECT_CALL(*this, OnLogoAvailable(_, false))
277 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
278 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
281 void ExpectCachedAndFreshLogos(const Logo* expected_cached_logo,
282 const Logo* expected_fresh_logo) {
283 Mock::VerifyAndClearExpectations(this);
284 InSequence dummy;
285 EXPECT_CALL(*this, OnLogoAvailable(_, true))
286 .WillOnce(ExpectLogosEqualAction(expected_cached_logo));
287 EXPECT_CALL(*this, OnLogoAvailable(_, false))
288 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
289 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
292 MOCK_METHOD2(OnLogoAvailable, void(const Logo*, bool));
293 MOCK_METHOD0(OnObserverRemoved, void());
296 class TestLogoDelegate : public LogoDelegate {
297 public:
298 TestLogoDelegate() {}
299 ~TestLogoDelegate() override {}
301 void DecodeUntrustedImage(
302 const scoped_refptr<base::RefCountedString>& encoded_image,
303 base::Callback<void(const SkBitmap&)> image_decoded_callback) override {
304 SkBitmap bitmap =
305 gfx::Image::CreateFrom1xPNGBytes(encoded_image->front(),
306 encoded_image->size()).AsBitmap();
307 base::ThreadTaskRunnerHandle::Get()->PostTask(
308 FROM_HERE, base::Bind(image_decoded_callback, bitmap));
312 class LogoTrackerTest : public ::testing::Test {
313 protected:
314 LogoTrackerTest()
315 : message_loop_(new base::MessageLoop()),
316 logo_url_("https://google.com/doodleoftheday?size=hp"),
317 test_clock_(new base::SimpleTestClock()),
318 logo_cache_(new NiceMock<MockLogoCache>()),
319 fake_url_fetcher_factory_(NULL) {
320 test_clock_->SetNow(base::Time::FromJsTime(INT64_C(1388686828000)));
321 logo_tracker_ =
322 new LogoTracker(base::FilePath(), base::ThreadTaskRunnerHandle::Get(),
323 base::ThreadTaskRunnerHandle::Get(),
324 new net::TestURLRequestContextGetter(
325 base::ThreadTaskRunnerHandle::Get()),
326 scoped_ptr<LogoDelegate>(new TestLogoDelegate()));
327 logo_tracker_->SetServerAPI(logo_url_, base::Bind(&GoogleParseLogoResponse),
328 base::Bind(&GoogleAppendQueryparamsToLogoURL),
329 false);
330 logo_tracker_->SetClockForTests(scoped_ptr<base::Clock>(test_clock_));
331 logo_tracker_->SetLogoCacheForTests(scoped_ptr<LogoCache>(logo_cache_));
334 virtual void TearDown() {
335 // logo_tracker_ owns logo_cache_, which gets destructed on the file thread
336 // after logo_tracker_'s destruction. Ensure that logo_cache_ is actually
337 // destructed before the test ends to make gmock happy.
338 delete logo_tracker_;
339 message_loop_->RunUntilIdle();
342 // Returns the response that the server would send for the given logo.
343 std::string ServerResponse(const Logo& logo) const;
345 // Sets the response to be returned when the LogoTracker fetches the logo.
346 void SetServerResponse(const std::string& response,
347 net::URLRequestStatus::Status request_status =
348 net::URLRequestStatus::SUCCESS,
349 net::HttpStatusCode response_code = net::HTTP_OK);
351 // Sets the response to be returned when the LogoTracker fetches the logo and
352 // provides the given fingerprint.
353 void SetServerResponseWhenFingerprint(
354 const std::string& fingerprint,
355 const std::string& response_when_fingerprint,
356 net::URLRequestStatus::Status request_status =
357 net::URLRequestStatus::SUCCESS,
358 net::HttpStatusCode response_code = net::HTTP_OK);
360 // Calls logo_tracker_->GetLogo() with listener_ and waits for the
361 // asynchronous response(s).
362 void GetLogo();
364 scoped_ptr<base::MessageLoop> message_loop_;
365 GURL logo_url_;
366 base::SimpleTestClock* test_clock_;
367 NiceMock<MockLogoCache>* logo_cache_;
368 net::FakeURLFetcherFactory fake_url_fetcher_factory_;
369 LogoTracker* logo_tracker_;
370 NiceMock<MockLogoObserver> observer_;
373 std::string LogoTrackerTest::ServerResponse(const Logo& logo) const {
374 base::TimeDelta time_to_live;
375 if (!logo.metadata.expiration_time.is_null())
376 time_to_live = logo.metadata.expiration_time - test_clock_->Now();
377 return MakeServerResponse(logo, time_to_live);
380 void LogoTrackerTest::SetServerResponse(
381 const std::string& response,
382 net::URLRequestStatus::Status request_status,
383 net::HttpStatusCode response_code) {
384 fake_url_fetcher_factory_.SetFakeResponse(
385 logo_url_, response, response_code, request_status);
388 void LogoTrackerTest::SetServerResponseWhenFingerprint(
389 const std::string& fingerprint,
390 const std::string& response_when_fingerprint,
391 net::URLRequestStatus::Status request_status,
392 net::HttpStatusCode response_code) {
393 GURL url_with_fp =
394 GoogleAppendQueryparamsToLogoURL(logo_url_, fingerprint, false);
395 fake_url_fetcher_factory_.SetFakeResponse(
396 url_with_fp, response_when_fingerprint, response_code, request_status);
399 void LogoTrackerTest::GetLogo() {
400 logo_tracker_->GetLogo(&observer_);
401 base::RunLoop().RunUntilIdle();
404 // Tests -----------------------------------------------------------------------
406 TEST_F(LogoTrackerTest, FingerprintURLHasColon) {
407 GURL url_with_fp = GoogleAppendQueryparamsToLogoURL(
408 GURL("http://logourl.com/path"), "abc123", false);
409 EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123", url_with_fp.spec());
411 url_with_fp = GoogleAppendQueryparamsToLogoURL(
412 GURL("http://logourl.com/?a=b"), "cafe0", false);
413 EXPECT_EQ("http://logourl.com/?a=b&async=es_dfp:cafe0", url_with_fp.spec());
416 TEST_F(LogoTrackerTest, CTAURLHasComma) {
417 GURL url_with_fp = GoogleAppendQueryparamsToLogoURL(
418 GURL("http://logourl.com/path"), "abc123", true);
419 EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123,cta:1",
420 url_with_fp.spec());
422 url_with_fp = GoogleAppendQueryparamsToLogoURL(
423 GURL("http://logourl.com/?a=b"), "", true);
424 EXPECT_EQ("http://logourl.com/?a=b&async=cta:1", url_with_fp.spec());
427 TEST_F(LogoTrackerTest, DownloadAndCacheLogo) {
428 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
429 SetServerResponse(ServerResponse(logo));
430 logo_cache_->ExpectSetCachedLogo(&logo);
431 observer_.ExpectFreshLogo(&logo);
432 GetLogo();
435 TEST_F(LogoTrackerTest, EmptyCacheAndFailedDownload) {
436 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
437 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
438 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
440 SetServerResponse("server is borked");
441 observer_.ExpectCachedLogo(NULL);
442 GetLogo();
444 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
445 observer_.ExpectCachedLogo(NULL);
446 GetLogo();
448 SetServerResponse("", net::URLRequestStatus::SUCCESS, net::HTTP_BAD_GATEWAY);
449 observer_.ExpectCachedLogo(NULL);
450 GetLogo();
453 TEST_F(LogoTrackerTest, AcceptMinimalLogoResponse) {
454 Logo logo;
455 logo.image = MakeBitmap(1, 2);
456 logo.metadata.source_url = logo_url_.spec();
457 logo.metadata.can_show_after_expiration = true;
458 logo.metadata.mime_type = "image/png";
460 std::string response = ")]}' {\"update\":{\"logo\":{\"data\":\"" +
461 EncodeBitmapAsPNGBase64(logo.image) +
462 "\",\"mime_type\":\"image/png\"}}}";
464 SetServerResponse(response);
465 observer_.ExpectFreshLogo(&logo);
466 GetLogo();
469 TEST_F(LogoTrackerTest, ReturnCachedLogo) {
470 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
471 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
472 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
474 net::URLRequestStatus::FAILED,
475 net::HTTP_OK);
477 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
478 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
479 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
480 observer_.ExpectCachedLogo(&cached_logo);
481 GetLogo();
484 TEST_F(LogoTrackerTest, ValidateCachedLogo) {
485 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
486 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
488 // During revalidation, the image data and mime_type are absent.
489 Logo fresh_logo = cached_logo;
490 fresh_logo.image.reset();
491 fresh_logo.metadata.mime_type.clear();
492 fresh_logo.metadata.expiration_time =
493 test_clock_->Now() + base::TimeDelta::FromDays(8);
494 SetServerResponseWhenFingerprint(fresh_logo.metadata.fingerprint,
495 ServerResponse(fresh_logo));
497 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(1);
498 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
499 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
500 observer_.ExpectCachedLogo(&cached_logo);
501 GetLogo();
503 EXPECT_TRUE(logo_cache_->GetCachedLogoMetadata() != NULL);
504 EXPECT_EQ(fresh_logo.metadata.expiration_time,
505 logo_cache_->GetCachedLogoMetadata()->expiration_time);
507 // Ensure that cached logo is still returned correctly on subsequent requests.
508 // In particular, the metadata should stay valid. http://crbug.com/480090
509 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(1);
510 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
511 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
512 observer_.ExpectCachedLogo(&cached_logo);
513 GetLogo();
516 TEST_F(LogoTrackerTest, UpdateCachedLogoMetadata) {
517 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
518 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
520 Logo fresh_logo = cached_logo;
521 fresh_logo.image.reset();
522 fresh_logo.metadata.mime_type.clear();
523 fresh_logo.metadata.on_click_url = "http://new.onclick.url";
524 fresh_logo.metadata.alt_text = "new alt text";
525 fresh_logo.metadata.animated_url = "http://new.animated.url";
526 fresh_logo.metadata.expiration_time =
527 test_clock_->Now() + base::TimeDelta::FromDays(8);
528 SetServerResponseWhenFingerprint(fresh_logo.metadata.fingerprint,
529 ServerResponse(fresh_logo));
531 // On the first request, the cached logo should be used.
532 observer_.ExpectCachedLogo(&cached_logo);
533 GetLogo();
535 // Subsequently, the cached image should be returned along with the updated
536 // metadata.
537 Logo expected_logo = fresh_logo;
538 expected_logo.image = cached_logo.image;
539 expected_logo.metadata.mime_type = cached_logo.metadata.mime_type;
540 observer_.ExpectCachedLogo(&expected_logo);
541 GetLogo();
544 TEST_F(LogoTrackerTest, UpdateCachedLogo) {
545 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
546 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
548 Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
549 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
550 ServerResponse(fresh_logo));
552 logo_cache_->ExpectSetCachedLogo(&fresh_logo);
553 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
554 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
555 observer_.ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
557 GetLogo();
560 TEST_F(LogoTrackerTest, InvalidateCachedLogo) {
561 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
562 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
564 // This response means there's no current logo.
565 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
566 ")]}' {\"update\":{}}");
568 logo_cache_->ExpectSetCachedLogo(NULL);
569 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
570 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
571 observer_.ExpectCachedAndFreshLogos(&cached_logo, NULL);
573 GetLogo();
576 TEST_F(LogoTrackerTest, DeleteCachedLogoFromOldUrl) {
577 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
578 Logo cached_logo =
579 GetSampleLogo(GURL("http://oldsearchprovider.com"), test_clock_->Now());
580 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
582 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
583 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
584 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
585 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
586 observer_.ExpectCachedLogo(NULL);
587 GetLogo();
590 TEST_F(LogoTrackerTest, LogoWithTTLCannotBeShownAfterExpiration) {
591 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
592 base::TimeDelta time_to_live = base::TimeDelta::FromDays(3);
593 logo.metadata.expiration_time = test_clock_->Now() + time_to_live;
594 SetServerResponse(ServerResponse(logo));
595 GetLogo();
597 const LogoMetadata* cached_metadata =
598 logo_cache_->GetCachedLogoMetadata();
599 EXPECT_TRUE(cached_metadata != NULL);
600 EXPECT_FALSE(cached_metadata->can_show_after_expiration);
601 EXPECT_EQ(test_clock_->Now() + time_to_live,
602 cached_metadata->expiration_time);
605 TEST_F(LogoTrackerTest, LogoWithoutTTLCanBeShownAfterExpiration) {
606 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
607 base::TimeDelta time_to_live = base::TimeDelta();
608 SetServerResponse(MakeServerResponse(logo, time_to_live));
609 GetLogo();
611 const LogoMetadata* cached_metadata =
612 logo_cache_->GetCachedLogoMetadata();
613 EXPECT_TRUE(cached_metadata != NULL);
614 EXPECT_TRUE(cached_metadata->can_show_after_expiration);
615 EXPECT_EQ(test_clock_->Now() + base::TimeDelta::FromDays(30),
616 cached_metadata->expiration_time);
619 TEST_F(LogoTrackerTest, UseSoftExpiredCachedLogo) {
620 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
621 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
622 cached_logo.metadata.expiration_time =
623 test_clock_->Now() - base::TimeDelta::FromSeconds(1);
624 cached_logo.metadata.can_show_after_expiration = true;
625 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
627 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
628 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
629 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
630 observer_.ExpectCachedLogo(&cached_logo);
631 GetLogo();
634 TEST_F(LogoTrackerTest, RerequestSoftExpiredCachedLogo) {
635 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
636 cached_logo.metadata.expiration_time =
637 test_clock_->Now() - base::TimeDelta::FromDays(5);
638 cached_logo.metadata.can_show_after_expiration = true;
639 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
641 Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
642 SetServerResponse(ServerResponse(fresh_logo));
644 logo_cache_->ExpectSetCachedLogo(&fresh_logo);
645 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
646 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
647 observer_.ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
649 GetLogo();
652 TEST_F(LogoTrackerTest, DeleteAncientCachedLogo) {
653 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
654 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
655 cached_logo.metadata.expiration_time =
656 test_clock_->Now() - base::TimeDelta::FromDays(200);
657 cached_logo.metadata.can_show_after_expiration = true;
658 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
660 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
661 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
662 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
663 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
664 observer_.ExpectCachedLogo(NULL);
665 GetLogo();
668 TEST_F(LogoTrackerTest, DeleteExpiredCachedLogo) {
669 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
670 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
671 cached_logo.metadata.expiration_time =
672 test_clock_->Now() - base::TimeDelta::FromSeconds(1);
673 cached_logo.metadata.can_show_after_expiration = false;
674 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
676 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
677 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
678 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
679 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
680 observer_.ExpectCachedLogo(NULL);
681 GetLogo();
684 // Tests that deal with multiple listeners.
686 void EnqueueObservers(LogoTracker* logo_tracker,
687 const ScopedVector<MockLogoObserver>& observers,
688 size_t start_index) {
689 if (start_index >= observers.size())
690 return;
692 logo_tracker->GetLogo(observers[start_index]);
693 base::ThreadTaskRunnerHandle::Get()->PostTask(
694 FROM_HERE, base::Bind(&EnqueueObservers, logo_tracker,
695 base::ConstRef(observers), start_index + 1));
698 TEST_F(LogoTrackerTest, SupportOverlappingLogoRequests) {
699 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
700 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
701 ON_CALL(*logo_cache_, SetCachedLogo(_)).WillByDefault(Return());
703 Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
704 std::string response = ServerResponse(fresh_logo);
705 SetServerResponse(response);
706 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint, response);
708 const int kNumListeners = 10;
709 ScopedVector<MockLogoObserver> listeners;
710 for (int i = 0; i < kNumListeners; ++i) {
711 MockLogoObserver* listener = new MockLogoObserver();
712 listener->ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
713 listeners.push_back(listener);
715 EnqueueObservers(logo_tracker_, listeners, 0);
717 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(AtMost(3));
718 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(3));
720 base::RunLoop().RunUntilIdle();
723 TEST_F(LogoTrackerTest, DeleteObserversWhenLogoURLChanged) {
724 MockLogoObserver listener1;
725 listener1.ExpectNoLogo();
726 logo_tracker_->GetLogo(&listener1);
728 logo_url_ = GURL("http://example.com/new-logo-url");
729 logo_tracker_->SetServerAPI(logo_url_, base::Bind(&GoogleParseLogoResponse),
730 base::Bind(&GoogleAppendQueryparamsToLogoURL),
731 false);
732 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
733 SetServerResponse(ServerResponse(logo));
735 MockLogoObserver listener2;
736 listener2.ExpectFreshLogo(&logo);
737 logo_tracker_->GetLogo(&listener2);
739 base::RunLoop().RunUntilIdle();
742 } // namespace
744 } // namespace search_provider_logos