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"
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"
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
{
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());
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
) {
69 bitmap
.setConfig(SkBitmap::kARGB_8888_Config
, width
, height
);
71 bitmap
.eraseColor(SK_ColorBLUE
);
75 EncodedLogo
EncodeLogo(const Logo
& logo
) {
76 EncodedLogo encoded_logo
;
77 encoded_logo
.encoded_image
= EncodeBitmapAsPNG(logo
.image
);
78 encoded_logo
.metadata
= logo
.metadata
;
82 Logo
DecodeLogo(const EncodedLogo
& encoded_logo
) {
84 logo
.image
= gfx::Image::CreateFrom1xPNGBytes(
85 encoded_logo
.encoded_image
->front(),
86 encoded_logo
.encoded_image
->size()).AsBitmap();
87 logo
.metadata
= encoded_logo
.metadata
;
91 Logo
GetSampleLogo(const GURL
& logo_url
, base::Time response_time
) {
93 logo
.image
= MakeBitmap(2, 5);
94 logo
.metadata
.can_show_after_expiration
= false;
95 logo
.metadata
.expiration_time
=
96 response_time
+ base::TimeDelta::FromHours(19);
97 logo
.metadata
.fingerprint
= "8bc33a80";
98 logo
.metadata
.source_url
= logo_url
.spec();
99 logo
.metadata
.on_click_url
= "http://www.google.com/search?q=potato";
100 logo
.metadata
.alt_text
= "A logo about potatoes";
101 logo
.metadata
.mime_type
= "image/png";
105 Logo
GetSampleLogo2(const GURL
& logo_url
, base::Time response_time
) {
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";
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
& mime_type
,
123 const std::string
& fingerprint
,
124 base::TimeDelta time_to_live
) {
125 base::DictionaryValue dict
;
126 if (!image
.isNull()) {
127 dict
.SetString("update.logo.data", EncodeBitmapAsPNGBase64(image
));
130 dict
.SetString("update.logo.target", on_click_url
);
131 dict
.SetString("update.logo.alt", alt_text
);
132 dict
.SetString("update.logo.mime_type", mime_type
);
133 dict
.SetString("update.logo.fingerprint", fingerprint
);
134 if (time_to_live
.ToInternalValue() != 0)
135 dict
.SetInteger("update.logo.time_to_live",
136 static_cast<int>(time_to_live
.InMilliseconds()));
139 base::JSONWriter::Write(&dict
, &output
);
143 std::string
MakeServerResponse(const Logo
& logo
, base::TimeDelta time_to_live
) {
144 return MakeServerResponse(logo
.image
,
145 logo
.metadata
.on_click_url
,
146 logo
.metadata
.alt_text
,
147 logo
.metadata
.mime_type
,
148 logo
.metadata
.fingerprint
,
152 void ExpectLogosEqual(const Logo
* expected_logo
,
153 const Logo
* actual_logo
) {
154 if (!expected_logo
) {
155 ASSERT_FALSE(actual_logo
);
158 ASSERT_TRUE(actual_logo
);
159 EXPECT_TRUE(AreImagesSameSize(expected_logo
->image
, actual_logo
->image
));
160 EXPECT_EQ(expected_logo
->metadata
.on_click_url
,
161 actual_logo
->metadata
.on_click_url
);
162 EXPECT_EQ(expected_logo
->metadata
.source_url
,
163 actual_logo
->metadata
.source_url
);
164 EXPECT_EQ(expected_logo
->metadata
.fingerprint
,
165 actual_logo
->metadata
.fingerprint
);
166 EXPECT_EQ(expected_logo
->metadata
.can_show_after_expiration
,
167 actual_logo
->metadata
.can_show_after_expiration
);
170 void ExpectLogosEqual(const Logo
* expected_logo
,
171 const EncodedLogo
* actual_encoded_logo
) {
173 if (actual_encoded_logo
)
174 actual_logo
= DecodeLogo(*actual_encoded_logo
);
175 ExpectLogosEqual(expected_logo
, actual_encoded_logo
? &actual_logo
: NULL
);
178 ACTION_P(ExpectLogosEqualAction
, expected_logo
) {
179 ExpectLogosEqual(expected_logo
, arg0
);
182 class MockLogoCache
: public LogoCache
{
184 MockLogoCache() : LogoCache(base::FilePath()) {
185 // Delegate actions to the *Internal() methods by default.
186 ON_CALL(*this, UpdateCachedLogoMetadata(_
)).WillByDefault(
187 Invoke(this, &MockLogoCache::UpdateCachedLogoMetadataInternal
));
188 ON_CALL(*this, GetCachedLogoMetadata()).WillByDefault(
189 Invoke(this, &MockLogoCache::GetCachedLogoMetadataInternal
));
190 ON_CALL(*this, SetCachedLogo(_
))
191 .WillByDefault(Invoke(this, &MockLogoCache::SetCachedLogoInternal
));
194 MOCK_METHOD1(UpdateCachedLogoMetadata
, void(const LogoMetadata
& metadata
));
195 MOCK_METHOD0(GetCachedLogoMetadata
, const LogoMetadata
*());
196 MOCK_METHOD1(SetCachedLogo
, void(const EncodedLogo
* logo
));
197 // GetCachedLogo() can't be mocked since it returns a scoped_ptr, which is
198 // non-copyable. Instead create a method that's pinged when GetCachedLogo() is
200 MOCK_METHOD0(OnGetCachedLogo
, void());
202 void EncodeAndSetCachedLogo(const Logo
& logo
) {
203 EncodedLogo encoded_logo
= EncodeLogo(logo
);
204 SetCachedLogo(&encoded_logo
);
207 void ExpectSetCachedLogo(const Logo
* expected_logo
) {
208 Mock::VerifyAndClearExpectations(this);
209 EXPECT_CALL(*this, SetCachedLogo(_
))
210 .WillOnce(ExpectLogosEqualAction(expected_logo
));
213 void UpdateCachedLogoMetadataInternal(const LogoMetadata
& metadata
) {
214 metadata_
.reset(new LogoMetadata(metadata
));
217 virtual const LogoMetadata
* GetCachedLogoMetadataInternal() {
218 return metadata_
.get();
221 virtual void SetCachedLogoInternal(const EncodedLogo
* logo
) {
222 logo_
.reset(logo
? new EncodedLogo(*logo
) : NULL
);
223 metadata_
.reset(logo
? new LogoMetadata(logo
->metadata
) : NULL
);
226 virtual scoped_ptr
<EncodedLogo
> GetCachedLogo() OVERRIDE
{
227 return make_scoped_ptr(logo_
? new EncodedLogo(*logo_
) : NULL
);
232 scoped_ptr
<LogoMetadata
> metadata_
;
233 scoped_ptr
<EncodedLogo
> logo_
;
236 class MockLogoObserver
: public LogoObserver
{
238 virtual ~MockLogoObserver() {}
240 void ExpectNoLogo() {
241 Mock::VerifyAndClearExpectations(this);
242 EXPECT_CALL(*this, OnLogoAvailable(_
, _
)).Times(0);
243 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
246 void ExpectCachedLogo(const Logo
* expected_cached_logo
) {
247 Mock::VerifyAndClearExpectations(this);
248 EXPECT_CALL(*this, OnLogoAvailable(_
, true))
249 .WillOnce(ExpectLogosEqualAction(expected_cached_logo
));
250 EXPECT_CALL(*this, OnLogoAvailable(_
, false)).Times(0);
251 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
254 void ExpectFreshLogo(const Logo
* expected_fresh_logo
) {
255 Mock::VerifyAndClearExpectations(this);
256 EXPECT_CALL(*this, OnLogoAvailable(_
, true)).Times(0);
257 EXPECT_CALL(*this, OnLogoAvailable(NULL
, true));
258 EXPECT_CALL(*this, OnLogoAvailable(_
, false))
259 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo
));
260 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
263 void ExpectCachedAndFreshLogos(const Logo
* expected_cached_logo
,
264 const Logo
* expected_fresh_logo
) {
265 Mock::VerifyAndClearExpectations(this);
267 EXPECT_CALL(*this, OnLogoAvailable(_
, true))
268 .WillOnce(ExpectLogosEqualAction(expected_cached_logo
));
269 EXPECT_CALL(*this, OnLogoAvailable(_
, false))
270 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo
));
271 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
274 MOCK_METHOD2(OnLogoAvailable
, void(const Logo
*, bool));
275 MOCK_METHOD0(OnObserverRemoved
, void());
278 class TestLogoDelegate
: public LogoDelegate
{
280 TestLogoDelegate() {}
281 virtual ~TestLogoDelegate() {}
283 virtual void DecodeUntrustedImage(
284 const scoped_refptr
<base::RefCountedString
>& encoded_image
,
285 base::Callback
<void(const SkBitmap
&)> image_decoded_callback
) OVERRIDE
{
287 gfx::Image::CreateFrom1xPNGBytes(encoded_image
->front(),
288 encoded_image
->size()).AsBitmap();
289 base::MessageLoopProxy::current()->PostTask(
290 FROM_HERE
, base::Bind(image_decoded_callback
, bitmap
));
294 class LogoTrackerTest
: public ::testing::Test
{
297 : message_loop_(new base::MessageLoop()),
298 logo_url_("https://google.com/doodleoftheday?size=hp"),
299 test_clock_(new base::SimpleTestClock()),
300 logo_cache_(new NiceMock
<MockLogoCache
>()),
301 fake_url_fetcher_factory_(NULL
) {
302 test_clock_
->SetNow(base::Time::FromJsTime(GG_INT64_C(1388686828000)));
303 logo_tracker_
= new LogoTracker(
305 base::MessageLoopProxy::current(),
306 base::MessageLoopProxy::current(),
307 new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()),
308 scoped_ptr
<LogoDelegate
>(new TestLogoDelegate()));
309 logo_tracker_
->SetServerAPI(logo_url_
,
310 base::Bind(&GoogleParseLogoResponse
),
311 base::Bind(&GoogleAppendFingerprintToLogoURL
));
312 logo_tracker_
->SetClockForTests(scoped_ptr
<base::Clock
>(test_clock_
));
313 logo_tracker_
->SetLogoCacheForTests(scoped_ptr
<LogoCache
>(logo_cache_
));
316 virtual void TearDown() {
317 // logo_tracker_ owns logo_cache_, which gets destructed on the file thread
318 // after logo_tracker_'s destruction. Ensure that logo_cache_ is actually
319 // destructed before the test ends to make gmock happy.
320 delete logo_tracker_
;
321 message_loop_
->RunUntilIdle();
324 // Returns the response that the server would send for the given logo.
325 std::string
ServerResponse(const Logo
& logo
) const;
327 // Sets the response to be returned when the LogoTracker fetches the logo.
328 void SetServerResponse(const std::string
& response
,
329 net::URLRequestStatus::Status request_status
=
330 net::URLRequestStatus::SUCCESS
,
331 net::HttpStatusCode response_code
= net::HTTP_OK
);
333 // Sets the response to be returned when the LogoTracker fetches the logo and
334 // provides the given fingerprint.
335 void SetServerResponseWhenFingerprint(
336 const std::string
& fingerprint
,
337 const std::string
& response_when_fingerprint
,
338 net::URLRequestStatus::Status request_status
=
339 net::URLRequestStatus::SUCCESS
,
340 net::HttpStatusCode response_code
= net::HTTP_OK
);
342 // Calls logo_tracker_->GetLogo() with listener_ and waits for the
343 // asynchronous response(s).
346 scoped_ptr
<base::MessageLoop
> message_loop_
;
348 base::SimpleTestClock
* test_clock_
;
349 NiceMock
<MockLogoCache
>* logo_cache_
;
350 net::FakeURLFetcherFactory fake_url_fetcher_factory_
;
351 LogoTracker
* logo_tracker_
;
352 NiceMock
<MockLogoObserver
> observer_
;
355 std::string
LogoTrackerTest::ServerResponse(const Logo
& logo
) const {
356 base::TimeDelta time_to_live
;
357 if (!logo
.metadata
.expiration_time
.is_null())
358 time_to_live
= logo
.metadata
.expiration_time
- test_clock_
->Now();
359 return MakeServerResponse(logo
, time_to_live
);
362 void LogoTrackerTest::SetServerResponse(
363 const std::string
& response
,
364 net::URLRequestStatus::Status request_status
,
365 net::HttpStatusCode response_code
) {
366 fake_url_fetcher_factory_
.SetFakeResponse(
367 logo_url_
, response
, response_code
, request_status
);
370 void LogoTrackerTest::SetServerResponseWhenFingerprint(
371 const std::string
& fingerprint
,
372 const std::string
& response_when_fingerprint
,
373 net::URLRequestStatus::Status request_status
,
374 net::HttpStatusCode response_code
) {
376 net::AppendQueryParameter(logo_url_
, "async", "es_dfp:" + fingerprint
);
377 fake_url_fetcher_factory_
.SetFakeResponse(
378 url_with_fp
, response_when_fingerprint
, response_code
, request_status
);
381 void LogoTrackerTest::GetLogo() {
382 logo_tracker_
->GetLogo(&observer_
);
383 base::RunLoop().RunUntilIdle();
386 // Tests -----------------------------------------------------------------------
388 TEST_F(LogoTrackerTest
, DownloadAndCacheLogo
) {
389 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
390 SetServerResponse(ServerResponse(logo
));
391 logo_cache_
->ExpectSetCachedLogo(&logo
);
392 observer_
.ExpectFreshLogo(&logo
);
396 TEST_F(LogoTrackerTest
, EmptyCacheAndFailedDownload
) {
397 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
398 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
399 EXPECT_CALL(*logo_cache_
, SetCachedLogo(NULL
)).Times(AnyNumber());
401 SetServerResponse("server is borked");
402 observer_
.ExpectCachedLogo(NULL
);
405 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
406 observer_
.ExpectCachedLogo(NULL
);
409 SetServerResponse("", net::URLRequestStatus::SUCCESS
, net::HTTP_BAD_GATEWAY
);
410 observer_
.ExpectCachedLogo(NULL
);
414 TEST_F(LogoTrackerTest
, AcceptMinimalLogoResponse
) {
416 logo
.image
= MakeBitmap(1, 2);
417 logo
.metadata
.source_url
= logo_url_
.spec();
418 logo
.metadata
.can_show_after_expiration
= true;
420 std::string response
= ")]}' {\"update\":{\"logo\":{\"data\":\"" +
421 EncodeBitmapAsPNGBase64(logo
.image
) +
422 "\",\"mime_type\":\"image/png\"}}}";
424 SetServerResponse(response
);
425 observer_
.ExpectFreshLogo(&logo
);
429 TEST_F(LogoTrackerTest
, ReturnCachedLogo
) {
430 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
431 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
432 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
,
434 net::URLRequestStatus::FAILED
,
437 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
438 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
439 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
440 observer_
.ExpectCachedLogo(&cached_logo
);
444 TEST_F(LogoTrackerTest
, ValidateCachedLogoFingerprint
) {
445 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
446 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
448 Logo fresh_logo
= cached_logo
;
449 fresh_logo
.image
.reset();
450 fresh_logo
.metadata
.expiration_time
=
451 test_clock_
->Now() + base::TimeDelta::FromDays(8);
452 SetServerResponseWhenFingerprint(fresh_logo
.metadata
.fingerprint
,
453 ServerResponse(fresh_logo
));
455 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(1);
456 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
457 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
458 observer_
.ExpectCachedLogo(&cached_logo
);
462 EXPECT_TRUE(logo_cache_
->GetCachedLogoMetadata() != NULL
);
463 EXPECT_EQ(logo_cache_
->GetCachedLogoMetadata()->expiration_time
,
464 fresh_logo
.metadata
.expiration_time
);
467 TEST_F(LogoTrackerTest
, UpdateCachedLogo
) {
468 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
469 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
471 Logo fresh_logo
= GetSampleLogo2(logo_url_
, test_clock_
->Now());
472 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
,
473 ServerResponse(fresh_logo
));
475 logo_cache_
->ExpectSetCachedLogo(&fresh_logo
);
476 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
477 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
478 observer_
.ExpectCachedAndFreshLogos(&cached_logo
, &fresh_logo
);
483 TEST_F(LogoTrackerTest
, InvalidateCachedLogo
) {
484 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
485 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
487 // This response means there's no current logo.
488 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
,
489 ")]}' {\"update\":{}}");
491 logo_cache_
->ExpectSetCachedLogo(NULL
);
492 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
493 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
494 observer_
.ExpectCachedAndFreshLogos(&cached_logo
, NULL
);
499 TEST_F(LogoTrackerTest
, DeleteCachedLogoFromOldUrl
) {
500 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
502 GetSampleLogo(GURL("http://oldsearchprovider.com"), test_clock_
->Now());
503 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
505 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
506 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
507 EXPECT_CALL(*logo_cache_
, SetCachedLogo(NULL
)).Times(AnyNumber());
508 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
509 observer_
.ExpectCachedLogo(NULL
);
513 TEST_F(LogoTrackerTest
, LogoWithTTLCannotBeShownAfterExpiration
) {
514 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
515 base::TimeDelta time_to_live
= base::TimeDelta::FromDays(3);
516 logo
.metadata
.expiration_time
= test_clock_
->Now() + time_to_live
;
517 SetServerResponse(ServerResponse(logo
));
520 const LogoMetadata
* cached_metadata
=
521 logo_cache_
->GetCachedLogoMetadata();
522 EXPECT_TRUE(cached_metadata
!= NULL
);
523 EXPECT_FALSE(cached_metadata
->can_show_after_expiration
);
524 EXPECT_EQ(test_clock_
->Now() + time_to_live
,
525 cached_metadata
->expiration_time
);
528 TEST_F(LogoTrackerTest
, LogoWithoutTTLCanBeShownAfterExpiration
) {
529 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
530 base::TimeDelta time_to_live
= base::TimeDelta();
531 SetServerResponse(MakeServerResponse(logo
, time_to_live
));
534 const LogoMetadata
* cached_metadata
=
535 logo_cache_
->GetCachedLogoMetadata();
536 EXPECT_TRUE(cached_metadata
!= NULL
);
537 EXPECT_TRUE(cached_metadata
->can_show_after_expiration
);
538 EXPECT_EQ(test_clock_
->Now() + base::TimeDelta::FromDays(30),
539 cached_metadata
->expiration_time
);
542 TEST_F(LogoTrackerTest
, UseSoftExpiredCachedLogo
) {
543 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
544 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
545 cached_logo
.metadata
.expiration_time
=
546 test_clock_
->Now() - base::TimeDelta::FromSeconds(1);
547 cached_logo
.metadata
.can_show_after_expiration
= true;
548 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
550 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
551 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
552 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
553 observer_
.ExpectCachedLogo(&cached_logo
);
557 TEST_F(LogoTrackerTest
, RerequestSoftExpiredCachedLogo
) {
558 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
559 cached_logo
.metadata
.expiration_time
=
560 test_clock_
->Now() - base::TimeDelta::FromDays(5);
561 cached_logo
.metadata
.can_show_after_expiration
= true;
562 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
564 Logo fresh_logo
= GetSampleLogo2(logo_url_
, test_clock_
->Now());
565 SetServerResponse(ServerResponse(fresh_logo
));
567 logo_cache_
->ExpectSetCachedLogo(&fresh_logo
);
568 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
569 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
570 observer_
.ExpectCachedAndFreshLogos(&cached_logo
, &fresh_logo
);
575 TEST_F(LogoTrackerTest
, DeleteAncientCachedLogo
) {
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::FromDays(200);
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_
, SetCachedLogo(NULL
)).Times(AnyNumber());
586 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
587 observer_
.ExpectCachedLogo(NULL
);
591 TEST_F(LogoTrackerTest
, DeleteExpiredCachedLogo
) {
592 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
593 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
594 cached_logo
.metadata
.expiration_time
=
595 test_clock_
->Now() - base::TimeDelta::FromSeconds(1);
596 cached_logo
.metadata
.can_show_after_expiration
= false;
597 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
599 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
600 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
601 EXPECT_CALL(*logo_cache_
, SetCachedLogo(NULL
)).Times(AnyNumber());
602 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
603 observer_
.ExpectCachedLogo(NULL
);
607 // Tests that deal with multiple listeners.
609 void EnqueueObservers(LogoTracker
* logo_tracker
,
610 const ScopedVector
<MockLogoObserver
>& observers
,
611 size_t start_index
) {
612 if (start_index
>= observers
.size())
615 logo_tracker
->GetLogo(observers
[start_index
]);
616 base::MessageLoop::current()->PostTask(FROM_HERE
,
617 base::Bind(&EnqueueObservers
,
619 base::ConstRef(observers
),
623 TEST_F(LogoTrackerTest
, SupportOverlappingLogoRequests
) {
624 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
625 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
626 ON_CALL(*logo_cache_
, SetCachedLogo(_
)).WillByDefault(Return());
628 Logo fresh_logo
= GetSampleLogo2(logo_url_
, test_clock_
->Now());
629 std::string response
= ServerResponse(fresh_logo
);
630 SetServerResponse(response
);
631 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
, response
);
633 const int kNumListeners
= 10;
634 ScopedVector
<MockLogoObserver
> listeners
;
635 for (int i
= 0; i
< kNumListeners
; ++i
) {
636 MockLogoObserver
* listener
= new MockLogoObserver();
637 listener
->ExpectCachedAndFreshLogos(&cached_logo
, &fresh_logo
);
638 listeners
.push_back(listener
);
640 EnqueueObservers(logo_tracker_
, listeners
, 0);
642 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(AtMost(3));
643 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(3));
645 base::RunLoop().RunUntilIdle();
648 TEST_F(LogoTrackerTest
, DeleteObserversWhenLogoURLChanged
) {
649 MockLogoObserver listener1
;
650 listener1
.ExpectNoLogo();
651 logo_tracker_
->GetLogo(&listener1
);
653 logo_url_
= GURL("http://example.com/new-logo-url");
654 logo_tracker_
->SetServerAPI(logo_url_
,
655 base::Bind(&GoogleParseLogoResponse
),
656 base::Bind(&GoogleAppendFingerprintToLogoURL
));
657 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
658 SetServerResponse(ServerResponse(logo
));
660 MockLogoObserver listener2
;
661 listener2
.ExpectFreshLogo(&logo
);
662 logo_tracker_
->GetLogo(&listener2
);
664 base::RunLoop().RunUntilIdle();
669 } // namespace search_provider_logos