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