Add Platform Verification Mojo Services.
[chromium-blink-merge.git] / components / search_provider_logos / logo_tracker_unittest.cc
blob590985a304ea4b442d21733997725fdf47f62de7
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/jpeg";
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));
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()));
141 std::string output;
142 base::JSONWriter::Write(&dict, &output);
143 return 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,
153 time_to_live);
156 void ExpectLogosEqual(const Logo* expected_logo,
157 const Logo* actual_logo) {
158 if (!expected_logo) {
159 ASSERT_FALSE(actual_logo);
160 return;
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) {
182 Logo actual_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 {
193 public:
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
209 // called.
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 {
241 OnGetCachedLogo();
242 return make_scoped_ptr(logo_ ? new EncodedLogo(*logo_) : NULL);
245 private:
246 scoped_ptr<LogoMetadata> metadata_;
247 scoped_ptr<EncodedLogo> logo_;
250 class MockLogoObserver : public LogoObserver {
251 public:
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);
280 InSequence dummy;
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 {
293 public:
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 {
300 SkBitmap bitmap =
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 {
309 protected:
310 LogoTrackerTest()
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(
318 base::FilePath(),
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),
325 false);
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).
358 void GetLogo();
360 scoped_ptr<base::MessageLoop> message_loop_;
361 GURL logo_url_;
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) {
389 GURL url_with_fp =
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",
416 url_with_fp.spec());
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);
428 GetLogo();
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);
438 GetLogo();
440 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
441 observer_.ExpectCachedLogo(NULL);
442 GetLogo();
444 SetServerResponse("", net::URLRequestStatus::SUCCESS, net::HTTP_BAD_GATEWAY);
445 observer_.ExpectCachedLogo(NULL);
446 GetLogo();
449 TEST_F(LogoTrackerTest, AcceptMinimalLogoResponse) {
450 Logo logo;
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);
462 GetLogo();
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,
471 net::HTTP_OK);
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);
477 GetLogo();
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);
497 GetLogo();
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);
509 GetLogo();
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);
529 GetLogo();
531 // Subsequently, the cached image should be returned along with the updated
532 // metadata.
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);
537 GetLogo();
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);
553 GetLogo();
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);
569 GetLogo();
572 TEST_F(LogoTrackerTest, DeleteCachedLogoFromOldUrl) {
573 SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
574 Logo cached_logo =
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);
583 GetLogo();
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));
591 GetLogo();
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));
605 GetLogo();
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);
627 GetLogo();
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);
645 GetLogo();
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);
661 GetLogo();
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);
677 GetLogo();
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())
686 return;
688 logo_tracker->GetLogo(observers[start_index]);
689 base::MessageLoop::current()->PostTask(FROM_HERE,
690 base::Bind(&EnqueueObservers,
691 logo_tracker,
692 base::ConstRef(observers),
693 start_index + 1));
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),
729 false);
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();
740 } // namespace
742 } // namespace search_provider_logos