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
.allocN32Pixels(width
, height
);
70 bitmap
.eraseColor(SK_ColorBLUE
);
74 EncodedLogo
EncodeLogo(const Logo
& logo
) {
75 EncodedLogo encoded_logo
;
76 encoded_logo
.encoded_image
= EncodeBitmapAsPNG(logo
.image
);
77 encoded_logo
.metadata
= logo
.metadata
;
81 Logo
DecodeLogo(const EncodedLogo
& encoded_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
;
90 Logo
GetSampleLogo(const GURL
& logo_url
, base::Time response_time
) {
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";
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/jpeg";
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
;
128 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 if (!animated_url
.empty())
133 dict
.SetString("update.logo.url", animated_url
);
134 if (!mime_type
.empty())
135 dict
.SetString("update.logo.mime_type", mime_type
);
136 dict
.SetString("update.logo.fingerprint", fingerprint
);
137 if (time_to_live
.ToInternalValue() != 0)
138 dict
.SetInteger("update.logo.time_to_live",
139 static_cast<int>(time_to_live
.InMilliseconds()));
142 base::JSONWriter::Write(&dict
, &output
);
146 std::string
MakeServerResponse(const Logo
& logo
, base::TimeDelta time_to_live
) {
147 return MakeServerResponse(logo
.image
,
148 logo
.metadata
.on_click_url
,
149 logo
.metadata
.alt_text
,
150 logo
.metadata
.animated_url
,
151 logo
.metadata
.mime_type
,
152 logo
.metadata
.fingerprint
,
156 void ExpectLogosEqual(const Logo
* expected_logo
,
157 const Logo
* actual_logo
) {
158 if (!expected_logo
) {
159 ASSERT_FALSE(actual_logo
);
162 ASSERT_TRUE(actual_logo
);
163 EXPECT_TRUE(AreImagesSameSize(expected_logo
->image
, actual_logo
->image
));
164 EXPECT_EQ(expected_logo
->metadata
.on_click_url
,
165 actual_logo
->metadata
.on_click_url
);
166 EXPECT_EQ(expected_logo
->metadata
.source_url
,
167 actual_logo
->metadata
.source_url
);
168 EXPECT_EQ(expected_logo
->metadata
.animated_url
,
169 actual_logo
->metadata
.animated_url
);
170 EXPECT_EQ(expected_logo
->metadata
.alt_text
,
171 actual_logo
->metadata
.alt_text
);
172 EXPECT_EQ(expected_logo
->metadata
.mime_type
,
173 actual_logo
->metadata
.mime_type
);
174 EXPECT_EQ(expected_logo
->metadata
.fingerprint
,
175 actual_logo
->metadata
.fingerprint
);
176 EXPECT_EQ(expected_logo
->metadata
.can_show_after_expiration
,
177 actual_logo
->metadata
.can_show_after_expiration
);
180 void ExpectLogosEqual(const Logo
* expected_logo
,
181 const EncodedLogo
* actual_encoded_logo
) {
183 if (actual_encoded_logo
)
184 actual_logo
= DecodeLogo(*actual_encoded_logo
);
185 ExpectLogosEqual(expected_logo
, actual_encoded_logo
? &actual_logo
: NULL
);
188 ACTION_P(ExpectLogosEqualAction
, expected_logo
) {
189 ExpectLogosEqual(expected_logo
, arg0
);
192 class MockLogoCache
: public LogoCache
{
194 MockLogoCache() : LogoCache(base::FilePath()) {
195 // Delegate actions to the *Internal() methods by default.
196 ON_CALL(*this, UpdateCachedLogoMetadata(_
)).WillByDefault(
197 Invoke(this, &MockLogoCache::UpdateCachedLogoMetadataInternal
));
198 ON_CALL(*this, GetCachedLogoMetadata()).WillByDefault(
199 Invoke(this, &MockLogoCache::GetCachedLogoMetadataInternal
));
200 ON_CALL(*this, SetCachedLogo(_
))
201 .WillByDefault(Invoke(this, &MockLogoCache::SetCachedLogoInternal
));
204 MOCK_METHOD1(UpdateCachedLogoMetadata
, void(const LogoMetadata
& metadata
));
205 MOCK_METHOD0(GetCachedLogoMetadata
, const LogoMetadata
*());
206 MOCK_METHOD1(SetCachedLogo
, void(const EncodedLogo
* logo
));
207 // GetCachedLogo() can't be mocked since it returns a scoped_ptr, which is
208 // non-copyable. Instead create a method that's pinged when GetCachedLogo() is
210 MOCK_METHOD0(OnGetCachedLogo
, void());
212 void EncodeAndSetCachedLogo(const Logo
& logo
) {
213 EncodedLogo encoded_logo
= EncodeLogo(logo
);
214 SetCachedLogo(&encoded_logo
);
217 void ExpectSetCachedLogo(const Logo
* expected_logo
) {
218 Mock::VerifyAndClearExpectations(this);
219 EXPECT_CALL(*this, SetCachedLogo(_
))
220 .WillOnce(ExpectLogosEqualAction(expected_logo
));
223 void UpdateCachedLogoMetadataInternal(const LogoMetadata
& metadata
) {
224 ASSERT_TRUE(logo_
.get());
225 ASSERT_TRUE(metadata_
.get());
226 EXPECT_EQ(metadata_
->fingerprint
, metadata
.fingerprint
);
227 metadata_
.reset(new LogoMetadata(metadata
));
228 logo_
->metadata
= metadata
;
231 virtual const LogoMetadata
* GetCachedLogoMetadataInternal() {
232 return metadata_
.get();
235 virtual void SetCachedLogoInternal(const EncodedLogo
* logo
) {
236 logo_
.reset(logo
? new EncodedLogo(*logo
) : NULL
);
237 metadata_
.reset(logo
? new LogoMetadata(logo
->metadata
) : NULL
);
240 scoped_ptr
<EncodedLogo
> GetCachedLogo() override
{
242 return make_scoped_ptr(logo_
? new EncodedLogo(*logo_
) : NULL
);
246 scoped_ptr
<LogoMetadata
> metadata_
;
247 scoped_ptr
<EncodedLogo
> logo_
;
250 class MockLogoObserver
: public LogoObserver
{
252 virtual ~MockLogoObserver() {}
254 void ExpectNoLogo() {
255 Mock::VerifyAndClearExpectations(this);
256 EXPECT_CALL(*this, OnLogoAvailable(_
, _
)).Times(0);
257 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
260 void ExpectCachedLogo(const Logo
* expected_cached_logo
) {
261 Mock::VerifyAndClearExpectations(this);
262 EXPECT_CALL(*this, OnLogoAvailable(_
, true))
263 .WillOnce(ExpectLogosEqualAction(expected_cached_logo
));
264 EXPECT_CALL(*this, OnLogoAvailable(_
, false)).Times(0);
265 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
268 void ExpectFreshLogo(const Logo
* expected_fresh_logo
) {
269 Mock::VerifyAndClearExpectations(this);
270 EXPECT_CALL(*this, OnLogoAvailable(_
, true)).Times(0);
271 EXPECT_CALL(*this, OnLogoAvailable(NULL
, true));
272 EXPECT_CALL(*this, OnLogoAvailable(_
, false))
273 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo
));
274 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
277 void ExpectCachedAndFreshLogos(const Logo
* expected_cached_logo
,
278 const Logo
* expected_fresh_logo
) {
279 Mock::VerifyAndClearExpectations(this);
281 EXPECT_CALL(*this, OnLogoAvailable(_
, true))
282 .WillOnce(ExpectLogosEqualAction(expected_cached_logo
));
283 EXPECT_CALL(*this, OnLogoAvailable(_
, false))
284 .WillOnce(ExpectLogosEqualAction(expected_fresh_logo
));
285 EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
288 MOCK_METHOD2(OnLogoAvailable
, void(const Logo
*, bool));
289 MOCK_METHOD0(OnObserverRemoved
, void());
292 class TestLogoDelegate
: public LogoDelegate
{
294 TestLogoDelegate() {}
295 ~TestLogoDelegate() override
{}
297 void DecodeUntrustedImage(
298 const scoped_refptr
<base::RefCountedString
>& encoded_image
,
299 base::Callback
<void(const SkBitmap
&)> image_decoded_callback
) override
{
301 gfx::Image::CreateFrom1xPNGBytes(encoded_image
->front(),
302 encoded_image
->size()).AsBitmap();
303 base::MessageLoopProxy::current()->PostTask(
304 FROM_HERE
, base::Bind(image_decoded_callback
, bitmap
));
308 class LogoTrackerTest
: public ::testing::Test
{
311 : message_loop_(new base::MessageLoop()),
312 logo_url_("https://google.com/doodleoftheday?size=hp"),
313 test_clock_(new base::SimpleTestClock()),
314 logo_cache_(new NiceMock
<MockLogoCache
>()),
315 fake_url_fetcher_factory_(NULL
) {
316 test_clock_
->SetNow(base::Time::FromJsTime(GG_INT64_C(1388686828000)));
317 logo_tracker_
= new LogoTracker(
319 base::MessageLoopProxy::current(),
320 base::MessageLoopProxy::current(),
321 new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()),
322 scoped_ptr
<LogoDelegate
>(new TestLogoDelegate()));
323 logo_tracker_
->SetServerAPI(logo_url_
, base::Bind(&GoogleParseLogoResponse
),
324 base::Bind(&GoogleAppendQueryparamsToLogoURL
),
326 logo_tracker_
->SetClockForTests(scoped_ptr
<base::Clock
>(test_clock_
));
327 logo_tracker_
->SetLogoCacheForTests(scoped_ptr
<LogoCache
>(logo_cache_
));
330 virtual void TearDown() {
331 // logo_tracker_ owns logo_cache_, which gets destructed on the file thread
332 // after logo_tracker_'s destruction. Ensure that logo_cache_ is actually
333 // destructed before the test ends to make gmock happy.
334 delete logo_tracker_
;
335 message_loop_
->RunUntilIdle();
338 // Returns the response that the server would send for the given logo.
339 std::string
ServerResponse(const Logo
& logo
) const;
341 // Sets the response to be returned when the LogoTracker fetches the logo.
342 void SetServerResponse(const std::string
& response
,
343 net::URLRequestStatus::Status request_status
=
344 net::URLRequestStatus::SUCCESS
,
345 net::HttpStatusCode response_code
= net::HTTP_OK
);
347 // Sets the response to be returned when the LogoTracker fetches the logo and
348 // provides the given fingerprint.
349 void SetServerResponseWhenFingerprint(
350 const std::string
& fingerprint
,
351 const std::string
& response_when_fingerprint
,
352 net::URLRequestStatus::Status request_status
=
353 net::URLRequestStatus::SUCCESS
,
354 net::HttpStatusCode response_code
= net::HTTP_OK
);
356 // Calls logo_tracker_->GetLogo() with listener_ and waits for the
357 // asynchronous response(s).
360 scoped_ptr
<base::MessageLoop
> message_loop_
;
362 base::SimpleTestClock
* test_clock_
;
363 NiceMock
<MockLogoCache
>* logo_cache_
;
364 net::FakeURLFetcherFactory fake_url_fetcher_factory_
;
365 LogoTracker
* logo_tracker_
;
366 NiceMock
<MockLogoObserver
> observer_
;
369 std::string
LogoTrackerTest::ServerResponse(const Logo
& logo
) const {
370 base::TimeDelta time_to_live
;
371 if (!logo
.metadata
.expiration_time
.is_null())
372 time_to_live
= logo
.metadata
.expiration_time
- test_clock_
->Now();
373 return MakeServerResponse(logo
, time_to_live
);
376 void LogoTrackerTest::SetServerResponse(
377 const std::string
& response
,
378 net::URLRequestStatus::Status request_status
,
379 net::HttpStatusCode response_code
) {
380 fake_url_fetcher_factory_
.SetFakeResponse(
381 logo_url_
, response
, response_code
, request_status
);
384 void LogoTrackerTest::SetServerResponseWhenFingerprint(
385 const std::string
& fingerprint
,
386 const std::string
& response_when_fingerprint
,
387 net::URLRequestStatus::Status request_status
,
388 net::HttpStatusCode response_code
) {
390 GoogleAppendQueryparamsToLogoURL(logo_url_
, fingerprint
, false);
391 fake_url_fetcher_factory_
.SetFakeResponse(
392 url_with_fp
, response_when_fingerprint
, response_code
, request_status
);
395 void LogoTrackerTest::GetLogo() {
396 logo_tracker_
->GetLogo(&observer_
);
397 base::RunLoop().RunUntilIdle();
400 // Tests -----------------------------------------------------------------------
402 TEST_F(LogoTrackerTest
, FingerprintURLHasColon
) {
403 GURL url_with_fp
= GoogleAppendQueryparamsToLogoURL(
404 GURL("http://logourl.com/path"), "abc123", false);
405 EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123", url_with_fp
.spec());
407 url_with_fp
= GoogleAppendQueryparamsToLogoURL(
408 GURL("http://logourl.com/?a=b"), "cafe0", false);
409 EXPECT_EQ("http://logourl.com/?a=b&async=es_dfp:cafe0", url_with_fp
.spec());
412 TEST_F(LogoTrackerTest
, CTAURLHasComma
) {
413 GURL url_with_fp
= GoogleAppendQueryparamsToLogoURL(
414 GURL("http://logourl.com/path"), "abc123", true);
415 EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123,cta:1",
418 url_with_fp
= GoogleAppendQueryparamsToLogoURL(
419 GURL("http://logourl.com/?a=b"), "", true);
420 EXPECT_EQ("http://logourl.com/?a=b&async=cta:1", url_with_fp
.spec());
423 TEST_F(LogoTrackerTest
, DownloadAndCacheLogo
) {
424 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
425 SetServerResponse(ServerResponse(logo
));
426 logo_cache_
->ExpectSetCachedLogo(&logo
);
427 observer_
.ExpectFreshLogo(&logo
);
431 TEST_F(LogoTrackerTest
, EmptyCacheAndFailedDownload
) {
432 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
433 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
434 EXPECT_CALL(*logo_cache_
, SetCachedLogo(NULL
)).Times(AnyNumber());
436 SetServerResponse("server is borked");
437 observer_
.ExpectCachedLogo(NULL
);
440 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
441 observer_
.ExpectCachedLogo(NULL
);
444 SetServerResponse("", net::URLRequestStatus::SUCCESS
, net::HTTP_BAD_GATEWAY
);
445 observer_
.ExpectCachedLogo(NULL
);
449 TEST_F(LogoTrackerTest
, AcceptMinimalLogoResponse
) {
451 logo
.image
= MakeBitmap(1, 2);
452 logo
.metadata
.source_url
= logo_url_
.spec();
453 logo
.metadata
.can_show_after_expiration
= true;
454 logo
.metadata
.mime_type
= "image/png";
456 std::string response
= ")]}' {\"update\":{\"logo\":{\"data\":\"" +
457 EncodeBitmapAsPNGBase64(logo
.image
) +
458 "\",\"mime_type\":\"image/png\"}}}";
460 SetServerResponse(response
);
461 observer_
.ExpectFreshLogo(&logo
);
465 TEST_F(LogoTrackerTest
, ReturnCachedLogo
) {
466 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
467 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
468 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
,
470 net::URLRequestStatus::FAILED
,
473 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
474 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
475 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
476 observer_
.ExpectCachedLogo(&cached_logo
);
480 TEST_F(LogoTrackerTest
, ValidateCachedLogo
) {
481 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
482 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
484 // During revalidation, the image data and mime_type are absent.
485 Logo fresh_logo
= cached_logo
;
486 fresh_logo
.image
.reset();
487 fresh_logo
.metadata
.mime_type
.clear();
488 fresh_logo
.metadata
.expiration_time
=
489 test_clock_
->Now() + base::TimeDelta::FromDays(8);
490 SetServerResponseWhenFingerprint(fresh_logo
.metadata
.fingerprint
,
491 ServerResponse(fresh_logo
));
493 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(1);
494 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
495 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
496 observer_
.ExpectCachedLogo(&cached_logo
);
499 EXPECT_TRUE(logo_cache_
->GetCachedLogoMetadata() != NULL
);
500 EXPECT_EQ(fresh_logo
.metadata
.expiration_time
,
501 logo_cache_
->GetCachedLogoMetadata()->expiration_time
);
503 // Ensure that cached logo is still returned correctly on subsequent requests.
504 // In particular, the metadata should stay valid. http://crbug.com/480090
505 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(1);
506 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
507 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
508 observer_
.ExpectCachedLogo(&cached_logo
);
512 TEST_F(LogoTrackerTest
, UpdateCachedLogoMetadata
) {
513 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
514 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
516 Logo fresh_logo
= cached_logo
;
517 fresh_logo
.image
.reset();
518 fresh_logo
.metadata
.mime_type
.clear();
519 fresh_logo
.metadata
.on_click_url
= "http://new.onclick.url";
520 fresh_logo
.metadata
.alt_text
= "new alt text";
521 fresh_logo
.metadata
.animated_url
= "http://new.animated.url";
522 fresh_logo
.metadata
.expiration_time
=
523 test_clock_
->Now() + base::TimeDelta::FromDays(8);
524 SetServerResponseWhenFingerprint(fresh_logo
.metadata
.fingerprint
,
525 ServerResponse(fresh_logo
));
527 // On the first request, the cached logo should be used.
528 observer_
.ExpectCachedLogo(&cached_logo
);
531 // Subsequently, the cached image should be returned along with the updated
533 Logo expected_logo
= fresh_logo
;
534 expected_logo
.image
= cached_logo
.image
;
535 expected_logo
.metadata
.mime_type
= cached_logo
.metadata
.mime_type
;
536 observer_
.ExpectCachedLogo(&expected_logo
);
540 TEST_F(LogoTrackerTest
, UpdateCachedLogo
) {
541 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
542 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
544 Logo fresh_logo
= GetSampleLogo2(logo_url_
, test_clock_
->Now());
545 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
,
546 ServerResponse(fresh_logo
));
548 logo_cache_
->ExpectSetCachedLogo(&fresh_logo
);
549 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
550 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
551 observer_
.ExpectCachedAndFreshLogos(&cached_logo
, &fresh_logo
);
556 TEST_F(LogoTrackerTest
, InvalidateCachedLogo
) {
557 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
558 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
560 // This response means there's no current logo.
561 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
,
562 ")]}' {\"update\":{}}");
564 logo_cache_
->ExpectSetCachedLogo(NULL
);
565 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
566 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
567 observer_
.ExpectCachedAndFreshLogos(&cached_logo
, NULL
);
572 TEST_F(LogoTrackerTest
, DeleteCachedLogoFromOldUrl
) {
573 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
575 GetSampleLogo(GURL("http://oldsearchprovider.com"), test_clock_
->Now());
576 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
578 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
579 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
580 EXPECT_CALL(*logo_cache_
, SetCachedLogo(NULL
)).Times(AnyNumber());
581 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
582 observer_
.ExpectCachedLogo(NULL
);
586 TEST_F(LogoTrackerTest
, LogoWithTTLCannotBeShownAfterExpiration
) {
587 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
588 base::TimeDelta time_to_live
= base::TimeDelta::FromDays(3);
589 logo
.metadata
.expiration_time
= test_clock_
->Now() + time_to_live
;
590 SetServerResponse(ServerResponse(logo
));
593 const LogoMetadata
* cached_metadata
=
594 logo_cache_
->GetCachedLogoMetadata();
595 EXPECT_TRUE(cached_metadata
!= NULL
);
596 EXPECT_FALSE(cached_metadata
->can_show_after_expiration
);
597 EXPECT_EQ(test_clock_
->Now() + time_to_live
,
598 cached_metadata
->expiration_time
);
601 TEST_F(LogoTrackerTest
, LogoWithoutTTLCanBeShownAfterExpiration
) {
602 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
603 base::TimeDelta time_to_live
= base::TimeDelta();
604 SetServerResponse(MakeServerResponse(logo
, time_to_live
));
607 const LogoMetadata
* cached_metadata
=
608 logo_cache_
->GetCachedLogoMetadata();
609 EXPECT_TRUE(cached_metadata
!= NULL
);
610 EXPECT_TRUE(cached_metadata
->can_show_after_expiration
);
611 EXPECT_EQ(test_clock_
->Now() + base::TimeDelta::FromDays(30),
612 cached_metadata
->expiration_time
);
615 TEST_F(LogoTrackerTest
, UseSoftExpiredCachedLogo
) {
616 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
617 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
618 cached_logo
.metadata
.expiration_time
=
619 test_clock_
->Now() - base::TimeDelta::FromSeconds(1);
620 cached_logo
.metadata
.can_show_after_expiration
= true;
621 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
623 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
624 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
625 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
626 observer_
.ExpectCachedLogo(&cached_logo
);
630 TEST_F(LogoTrackerTest
, RerequestSoftExpiredCachedLogo
) {
631 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
632 cached_logo
.metadata
.expiration_time
=
633 test_clock_
->Now() - base::TimeDelta::FromDays(5);
634 cached_logo
.metadata
.can_show_after_expiration
= true;
635 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
637 Logo fresh_logo
= GetSampleLogo2(logo_url_
, test_clock_
->Now());
638 SetServerResponse(ServerResponse(fresh_logo
));
640 logo_cache_
->ExpectSetCachedLogo(&fresh_logo
);
641 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
642 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
643 observer_
.ExpectCachedAndFreshLogos(&cached_logo
, &fresh_logo
);
648 TEST_F(LogoTrackerTest
, DeleteAncientCachedLogo
) {
649 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
650 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
651 cached_logo
.metadata
.expiration_time
=
652 test_clock_
->Now() - base::TimeDelta::FromDays(200);
653 cached_logo
.metadata
.can_show_after_expiration
= true;
654 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
656 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
657 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
658 EXPECT_CALL(*logo_cache_
, SetCachedLogo(NULL
)).Times(AnyNumber());
659 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
660 observer_
.ExpectCachedLogo(NULL
);
664 TEST_F(LogoTrackerTest
, DeleteExpiredCachedLogo
) {
665 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
666 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
667 cached_logo
.metadata
.expiration_time
=
668 test_clock_
->Now() - base::TimeDelta::FromSeconds(1);
669 cached_logo
.metadata
.can_show_after_expiration
= false;
670 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
672 EXPECT_CALL(*logo_cache_
, UpdateCachedLogoMetadata(_
)).Times(0);
673 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(0);
674 EXPECT_CALL(*logo_cache_
, SetCachedLogo(NULL
)).Times(AnyNumber());
675 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(1));
676 observer_
.ExpectCachedLogo(NULL
);
680 // Tests that deal with multiple listeners.
682 void EnqueueObservers(LogoTracker
* logo_tracker
,
683 const ScopedVector
<MockLogoObserver
>& observers
,
684 size_t start_index
) {
685 if (start_index
>= observers
.size())
688 logo_tracker
->GetLogo(observers
[start_index
]);
689 base::MessageLoop::current()->PostTask(FROM_HERE
,
690 base::Bind(&EnqueueObservers
,
692 base::ConstRef(observers
),
696 TEST_F(LogoTrackerTest
, SupportOverlappingLogoRequests
) {
697 Logo cached_logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
698 logo_cache_
->EncodeAndSetCachedLogo(cached_logo
);
699 ON_CALL(*logo_cache_
, SetCachedLogo(_
)).WillByDefault(Return());
701 Logo fresh_logo
= GetSampleLogo2(logo_url_
, test_clock_
->Now());
702 std::string response
= ServerResponse(fresh_logo
);
703 SetServerResponse(response
);
704 SetServerResponseWhenFingerprint(cached_logo
.metadata
.fingerprint
, response
);
706 const int kNumListeners
= 10;
707 ScopedVector
<MockLogoObserver
> listeners
;
708 for (int i
= 0; i
< kNumListeners
; ++i
) {
709 MockLogoObserver
* listener
= new MockLogoObserver();
710 listener
->ExpectCachedAndFreshLogos(&cached_logo
, &fresh_logo
);
711 listeners
.push_back(listener
);
713 EnqueueObservers(logo_tracker_
, listeners
, 0);
715 EXPECT_CALL(*logo_cache_
, SetCachedLogo(_
)).Times(AtMost(3));
716 EXPECT_CALL(*logo_cache_
, OnGetCachedLogo()).Times(AtMost(3));
718 base::RunLoop().RunUntilIdle();
721 TEST_F(LogoTrackerTest
, DeleteObserversWhenLogoURLChanged
) {
722 MockLogoObserver listener1
;
723 listener1
.ExpectNoLogo();
724 logo_tracker_
->GetLogo(&listener1
);
726 logo_url_
= GURL("http://example.com/new-logo-url");
727 logo_tracker_
->SetServerAPI(logo_url_
, base::Bind(&GoogleParseLogoResponse
),
728 base::Bind(&GoogleAppendQueryparamsToLogoURL
),
730 Logo logo
= GetSampleLogo(logo_url_
, test_clock_
->Now());
731 SetServerResponse(ServerResponse(logo
));
733 MockLogoObserver listener2
;
734 listener2
.ExpectFreshLogo(&logo
);
735 logo_tracker_
->GetLogo(&listener2
);
737 base::RunLoop().RunUntilIdle();
742 } // namespace search_provider_logos