Roll libvpx 861f35:1fff3e
[chromium-blink-merge.git] / components / search_provider_logos / logo_tracker_unittest.cc
blob4ae77346b961499d95bef4f92d28dd1412fccbe0
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 <vector>
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_path.h"
13 #include "base/json/json_writer.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_piece.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/test/simple_test_clock.h"
20 #include "base/time/time.h"
21 #include "base/values.h"
22 #include "components/search_provider_logos/google_logo_api.h"
23 #include "net/base/url_util.h"
24 #include "net/http/http_response_headers.h"
25 #include "net/http/http_status_code.h"
26 #include "net/url_request/test_url_fetcher_factory.h"
27 #include "net/url_request/url_request_status.h"
28 #include "net/url_request/url_request_test_util.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "ui/gfx/image/image.h"
33 using ::testing::_;
34 using ::testing::AnyNumber;
35 using ::testing::AtMost;
36 using ::testing::InSequence;
37 using ::testing::Invoke;
38 using ::testing::Mock;
39 using ::testing::NiceMock;
40 using ::testing::Return;
42 namespace search_provider_logos {
44 namespace {
46 bool AreImagesSameSize(const SkBitmap& bitmap1, const SkBitmap& bitmap2) {
47 return bitmap1.width() == bitmap2.width() &&
48 bitmap1.height() == bitmap2.height();
51 scoped_refptr<base::RefCountedString> EncodeBitmapAsPNG(
52 const SkBitmap& bitmap) {
53 scoped_refptr<base::RefCountedMemory> png_bytes =
54 gfx::Image::CreateFrom1xBitmap(bitmap).As1xPNGBytes();
55 scoped_refptr<base::RefCountedString> str = new base::RefCountedString();
56 str->data().assign(png_bytes->front_as<char>(), png_bytes->size());
57 return str;
60 std::string EncodeBitmapAsPNGBase64(const SkBitmap& bitmap) {
61 scoped_refptr<base::RefCountedString> png_bytes = EncodeBitmapAsPNG(bitmap);
62 std::string encoded_image_base64;
63 base::Base64Encode(png_bytes->data(), &encoded_image_base64);
64 return encoded_image_base64;
67 SkBitmap MakeBitmap(int width, int height) {
68 SkBitmap bitmap;
69 bitmap.allocN32Pixels(width, height);
70 bitmap.eraseColor(SK_ColorBLUE);
71 return bitmap;
74 EncodedLogo EncodeLogo(const Logo& logo) {
75 EncodedLogo encoded_logo;
76 encoded_logo.encoded_image = EncodeBitmapAsPNG(logo.image);
77 encoded_logo.metadata = logo.metadata;
78 return encoded_logo;
81 Logo DecodeLogo(const EncodedLogo& encoded_logo) {
82 Logo logo;
83 logo.image = gfx::Image::CreateFrom1xPNGBytes(
84 encoded_logo.encoded_image->front(),
85 encoded_logo.encoded_image->size()).AsBitmap();
86 logo.metadata = encoded_logo.metadata;
87 return logo;
90 Logo GetSampleLogo(const GURL& logo_url, base::Time response_time) {
91 Logo logo;
92 logo.image = MakeBitmap(2, 5);
93 logo.metadata.can_show_after_expiration = false;
94 logo.metadata.expiration_time =
95 response_time + base::TimeDelta::FromHours(19);
96 logo.metadata.fingerprint = "8bc33a80";
97 logo.metadata.source_url = logo_url.spec();
98 logo.metadata.on_click_url = "http://www.google.com/search?q=potato";
99 logo.metadata.alt_text = "A logo about potatoes";
100 logo.metadata.animated_url = "http://www.google.com/logos/doodle.png";
101 logo.metadata.mime_type = "image/png";
102 return logo;
105 Logo GetSampleLogo2(const GURL& logo_url, base::Time response_time) {
106 Logo logo;
107 logo.image = MakeBitmap(4, 3);
108 logo.metadata.can_show_after_expiration = true;
109 logo.metadata.expiration_time = base::Time();
110 logo.metadata.fingerprint = "71082741021409127";
111 logo.metadata.source_url = logo_url.spec();
112 logo.metadata.on_click_url = "http://example.com/page25";
113 logo.metadata.alt_text = "The logo for example.com";
114 logo.metadata.mime_type = "image/png";
115 return logo;
118 std::string MakeServerResponse(
119 const SkBitmap& image,
120 const std::string& on_click_url,
121 const std::string& alt_text,
122 const std::string& animated_url,
123 const std::string& mime_type,
124 const std::string& fingerprint,
125 base::TimeDelta time_to_live) {
126 base::DictionaryValue dict;
127 if (!image.isNull()) {
128 dict.SetString("update.logo.data", EncodeBitmapAsPNGBase64(image));
131 dict.SetString("update.logo.target", on_click_url);
132 dict.SetString("update.logo.alt", alt_text);
133 if (!animated_url.empty()) {
134 dict.SetString("update.logo.url", animated_url);
136 dict.SetString("update.logo.mime_type", mime_type);
137 dict.SetString("update.logo.fingerprint", fingerprint);
138 if (time_to_live.ToInternalValue() != 0)
139 dict.SetInteger("update.logo.time_to_live",
140 static_cast<int>(time_to_live.InMilliseconds()));
142 std::string output;
143 base::JSONWriter::Write(&dict, &output);
144 return output;
147 std::string MakeServerResponse(const Logo& logo, base::TimeDelta time_to_live) {
148 return MakeServerResponse(logo.image,
149 logo.metadata.on_click_url,
150 logo.metadata.alt_text,
151 logo.metadata.animated_url,
152 logo.metadata.mime_type,
153 logo.metadata.fingerprint,
154 time_to_live);
157 void ExpectLogosEqual(const Logo* expected_logo,
158 const Logo* actual_logo) {
159 if (!expected_logo) {
160 ASSERT_FALSE(actual_logo);
161 return;
163 ASSERT_TRUE(actual_logo);
164 EXPECT_TRUE(AreImagesSameSize(expected_logo->image, actual_logo->image));
165 EXPECT_EQ(expected_logo->metadata.on_click_url,
166 actual_logo->metadata.on_click_url);
167 EXPECT_EQ(expected_logo->metadata.source_url,
168 actual_logo->metadata.source_url);
169 EXPECT_EQ(expected_logo->metadata.animated_url,
170 actual_logo->metadata.animated_url);
171 EXPECT_EQ(expected_logo->metadata.alt_text,
172 actual_logo->metadata.alt_text);
173 EXPECT_EQ(expected_logo->metadata.mime_type,
174 actual_logo->metadata.mime_type);
175 EXPECT_EQ(expected_logo->metadata.fingerprint,
176 actual_logo->metadata.fingerprint);
177 EXPECT_EQ(expected_logo->metadata.can_show_after_expiration,
178 actual_logo->metadata.can_show_after_expiration);
181 void ExpectLogosEqual(const Logo* expected_logo,
182 const EncodedLogo* actual_encoded_logo) {
183 Logo actual_logo;
184 if (actual_encoded_logo)
185 actual_logo = DecodeLogo(*actual_encoded_logo);
186 ExpectLogosEqual(expected_logo, actual_encoded_logo ? &actual_logo : NULL);
189 ACTION_P(ExpectLogosEqualAction, expected_logo) {
190 ExpectLogosEqual(expected_logo, arg0);
193 class MockLogoCache : public LogoCache {
194 public:
195 MockLogoCache() : LogoCache(base::FilePath()) {
196 // Delegate actions to the *Internal() methods by default.
197 ON_CALL(*this, UpdateCachedLogoMetadata(_)).WillByDefault(
198 Invoke(this, &MockLogoCache::UpdateCachedLogoMetadataInternal));
199 ON_CALL(*this, GetCachedLogoMetadata()).WillByDefault(
200 Invoke(this, &MockLogoCache::GetCachedLogoMetadataInternal));
201 ON_CALL(*this, SetCachedLogo(_))
202 .WillByDefault(Invoke(this, &MockLogoCache::SetCachedLogoInternal));
205 MOCK_METHOD1(UpdateCachedLogoMetadata, void(const LogoMetadata& metadata));
206 MOCK_METHOD0(GetCachedLogoMetadata, const LogoMetadata*());
207 MOCK_METHOD1(SetCachedLogo, void(const EncodedLogo* logo));
208 // GetCachedLogo() can't be mocked since it returns a scoped_ptr, which is
209 // non-copyable. Instead create a method that's pinged when GetCachedLogo() is
210 // called.
211 MOCK_METHOD0(OnGetCachedLogo, void());
213 void EncodeAndSetCachedLogo(const Logo& logo) {
214 EncodedLogo encoded_logo = EncodeLogo(logo);
215 SetCachedLogo(&encoded_logo);
218 void ExpectSetCachedLogo(const Logo* expected_logo) {
219 Mock::VerifyAndClearExpectations(this);
220 EXPECT_CALL(*this, SetCachedLogo(_))
221 .WillOnce(ExpectLogosEqualAction(expected_logo));
224 void UpdateCachedLogoMetadataInternal(const LogoMetadata& metadata) {
225 metadata_.reset(new LogoMetadata(metadata));
228 virtual const LogoMetadata* GetCachedLogoMetadataInternal() {
229 return metadata_.get();
232 virtual void SetCachedLogoInternal(const EncodedLogo* logo) {
233 logo_.reset(logo ? new EncodedLogo(*logo) : NULL);
234 metadata_.reset(logo ? new LogoMetadata(logo->metadata) : NULL);
237 virtual scoped_ptr<EncodedLogo> GetCachedLogo() override {
238 OnGetCachedLogo();
239 return make_scoped_ptr(logo_ ? new EncodedLogo(*logo_) : NULL);
242 private:
243 scoped_ptr<LogoMetadata> metadata_;
244 scoped_ptr<EncodedLogo> logo_;
247 class MockLogoObserver : public LogoObserver {
248 public:
249 virtual ~MockLogoObserver() {}
251 void ExpectNoLogo() {
252 Mock::VerifyAndClearExpectations(this);
253 EXPECT_CALL(*this, OnLogoAvailable(_, _)).Times(0);
254 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
257 void ExpectCachedLogo(const Logo* expected_cached_logo) {
258 Mock::VerifyAndClearExpectations(this);
259 EXPECT_CALL(*this, OnLogoAvailable(_, true))
260 .WillOnce(ExpectLogosEqualAction(expected_cached_logo));
261 EXPECT_CALL(*this, OnLogoAvailable(_, false)).Times(0);
262 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
265 void ExpectFreshLogo(const Logo* expected_fresh_logo) {
266 Mock::VerifyAndClearExpectations(this);
267 EXPECT_CALL(*this, OnLogoAvailable(_, true)).Times(0);
268 EXPECT_CALL(*this, OnLogoAvailable(NULL, true));
269 EXPECT_CALL(*this, OnLogoAvailable(_, false))
270 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
271 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
274 void ExpectCachedAndFreshLogos(const Logo* expected_cached_logo,
275 const Logo* expected_fresh_logo) {
276 Mock::VerifyAndClearExpectations(this);
277 InSequence dummy;
278 EXPECT_CALL(*this, OnLogoAvailable(_, true))
279 .WillOnce(ExpectLogosEqualAction(expected_cached_logo));
280 EXPECT_CALL(*this, OnLogoAvailable(_, false))
281 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
282 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
285 MOCK_METHOD2(OnLogoAvailable, void(const Logo*, bool));
286 MOCK_METHOD0(OnObserverRemoved, void());
289 class TestLogoDelegate : public LogoDelegate {
290 public:
291 TestLogoDelegate() {}
292 ~TestLogoDelegate() override {}
294 void DecodeUntrustedImage(
295 const scoped_refptr<base::RefCountedString>& encoded_image,
296 base::Callback<void(const SkBitmap&)> image_decoded_callback) override {
297 SkBitmap bitmap =
298 gfx::Image::CreateFrom1xPNGBytes(encoded_image->front(),
299 encoded_image->size()).AsBitmap();
300 base::MessageLoopProxy::current()->PostTask(
301 FROM_HERE, base::Bind(image_decoded_callback, bitmap));
305 class LogoTrackerTest : public ::testing::Test {
306 protected:
307 LogoTrackerTest()
308 : message_loop_(new base::MessageLoop()),
309 logo_url_("https://google.com/doodleoftheday?size=hp"),
310 test_clock_(new base::SimpleTestClock()),
311 logo_cache_(new NiceMock<MockLogoCache>()),
312 fake_url_fetcher_factory_(NULL) {
313 test_clock_->SetNow(base::Time::FromJsTime(GG_INT64_C(1388686828000)));
314 logo_tracker_ = new LogoTracker(
315 base::FilePath(),
316 base::MessageLoopProxy::current(),
317 base::MessageLoopProxy::current(),
318 new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()),
319 scoped_ptr<LogoDelegate>(new TestLogoDelegate()));
320 logo_tracker_->SetServerAPI(logo_url_, base::Bind(&GoogleParseLogoResponse),
321 base::Bind(&GoogleAppendQueryparamsToLogoURL),
322 false);
323 logo_tracker_->SetClockForTests(scoped_ptr<base::Clock>(test_clock_));
324 logo_tracker_->SetLogoCacheForTests(scoped_ptr<LogoCache>(logo_cache_));
327 virtual void TearDown() {
328 // logo_tracker_ owns logo_cache_, which gets destructed on the file thread
329 // after logo_tracker_'s destruction. Ensure that logo_cache_ is actually
330 // destructed before the test ends to make gmock happy.
331 delete logo_tracker_;
332 message_loop_->RunUntilIdle();
335 // Returns the response that the server would send for the given logo.
336 std::string ServerResponse(const Logo& logo) const;
338 // Sets the response to be returned when the LogoTracker fetches the logo.
339 void SetServerResponse(const std::string& response,
340 net::URLRequestStatus::Status request_status =
341 net::URLRequestStatus::SUCCESS,
342 net::HttpStatusCode response_code = net::HTTP_OK);
344 // Sets the response to be returned when the LogoTracker fetches the logo and
345 // provides the given fingerprint.
346 void SetServerResponseWhenFingerprint(
347 const std::string& fingerprint,
348 const std::string& response_when_fingerprint,
349 net::URLRequestStatus::Status request_status =
350 net::URLRequestStatus::SUCCESS,
351 net::HttpStatusCode response_code = net::HTTP_OK);
353 // Calls logo_tracker_->GetLogo() with listener_ and waits for the
354 // asynchronous response(s).
355 void GetLogo();
357 scoped_ptr<base::MessageLoop> message_loop_;
358 GURL logo_url_;
359 base::SimpleTestClock* test_clock_;
360 NiceMock<MockLogoCache>* logo_cache_;
361 net::FakeURLFetcherFactory fake_url_fetcher_factory_;
362 LogoTracker* logo_tracker_;
363 NiceMock<MockLogoObserver> observer_;
366 std::string LogoTrackerTest::ServerResponse(const Logo& logo) const {
367 base::TimeDelta time_to_live;
368 if (!logo.metadata.expiration_time.is_null())
369 time_to_live = logo.metadata.expiration_time - test_clock_->Now();
370 return MakeServerResponse(logo, time_to_live);
373 void LogoTrackerTest::SetServerResponse(
374 const std::string& response,
375 net::URLRequestStatus::Status request_status,
376 net::HttpStatusCode response_code) {
377 fake_url_fetcher_factory_.SetFakeResponse(
378 logo_url_, response, response_code, request_status);
381 void LogoTrackerTest::SetServerResponseWhenFingerprint(
382 const std::string& fingerprint,
383 const std::string& response_when_fingerprint,
384 net::URLRequestStatus::Status request_status,
385 net::HttpStatusCode response_code) {
386 GURL url_with_fp =
387 GoogleAppendQueryparamsToLogoURL(logo_url_, fingerprint, false);
388 fake_url_fetcher_factory_.SetFakeResponse(
389 url_with_fp, response_when_fingerprint, response_code, request_status);
392 void LogoTrackerTest::GetLogo() {
393 logo_tracker_->GetLogo(&observer_);
394 base::RunLoop().RunUntilIdle();
397 // Tests -----------------------------------------------------------------------
399 TEST_F(LogoTrackerTest, FingerprintURLHasColon) {
400 GURL url_with_fp = GoogleAppendQueryparamsToLogoURL(
401 GURL("http://logourl.com/path"), "abc123", false);
402 EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123", url_with_fp.spec());
404 url_with_fp = GoogleAppendQueryparamsToLogoURL(
405 GURL("http://logourl.com/?a=b"), "cafe0", false);
406 EXPECT_EQ("http://logourl.com/?a=b&async=es_dfp:cafe0", url_with_fp.spec());
409 TEST_F(LogoTrackerTest, CTAURLHasComma) {
410 GURL url_with_fp = GoogleAppendQueryparamsToLogoURL(
411 GURL("http://logourl.com/path"), "abc123", true);
412 EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123,cta:1",
413 url_with_fp.spec());
415 url_with_fp = GoogleAppendQueryparamsToLogoURL(
416 GURL("http://logourl.com/?a=b"), "", true);
417 EXPECT_EQ("http://logourl.com/?a=b&async=cta:1", url_with_fp.spec());
420 TEST_F(LogoTrackerTest, DownloadAndCacheLogo) {
421 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
422 SetServerResponse(ServerResponse(logo));
423 logo_cache_->ExpectSetCachedLogo(&logo);
424 observer_.ExpectFreshLogo(&logo);
425 GetLogo();
428 TEST_F(LogoTrackerTest, EmptyCacheAndFailedDownload) {
429 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
430 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
431 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
433 SetServerResponse("server is borked");
434 observer_.ExpectCachedLogo(NULL);
435 GetLogo();
437 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
438 observer_.ExpectCachedLogo(NULL);
439 GetLogo();
441 SetServerResponse("", net::URLRequestStatus::SUCCESS, net::HTTP_BAD_GATEWAY);
442 observer_.ExpectCachedLogo(NULL);
443 GetLogo();
446 TEST_F(LogoTrackerTest, AcceptMinimalLogoResponse) {
447 Logo logo;
448 logo.image = MakeBitmap(1, 2);
449 logo.metadata.source_url = logo_url_.spec();
450 logo.metadata.can_show_after_expiration = true;
451 logo.metadata.mime_type = "image/png";
453 std::string response = ")]}' {\"update\":{\"logo\":{\"data\":\"" +
454 EncodeBitmapAsPNGBase64(logo.image) +
455 "\",\"mime_type\":\"image/png\"}}}";
457 SetServerResponse(response);
458 observer_.ExpectFreshLogo(&logo);
459 GetLogo();
462 TEST_F(LogoTrackerTest, ReturnCachedLogo) {
463 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
464 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
465 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
467 net::URLRequestStatus::FAILED,
468 net::HTTP_OK);
470 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
471 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
472 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
473 observer_.ExpectCachedLogo(&cached_logo);
474 GetLogo();
477 TEST_F(LogoTrackerTest, ValidateCachedLogoFingerprint) {
478 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
479 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
481 Logo fresh_logo = cached_logo;
482 fresh_logo.image.reset();
483 fresh_logo.metadata.expiration_time =
484 test_clock_->Now() + base::TimeDelta::FromDays(8);
485 SetServerResponseWhenFingerprint(fresh_logo.metadata.fingerprint,
486 ServerResponse(fresh_logo));
488 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(1);
489 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
490 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
491 observer_.ExpectCachedLogo(&cached_logo);
493 GetLogo();
495 EXPECT_TRUE(logo_cache_->GetCachedLogoMetadata() != NULL);
496 EXPECT_EQ(logo_cache_->GetCachedLogoMetadata()->expiration_time,
497 fresh_logo.metadata.expiration_time);
500 TEST_F(LogoTrackerTest, UpdateCachedLogo) {
501 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
502 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
504 Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
505 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
506 ServerResponse(fresh_logo));
508 logo_cache_->ExpectSetCachedLogo(&fresh_logo);
509 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
510 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
511 observer_.ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
513 GetLogo();
516 TEST_F(LogoTrackerTest, InvalidateCachedLogo) {
517 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
518 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
520 // This response means there's no current logo.
521 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
522 ")]}' {\"update\":{}}");
524 logo_cache_->ExpectSetCachedLogo(NULL);
525 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
526 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
527 observer_.ExpectCachedAndFreshLogos(&cached_logo, NULL);
529 GetLogo();
532 TEST_F(LogoTrackerTest, DeleteCachedLogoFromOldUrl) {
533 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
534 Logo cached_logo =
535 GetSampleLogo(GURL("http://oldsearchprovider.com"), test_clock_->Now());
536 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
538 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
539 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
540 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
541 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
542 observer_.ExpectCachedLogo(NULL);
543 GetLogo();
546 TEST_F(LogoTrackerTest, LogoWithTTLCannotBeShownAfterExpiration) {
547 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
548 base::TimeDelta time_to_live = base::TimeDelta::FromDays(3);
549 logo.metadata.expiration_time = test_clock_->Now() + time_to_live;
550 SetServerResponse(ServerResponse(logo));
551 GetLogo();
553 const LogoMetadata* cached_metadata =
554 logo_cache_->GetCachedLogoMetadata();
555 EXPECT_TRUE(cached_metadata != NULL);
556 EXPECT_FALSE(cached_metadata->can_show_after_expiration);
557 EXPECT_EQ(test_clock_->Now() + time_to_live,
558 cached_metadata->expiration_time);
561 TEST_F(LogoTrackerTest, LogoWithoutTTLCanBeShownAfterExpiration) {
562 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
563 base::TimeDelta time_to_live = base::TimeDelta();
564 SetServerResponse(MakeServerResponse(logo, time_to_live));
565 GetLogo();
567 const LogoMetadata* cached_metadata =
568 logo_cache_->GetCachedLogoMetadata();
569 EXPECT_TRUE(cached_metadata != NULL);
570 EXPECT_TRUE(cached_metadata->can_show_after_expiration);
571 EXPECT_EQ(test_clock_->Now() + base::TimeDelta::FromDays(30),
572 cached_metadata->expiration_time);
575 TEST_F(LogoTrackerTest, UseSoftExpiredCachedLogo) {
576 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
577 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
578 cached_logo.metadata.expiration_time =
579 test_clock_->Now() - base::TimeDelta::FromSeconds(1);
580 cached_logo.metadata.can_show_after_expiration = true;
581 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
583 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
584 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
585 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
586 observer_.ExpectCachedLogo(&cached_logo);
587 GetLogo();
590 TEST_F(LogoTrackerTest, RerequestSoftExpiredCachedLogo) {
591 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
592 cached_logo.metadata.expiration_time =
593 test_clock_->Now() - base::TimeDelta::FromDays(5);
594 cached_logo.metadata.can_show_after_expiration = true;
595 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
597 Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
598 SetServerResponse(ServerResponse(fresh_logo));
600 logo_cache_->ExpectSetCachedLogo(&fresh_logo);
601 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
602 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
603 observer_.ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
605 GetLogo();
608 TEST_F(LogoTrackerTest, DeleteAncientCachedLogo) {
609 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
610 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
611 cached_logo.metadata.expiration_time =
612 test_clock_->Now() - base::TimeDelta::FromDays(200);
613 cached_logo.metadata.can_show_after_expiration = true;
614 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
616 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
617 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
618 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
619 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
620 observer_.ExpectCachedLogo(NULL);
621 GetLogo();
624 TEST_F(LogoTrackerTest, DeleteExpiredCachedLogo) {
625 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
626 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
627 cached_logo.metadata.expiration_time =
628 test_clock_->Now() - base::TimeDelta::FromSeconds(1);
629 cached_logo.metadata.can_show_after_expiration = false;
630 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
632 EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
633 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
634 EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
635 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
636 observer_.ExpectCachedLogo(NULL);
637 GetLogo();
640 // Tests that deal with multiple listeners.
642 void EnqueueObservers(LogoTracker* logo_tracker,
643 const ScopedVector<MockLogoObserver>& observers,
644 size_t start_index) {
645 if (start_index >= observers.size())
646 return;
648 logo_tracker->GetLogo(observers[start_index]);
649 base::MessageLoop::current()->PostTask(FROM_HERE,
650 base::Bind(&EnqueueObservers,
651 logo_tracker,
652 base::ConstRef(observers),
653 start_index + 1));
656 TEST_F(LogoTrackerTest, SupportOverlappingLogoRequests) {
657 Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
658 logo_cache_->EncodeAndSetCachedLogo(cached_logo);
659 ON_CALL(*logo_cache_, SetCachedLogo(_)).WillByDefault(Return());
661 Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
662 std::string response = ServerResponse(fresh_logo);
663 SetServerResponse(response);
664 SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint, response);
666 const int kNumListeners = 10;
667 ScopedVector<MockLogoObserver> listeners;
668 for (int i = 0; i < kNumListeners; ++i) {
669 MockLogoObserver* listener = new MockLogoObserver();
670 listener->ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
671 listeners.push_back(listener);
673 EnqueueObservers(logo_tracker_, listeners, 0);
675 EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(AtMost(3));
676 EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(3));
678 base::RunLoop().RunUntilIdle();
681 TEST_F(LogoTrackerTest, DeleteObserversWhenLogoURLChanged) {
682 MockLogoObserver listener1;
683 listener1.ExpectNoLogo();
684 logo_tracker_->GetLogo(&listener1);
686 logo_url_ = GURL("http://example.com/new-logo-url");
687 logo_tracker_->SetServerAPI(logo_url_, base::Bind(&GoogleParseLogoResponse),
688 base::Bind(&GoogleAppendQueryparamsToLogoURL),
689 false);
690 Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
691 SetServerResponse(ServerResponse(logo));
693 MockLogoObserver listener2;
694 listener2.ExpectFreshLogo(&logo);
695 logo_tracker_->GetLogo(&listener2);
697 base::RunLoop().RunUntilIdle();
700 } // namespace
702 } // namespace search_provider_logos