Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / components / search_provider_logos / logo_tracker_unittest.cc
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.
4
5 #include "components/search_provider_logos/logo_tracker.h"
6
7 #include <vector>
8
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"
32
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;
41
42 namespace search_provider_logos {
43
44 namespace {
45
46 bool AreImagesSameSize(const SkBitmap& bitmap1, const SkBitmap& bitmap2) {
47   return bitmap1.width() == bitmap2.width() &&
48          bitmap1.height() == bitmap2.height();
49 }
50
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;
58 }
59
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;
65 }
66
67 SkBitmap MakeBitmap(int width, int height) {
68   SkBitmap bitmap;
69   bitmap.allocN32Pixels(width, height);
70   bitmap.eraseColor(SK_ColorBLUE);
71   return bitmap;
72 }
73
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;
79 }
80
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;
88 }
89
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.mime_type = "image/png";
101   return logo;
102 }
103
104 Logo GetSampleLogo2(const GURL& logo_url, base::Time response_time) {
105   Logo logo;
106   logo.image = MakeBitmap(4, 3);
107   logo.metadata.can_show_after_expiration = true;
108   logo.metadata.expiration_time = base::Time();
109   logo.metadata.fingerprint = "71082741021409127";
110   logo.metadata.source_url = logo_url.spec();
111   logo.metadata.on_click_url = "http://example.com/page25";
112   logo.metadata.alt_text = "The logo for example.com";
113   logo.metadata.mime_type = "image/png";
114   return logo;
115 }
116
117 std::string MakeServerResponse(
118     const SkBitmap& image,
119     const std::string& on_click_url,
120     const std::string& alt_text,
121     const std::string& mime_type,
122     const std::string& fingerprint,
123     base::TimeDelta time_to_live) {
124   base::DictionaryValue dict;
125   if (!image.isNull()) {
126     dict.SetString("update.logo.data", EncodeBitmapAsPNGBase64(image));
127   }
128
129   dict.SetString("update.logo.target", on_click_url);
130   dict.SetString("update.logo.alt", alt_text);
131   dict.SetString("update.logo.mime_type", mime_type);
132   dict.SetString("update.logo.fingerprint", fingerprint);
133   if (time_to_live.ToInternalValue() != 0)
134     dict.SetInteger("update.logo.time_to_live",
135                     static_cast<int>(time_to_live.InMilliseconds()));
136
137   std::string output;
138   base::JSONWriter::Write(&dict, &output);
139   return output;
140 }
141
142 std::string MakeServerResponse(const Logo& logo, base::TimeDelta time_to_live) {
143   return MakeServerResponse(logo.image,
144                             logo.metadata.on_click_url,
145                             logo.metadata.alt_text,
146                             logo.metadata.mime_type,
147                             logo.metadata.fingerprint,
148                             time_to_live);
149 }
150
151 void ExpectLogosEqual(const Logo* expected_logo,
152                       const Logo* actual_logo) {
153   if (!expected_logo) {
154     ASSERT_FALSE(actual_logo);
155     return;
156   }
157   ASSERT_TRUE(actual_logo);
158   EXPECT_TRUE(AreImagesSameSize(expected_logo->image, actual_logo->image));
159   EXPECT_EQ(expected_logo->metadata.on_click_url,
160             actual_logo->metadata.on_click_url);
161   EXPECT_EQ(expected_logo->metadata.source_url,
162             actual_logo->metadata.source_url);
163   EXPECT_EQ(expected_logo->metadata.fingerprint,
164             actual_logo->metadata.fingerprint);
165   EXPECT_EQ(expected_logo->metadata.can_show_after_expiration,
166             actual_logo->metadata.can_show_after_expiration);
167 }
168
169 void ExpectLogosEqual(const Logo* expected_logo,
170                       const EncodedLogo* actual_encoded_logo) {
171   Logo actual_logo;
172   if (actual_encoded_logo)
173     actual_logo = DecodeLogo(*actual_encoded_logo);
174   ExpectLogosEqual(expected_logo, actual_encoded_logo ? &actual_logo : NULL);
175 }
176
177 ACTION_P(ExpectLogosEqualAction, expected_logo) {
178   ExpectLogosEqual(expected_logo, arg0);
179 }
180
181 class MockLogoCache : public LogoCache {
182  public:
183   MockLogoCache() : LogoCache(base::FilePath()) {
184     // Delegate actions to the *Internal() methods by default.
185     ON_CALL(*this, UpdateCachedLogoMetadata(_)).WillByDefault(
186         Invoke(this, &MockLogoCache::UpdateCachedLogoMetadataInternal));
187     ON_CALL(*this, GetCachedLogoMetadata()).WillByDefault(
188         Invoke(this, &MockLogoCache::GetCachedLogoMetadataInternal));
189     ON_CALL(*this, SetCachedLogo(_))
190         .WillByDefault(Invoke(this, &MockLogoCache::SetCachedLogoInternal));
191   }
192
193   MOCK_METHOD1(UpdateCachedLogoMetadata, void(const LogoMetadata& metadata));
194   MOCK_METHOD0(GetCachedLogoMetadata, const LogoMetadata*());
195   MOCK_METHOD1(SetCachedLogo, void(const EncodedLogo* logo));
196   // GetCachedLogo() can't be mocked since it returns a scoped_ptr, which is
197   // non-copyable. Instead create a method that's pinged when GetCachedLogo() is
198   // called.
199   MOCK_METHOD0(OnGetCachedLogo, void());
200
201   void EncodeAndSetCachedLogo(const Logo& logo) {
202     EncodedLogo encoded_logo = EncodeLogo(logo);
203     SetCachedLogo(&encoded_logo);
204   }
205
206   void ExpectSetCachedLogo(const Logo* expected_logo) {
207     Mock::VerifyAndClearExpectations(this);
208     EXPECT_CALL(*this, SetCachedLogo(_))
209         .WillOnce(ExpectLogosEqualAction(expected_logo));
210   }
211
212   void UpdateCachedLogoMetadataInternal(const LogoMetadata& metadata) {
213     metadata_.reset(new LogoMetadata(metadata));
214   }
215
216   virtual const LogoMetadata* GetCachedLogoMetadataInternal() {
217     return metadata_.get();
218   }
219
220   virtual void SetCachedLogoInternal(const EncodedLogo* logo) {
221     logo_.reset(logo ? new EncodedLogo(*logo) : NULL);
222     metadata_.reset(logo ? new LogoMetadata(logo->metadata) : NULL);
223   }
224
225   virtual scoped_ptr<EncodedLogo> GetCachedLogo() OVERRIDE {
226     OnGetCachedLogo();
227     return make_scoped_ptr(logo_ ? new EncodedLogo(*logo_) : NULL);
228   }
229
230  private:
231   scoped_ptr<LogoMetadata> metadata_;
232   scoped_ptr<EncodedLogo> logo_;
233 };
234
235 class MockLogoObserver : public LogoObserver {
236  public:
237   virtual ~MockLogoObserver() {}
238
239   void ExpectNoLogo() {
240     Mock::VerifyAndClearExpectations(this);
241     EXPECT_CALL(*this, OnLogoAvailable(_, _)).Times(0);
242     EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
243   }
244
245   void ExpectCachedLogo(const Logo* expected_cached_logo) {
246     Mock::VerifyAndClearExpectations(this);
247     EXPECT_CALL(*this, OnLogoAvailable(_, true))
248         .WillOnce(ExpectLogosEqualAction(expected_cached_logo));
249     EXPECT_CALL(*this, OnLogoAvailable(_, false)).Times(0);
250     EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
251   }
252
253   void ExpectFreshLogo(const Logo* expected_fresh_logo) {
254     Mock::VerifyAndClearExpectations(this);
255     EXPECT_CALL(*this, OnLogoAvailable(_, true)).Times(0);
256     EXPECT_CALL(*this, OnLogoAvailable(NULL, true));
257     EXPECT_CALL(*this, OnLogoAvailable(_, false))
258         .WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
259     EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
260   }
261
262   void ExpectCachedAndFreshLogos(const Logo* expected_cached_logo,
263                                  const Logo* expected_fresh_logo) {
264     Mock::VerifyAndClearExpectations(this);
265     InSequence dummy;
266     EXPECT_CALL(*this, OnLogoAvailable(_, true))
267         .WillOnce(ExpectLogosEqualAction(expected_cached_logo));
268     EXPECT_CALL(*this, OnLogoAvailable(_, false))
269         .WillOnce(ExpectLogosEqualAction(expected_fresh_logo));
270     EXPECT_CALL(*this, OnObserverRemoved()).Times(1);
271   }
272
273   MOCK_METHOD2(OnLogoAvailable, void(const Logo*, bool));
274   MOCK_METHOD0(OnObserverRemoved, void());
275 };
276
277 class TestLogoDelegate : public LogoDelegate {
278  public:
279   TestLogoDelegate() {}
280   virtual ~TestLogoDelegate() {}
281
282   virtual void DecodeUntrustedImage(
283       const scoped_refptr<base::RefCountedString>& encoded_image,
284       base::Callback<void(const SkBitmap&)> image_decoded_callback) OVERRIDE {
285     SkBitmap bitmap =
286         gfx::Image::CreateFrom1xPNGBytes(encoded_image->front(),
287                                          encoded_image->size()).AsBitmap();
288     base::MessageLoopProxy::current()->PostTask(
289         FROM_HERE, base::Bind(image_decoded_callback, bitmap));
290   }
291 };
292
293 class LogoTrackerTest : public ::testing::Test {
294  protected:
295   LogoTrackerTest()
296       : message_loop_(new base::MessageLoop()),
297         logo_url_("https://google.com/doodleoftheday?size=hp"),
298         test_clock_(new base::SimpleTestClock()),
299         logo_cache_(new NiceMock<MockLogoCache>()),
300         fake_url_fetcher_factory_(NULL) {
301     test_clock_->SetNow(base::Time::FromJsTime(GG_INT64_C(1388686828000)));
302     logo_tracker_ = new LogoTracker(
303         base::FilePath(),
304         base::MessageLoopProxy::current(),
305         base::MessageLoopProxy::current(),
306         new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()),
307         scoped_ptr<LogoDelegate>(new TestLogoDelegate()));
308     logo_tracker_->SetServerAPI(logo_url_,
309                                 base::Bind(&GoogleParseLogoResponse),
310                                 base::Bind(&GoogleAppendFingerprintToLogoURL));
311     logo_tracker_->SetClockForTests(scoped_ptr<base::Clock>(test_clock_));
312     logo_tracker_->SetLogoCacheForTests(scoped_ptr<LogoCache>(logo_cache_));
313   }
314
315   virtual void TearDown() {
316     // logo_tracker_ owns logo_cache_, which gets destructed on the file thread
317     // after logo_tracker_'s destruction. Ensure that logo_cache_ is actually
318     // destructed before the test ends to make gmock happy.
319     delete logo_tracker_;
320     message_loop_->RunUntilIdle();
321   }
322
323   // Returns the response that the server would send for the given logo.
324   std::string ServerResponse(const Logo& logo) const;
325
326   // Sets the response to be returned when the LogoTracker fetches the logo.
327   void SetServerResponse(const std::string& response,
328                          net::URLRequestStatus::Status request_status =
329                              net::URLRequestStatus::SUCCESS,
330                          net::HttpStatusCode response_code = net::HTTP_OK);
331
332   // Sets the response to be returned when the LogoTracker fetches the logo and
333   // provides the given fingerprint.
334   void SetServerResponseWhenFingerprint(
335       const std::string& fingerprint,
336       const std::string& response_when_fingerprint,
337       net::URLRequestStatus::Status request_status =
338           net::URLRequestStatus::SUCCESS,
339       net::HttpStatusCode response_code = net::HTTP_OK);
340
341   // Calls logo_tracker_->GetLogo() with listener_ and waits for the
342   // asynchronous response(s).
343   void GetLogo();
344
345   scoped_ptr<base::MessageLoop> message_loop_;
346   GURL logo_url_;
347   base::SimpleTestClock* test_clock_;
348   NiceMock<MockLogoCache>* logo_cache_;
349   net::FakeURLFetcherFactory fake_url_fetcher_factory_;
350   LogoTracker* logo_tracker_;
351   NiceMock<MockLogoObserver> observer_;
352 };
353
354 std::string LogoTrackerTest::ServerResponse(const Logo& logo) const {
355   base::TimeDelta time_to_live;
356   if (!logo.metadata.expiration_time.is_null())
357     time_to_live = logo.metadata.expiration_time - test_clock_->Now();
358   return MakeServerResponse(logo, time_to_live);
359 }
360
361 void LogoTrackerTest::SetServerResponse(
362     const std::string& response,
363     net::URLRequestStatus::Status request_status,
364     net::HttpStatusCode response_code) {
365   fake_url_fetcher_factory_.SetFakeResponse(
366       logo_url_, response, response_code, request_status);
367 }
368
369 void LogoTrackerTest::SetServerResponseWhenFingerprint(
370     const std::string& fingerprint,
371     const std::string& response_when_fingerprint,
372     net::URLRequestStatus::Status request_status,
373     net::HttpStatusCode response_code) {
374   GURL url_with_fp = GoogleAppendFingerprintToLogoURL(logo_url_, fingerprint);
375   fake_url_fetcher_factory_.SetFakeResponse(
376       url_with_fp, response_when_fingerprint, response_code, request_status);
377 }
378
379 void LogoTrackerTest::GetLogo() {
380   logo_tracker_->GetLogo(&observer_);
381   base::RunLoop().RunUntilIdle();
382 }
383
384 // Tests -----------------------------------------------------------------------
385
386 TEST_F(LogoTrackerTest, FingerprintURLHasColon) {
387   GURL url_with_fp = GoogleAppendFingerprintToLogoURL(
388       GURL("http://logourl.com/path"), "abc123");
389   EXPECT_EQ("http://logourl.com/path?async=es_dfp:abc123", url_with_fp.spec());
390
391   url_with_fp = GoogleAppendFingerprintToLogoURL(
392       GURL("http://logourl.com/?a=b"), "cafe0");
393   EXPECT_EQ("http://logourl.com/?a=b&async=es_dfp:cafe0", url_with_fp.spec());
394 }
395
396 TEST_F(LogoTrackerTest, DownloadAndCacheLogo) {
397   Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
398   SetServerResponse(ServerResponse(logo));
399   logo_cache_->ExpectSetCachedLogo(&logo);
400   observer_.ExpectFreshLogo(&logo);
401   GetLogo();
402 }
403
404 TEST_F(LogoTrackerTest, EmptyCacheAndFailedDownload) {
405   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
406   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
407   EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
408
409   SetServerResponse("server is borked");
410   observer_.ExpectCachedLogo(NULL);
411   GetLogo();
412
413   SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
414   observer_.ExpectCachedLogo(NULL);
415   GetLogo();
416
417   SetServerResponse("", net::URLRequestStatus::SUCCESS, net::HTTP_BAD_GATEWAY);
418   observer_.ExpectCachedLogo(NULL);
419   GetLogo();
420 }
421
422 TEST_F(LogoTrackerTest, AcceptMinimalLogoResponse) {
423   Logo logo;
424   logo.image = MakeBitmap(1, 2);
425   logo.metadata.source_url = logo_url_.spec();
426   logo.metadata.can_show_after_expiration = true;
427
428   std::string response = ")]}' {\"update\":{\"logo\":{\"data\":\"" +
429                          EncodeBitmapAsPNGBase64(logo.image) +
430                          "\",\"mime_type\":\"image/png\"}}}";
431
432   SetServerResponse(response);
433   observer_.ExpectFreshLogo(&logo);
434   GetLogo();
435 }
436
437 TEST_F(LogoTrackerTest, ReturnCachedLogo) {
438   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
439   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
440   SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
441                                    "",
442                                    net::URLRequestStatus::FAILED,
443                                    net::HTTP_OK);
444
445   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
446   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
447   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
448   observer_.ExpectCachedLogo(&cached_logo);
449   GetLogo();
450 }
451
452 TEST_F(LogoTrackerTest, ValidateCachedLogoFingerprint) {
453   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
454   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
455
456   Logo fresh_logo = cached_logo;
457   fresh_logo.image.reset();
458   fresh_logo.metadata.expiration_time =
459       test_clock_->Now() + base::TimeDelta::FromDays(8);
460   SetServerResponseWhenFingerprint(fresh_logo.metadata.fingerprint,
461                                    ServerResponse(fresh_logo));
462
463   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(1);
464   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
465   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
466   observer_.ExpectCachedLogo(&cached_logo);
467
468   GetLogo();
469
470   EXPECT_TRUE(logo_cache_->GetCachedLogoMetadata() != NULL);
471   EXPECT_EQ(logo_cache_->GetCachedLogoMetadata()->expiration_time,
472             fresh_logo.metadata.expiration_time);
473 }
474
475 TEST_F(LogoTrackerTest, UpdateCachedLogo) {
476   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
477   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
478
479   Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
480   SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
481                                    ServerResponse(fresh_logo));
482
483   logo_cache_->ExpectSetCachedLogo(&fresh_logo);
484   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
485   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
486   observer_.ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
487
488   GetLogo();
489 }
490
491 TEST_F(LogoTrackerTest, InvalidateCachedLogo) {
492   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
493   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
494
495   // This response means there's no current logo.
496   SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint,
497                                    ")]}' {\"update\":{}}");
498
499   logo_cache_->ExpectSetCachedLogo(NULL);
500   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
501   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
502   observer_.ExpectCachedAndFreshLogos(&cached_logo, NULL);
503
504   GetLogo();
505 }
506
507 TEST_F(LogoTrackerTest, DeleteCachedLogoFromOldUrl) {
508   SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
509   Logo cached_logo =
510       GetSampleLogo(GURL("http://oldsearchprovider.com"), test_clock_->Now());
511   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
512
513   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
514   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
515   EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
516   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
517   observer_.ExpectCachedLogo(NULL);
518   GetLogo();
519 }
520
521 TEST_F(LogoTrackerTest, LogoWithTTLCannotBeShownAfterExpiration) {
522   Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
523   base::TimeDelta time_to_live = base::TimeDelta::FromDays(3);
524   logo.metadata.expiration_time = test_clock_->Now() + time_to_live;
525   SetServerResponse(ServerResponse(logo));
526   GetLogo();
527
528   const LogoMetadata* cached_metadata =
529       logo_cache_->GetCachedLogoMetadata();
530   EXPECT_TRUE(cached_metadata != NULL);
531   EXPECT_FALSE(cached_metadata->can_show_after_expiration);
532   EXPECT_EQ(test_clock_->Now() + time_to_live,
533             cached_metadata->expiration_time);
534 }
535
536 TEST_F(LogoTrackerTest, LogoWithoutTTLCanBeShownAfterExpiration) {
537   Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
538   base::TimeDelta time_to_live = base::TimeDelta();
539   SetServerResponse(MakeServerResponse(logo, time_to_live));
540   GetLogo();
541
542   const LogoMetadata* cached_metadata =
543       logo_cache_->GetCachedLogoMetadata();
544   EXPECT_TRUE(cached_metadata != NULL);
545   EXPECT_TRUE(cached_metadata->can_show_after_expiration);
546   EXPECT_EQ(test_clock_->Now() + base::TimeDelta::FromDays(30),
547             cached_metadata->expiration_time);
548 }
549
550 TEST_F(LogoTrackerTest, UseSoftExpiredCachedLogo) {
551   SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
552   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
553   cached_logo.metadata.expiration_time =
554       test_clock_->Now() - base::TimeDelta::FromSeconds(1);
555   cached_logo.metadata.can_show_after_expiration = true;
556   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
557
558   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
559   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
560   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
561   observer_.ExpectCachedLogo(&cached_logo);
562   GetLogo();
563 }
564
565 TEST_F(LogoTrackerTest, RerequestSoftExpiredCachedLogo) {
566   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
567   cached_logo.metadata.expiration_time =
568       test_clock_->Now() - base::TimeDelta::FromDays(5);
569   cached_logo.metadata.can_show_after_expiration = true;
570   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
571
572   Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
573   SetServerResponse(ServerResponse(fresh_logo));
574
575   logo_cache_->ExpectSetCachedLogo(&fresh_logo);
576   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
577   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
578   observer_.ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
579
580   GetLogo();
581 }
582
583 TEST_F(LogoTrackerTest, DeleteAncientCachedLogo) {
584   SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
585   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
586   cached_logo.metadata.expiration_time =
587       test_clock_->Now() - base::TimeDelta::FromDays(200);
588   cached_logo.metadata.can_show_after_expiration = true;
589   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
590
591   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
592   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
593   EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
594   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
595   observer_.ExpectCachedLogo(NULL);
596   GetLogo();
597 }
598
599 TEST_F(LogoTrackerTest, DeleteExpiredCachedLogo) {
600   SetServerResponse("", net::URLRequestStatus::FAILED, net::HTTP_OK);
601   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
602   cached_logo.metadata.expiration_time =
603       test_clock_->Now() - base::TimeDelta::FromSeconds(1);
604   cached_logo.metadata.can_show_after_expiration = false;
605   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
606
607   EXPECT_CALL(*logo_cache_, UpdateCachedLogoMetadata(_)).Times(0);
608   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(0);
609   EXPECT_CALL(*logo_cache_, SetCachedLogo(NULL)).Times(AnyNumber());
610   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(1));
611   observer_.ExpectCachedLogo(NULL);
612   GetLogo();
613 }
614
615 // Tests that deal with multiple listeners.
616
617 void EnqueueObservers(LogoTracker* logo_tracker,
618                       const ScopedVector<MockLogoObserver>& observers,
619                       size_t start_index) {
620   if (start_index >= observers.size())
621     return;
622
623   logo_tracker->GetLogo(observers[start_index]);
624   base::MessageLoop::current()->PostTask(FROM_HERE,
625                                          base::Bind(&EnqueueObservers,
626                                                     logo_tracker,
627                                                     base::ConstRef(observers),
628                                                     start_index + 1));
629 }
630
631 TEST_F(LogoTrackerTest, SupportOverlappingLogoRequests) {
632   Logo cached_logo = GetSampleLogo(logo_url_, test_clock_->Now());
633   logo_cache_->EncodeAndSetCachedLogo(cached_logo);
634   ON_CALL(*logo_cache_, SetCachedLogo(_)).WillByDefault(Return());
635
636   Logo fresh_logo = GetSampleLogo2(logo_url_, test_clock_->Now());
637   std::string response = ServerResponse(fresh_logo);
638   SetServerResponse(response);
639   SetServerResponseWhenFingerprint(cached_logo.metadata.fingerprint, response);
640
641   const int kNumListeners = 10;
642   ScopedVector<MockLogoObserver> listeners;
643   for (int i = 0; i < kNumListeners; ++i) {
644     MockLogoObserver* listener = new MockLogoObserver();
645     listener->ExpectCachedAndFreshLogos(&cached_logo, &fresh_logo);
646     listeners.push_back(listener);
647   }
648   EnqueueObservers(logo_tracker_, listeners, 0);
649
650   EXPECT_CALL(*logo_cache_, SetCachedLogo(_)).Times(AtMost(3));
651   EXPECT_CALL(*logo_cache_, OnGetCachedLogo()).Times(AtMost(3));
652
653   base::RunLoop().RunUntilIdle();
654 }
655
656 TEST_F(LogoTrackerTest, DeleteObserversWhenLogoURLChanged) {
657   MockLogoObserver listener1;
658   listener1.ExpectNoLogo();
659   logo_tracker_->GetLogo(&listener1);
660
661   logo_url_ = GURL("http://example.com/new-logo-url");
662   logo_tracker_->SetServerAPI(logo_url_,
663                               base::Bind(&GoogleParseLogoResponse),
664                               base::Bind(&GoogleAppendFingerprintToLogoURL));
665   Logo logo = GetSampleLogo(logo_url_, test_clock_->Now());
666   SetServerResponse(ServerResponse(logo));
667
668   MockLogoObserver listener2;
669   listener2.ExpectFreshLogo(&logo);
670   logo_tracker_->GetLogo(&listener2);
671
672   base::RunLoop().RunUntilIdle();
673 }
674
675 }  // namespace
676
677 }  // namespace search_provider_logos