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"
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"
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
{
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());
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
) {
73 bitmap
.allocN32Pixels(width
, height
);
74 bitmap
.eraseColor(SK_ColorBLUE
);
78 EncodedLogo
EncodeLogo(const Logo
& logo
) {
79 EncodedLogo encoded_logo
;
80 encoded_logo
.encoded_image
= EncodeBitmapAsPNG(logo
.image
);
81 encoded_logo
.metadata
= logo
.metadata
;
85 Logo
DecodeLogo(const EncodedLogo
& encoded_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
;
94 Logo
GetSampleLogo(const GURL
& logo_url
, base::Time response_time
) {
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";
109 Logo
GetSampleLogo2(const GURL
& logo_url
, base::Time response_time
) {
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";
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
;
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()));
146 base::JSONWriter::Write(dict
, &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
,
160 void ExpectLogosEqual(const Logo
* expected_logo
,
161 const Logo
* actual_logo
) {
162 if (!expected_logo
) {
163 ASSERT_FALSE(actual_logo
);
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
) {
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
{
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
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
{
246 return make_scoped_ptr(logo_
? new EncodedLogo(*logo_
) : NULL
);
250 scoped_ptr
<LogoMetadata
> metadata_
;
251 scoped_ptr
<EncodedLogo
> logo_
;
254 class MockLogoObserver
: public LogoObserver
{
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);
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
{
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
{
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
{
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)));
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
),
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).
364 scoped_ptr
<base::MessageLoop
> message_loop_
;
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
) {
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",
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
);
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
);
444 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
445 observer_
.ExpectCachedLogo(NULL
);
448 SetServerResponse("", net::URLRequestStatus::SUCCESS
, net::HTTP_BAD_GATEWAY
);
449 observer_
.ExpectCachedLogo(NULL
);
453 TEST_F(LogoTrackerTest
, AcceptMinimalLogoResponse
) {
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
);
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
,
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
);
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
);
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
);
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
);
535 // Subsequently, the cached image should be returned along with the updated
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
);
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
);
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
);
576 TEST_F(LogoTrackerTest
, DeleteCachedLogoFromOldUrl
) {
577 SetServerResponse("", net::URLRequestStatus::FAILED
, net::HTTP_OK
);
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
);
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
));
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
));
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
);
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
);
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
);
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
);
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())
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
),
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();
744 } // namespace search_provider_logos