Home | History | Annotate | Download | only in suggestions
      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/suggestions/suggestions_service.h"
      6 
      7 #include <map>
      8 #include <sstream>
      9 #include <string>
     10 
     11 #include "base/bind.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/metrics/field_trial.h"
     15 #include "base/prefs/pref_service.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "components/suggestions/blacklist_store.h"
     18 #include "components/suggestions/image_manager.h"
     19 #include "components/suggestions/proto/suggestions.pb.h"
     20 #include "components/suggestions/suggestions_store.h"
     21 #include "components/suggestions/suggestions_utils.h"
     22 #include "components/variations/entropy_provider.h"
     23 #include "components/variations/variations_associated_data.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 
     32 using testing::DoAll;
     33 using ::testing::Eq;
     34 using ::testing::Return;
     35 using testing::SetArgPointee;
     36 using ::testing::NiceMock;
     37 using ::testing::StrictMock;
     38 using ::testing::_;
     39 
     40 namespace {
     41 
     42 const char kFakeSuggestionsURL[] = "https://mysuggestions.com/proto";
     43 const char kFakeSuggestionsCommonParams[] = "foo=bar";
     44 const char kFakeBlacklistPath[] = "/blacklist";
     45 const char kFakeBlacklistUrlParam[] = "baz";
     46 
     47 const char kTestTitle[] = "a title";
     48 const char kTestUrl[] = "http://go.com";
     49 const char kBlacklistUrl[] = "http://blacklist.com";
     50 const int64 kTestDefaultExpiry = 1402200000000000;
     51 const int64 kTestSetExpiry = 1404792000000000;
     52 
     53 scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
     54     const GURL& url, net::URLFetcherDelegate* delegate,
     55     const std::string& response_data, net::HttpStatusCode response_code,
     56     net::URLRequestStatus::Status status) {
     57   scoped_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher(
     58       url, delegate, response_data, response_code, status));
     59 
     60   if (response_code == net::HTTP_OK) {
     61     scoped_refptr<net::HttpResponseHeaders> download_headers(
     62         new net::HttpResponseHeaders(""));
     63     download_headers->AddHeader("Content-Type: text/html");
     64     fetcher->set_response_headers(download_headers);
     65   }
     66   return fetcher.Pass();
     67 }
     68 
     69 std::string GetExpectedBlacklistRequestUrl(const GURL& blacklist_url) {
     70   std::stringstream request_url;
     71   request_url << kFakeSuggestionsURL << kFakeBlacklistPath << "?"
     72               << kFakeSuggestionsCommonParams << "&" << kFakeBlacklistUrlParam
     73               << "=" << net::EscapeQueryParamValue(blacklist_url.spec(), true);
     74   return request_url.str();
     75 }
     76 
     77 // GMock matcher for protobuf equality.
     78 MATCHER_P(EqualsProto, message, "") {
     79   // This implementation assumes protobuf serialization is deterministic, which
     80   // is true in practice but technically not something that code is supposed
     81   // to rely on.  However, it vastly simplifies the implementation.
     82   std::string expected_serialized, actual_serialized;
     83   message.SerializeToString(&expected_serialized);
     84   arg.SerializeToString(&actual_serialized);
     85   return expected_serialized == actual_serialized;
     86 }
     87 
     88 }  // namespace
     89 
     90 namespace suggestions {
     91 
     92 scoped_ptr<SuggestionsProfile> CreateSuggestionsProfile() {
     93   scoped_ptr<SuggestionsProfile> profile(new SuggestionsProfile());
     94   ChromeSuggestion* suggestion = profile->add_suggestions();
     95   suggestion->set_title(kTestTitle);
     96   suggestion->set_url(kTestUrl);
     97   suggestion->set_expiry_ts(kTestSetExpiry);
     98   return profile.Pass();
     99 }
    100 
    101 // Creates one suggestion with expiry timestamp and one without.
    102 SuggestionsProfile CreateSuggestionsProfileWithExpiryTimestamps() {
    103   SuggestionsProfile profile;
    104   ChromeSuggestion* suggestion = profile.add_suggestions();
    105   suggestion->set_title(kTestTitle);
    106   suggestion->set_url(kTestUrl);
    107   suggestion->set_expiry_ts(kTestSetExpiry);
    108 
    109   suggestion = profile.add_suggestions();
    110   suggestion->set_title(kTestTitle);
    111   suggestion->set_url(kTestUrl);
    112 
    113   return profile;
    114 }
    115 
    116 class MockSuggestionsStore : public suggestions::SuggestionsStore {
    117  public:
    118   MOCK_METHOD1(LoadSuggestions, bool(SuggestionsProfile*));
    119   MOCK_METHOD1(StoreSuggestions, bool(const SuggestionsProfile&));
    120   MOCK_METHOD0(ClearSuggestions, void());
    121 };
    122 
    123 class MockImageManager : public suggestions::ImageManager {
    124  public:
    125   MockImageManager() {}
    126   virtual ~MockImageManager() {}
    127   MOCK_METHOD1(Initialize, void(const SuggestionsProfile&));
    128   MOCK_METHOD2(GetImageForURL,
    129                void(const GURL&,
    130                     base::Callback<void(const GURL&, const SkBitmap*)>));
    131 };
    132 
    133 class MockBlacklistStore : public suggestions::BlacklistStore {
    134  public:
    135   MOCK_METHOD1(BlacklistUrl, bool(const GURL&));
    136   MOCK_METHOD1(GetFirstUrlFromBlacklist, bool(GURL*));
    137   MOCK_METHOD1(RemoveUrl, bool(const GURL&));
    138   MOCK_METHOD1(FilterSuggestions, void(SuggestionsProfile*));
    139 };
    140 
    141 class SuggestionsServiceTest : public testing::Test {
    142  public:
    143   void CheckSuggestionsData(const SuggestionsProfile& suggestions_profile) {
    144     EXPECT_EQ(1, suggestions_profile.suggestions_size());
    145     EXPECT_EQ(kTestTitle, suggestions_profile.suggestions(0).title());
    146     EXPECT_EQ(kTestUrl, suggestions_profile.suggestions(0).url());
    147     ++suggestions_data_check_count_;
    148   }
    149 
    150   void ExpectEmptySuggestionsProfile(const SuggestionsProfile& profile) {
    151     EXPECT_EQ(0, profile.suggestions_size());
    152     ++suggestions_empty_data_count_;
    153   }
    154 
    155   int suggestions_data_check_count_;
    156   int suggestions_empty_data_count_;
    157 
    158  protected:
    159   SuggestionsServiceTest()
    160       : suggestions_data_check_count_(0),
    161         suggestions_empty_data_count_(0),
    162         factory_(NULL, base::Bind(&CreateURLFetcher)),
    163         mock_suggestions_store_(NULL),
    164         mock_thumbnail_manager_(NULL) {}
    165 
    166   virtual ~SuggestionsServiceTest() {}
    167 
    168   virtual void SetUp() OVERRIDE {
    169     request_context_ = new net::TestURLRequestContextGetter(
    170         io_message_loop_.message_loop_proxy());
    171   }
    172 
    173   // Enables the "ChromeSuggestions.Group1" field trial.
    174   void EnableFieldTrial(const std::string& url,
    175                         const std::string& common_params,
    176                         const std::string& blacklist_path,
    177                         const std::string& blacklist_url_param,
    178                         bool control_group) {
    179     // Clear the existing |field_trial_list_| to avoid firing a DCHECK.
    180     field_trial_list_.reset(NULL);
    181     field_trial_list_.reset(
    182         new base::FieldTrialList(new metrics::SHA1EntropyProvider("foo")));
    183 
    184     variations::testing::ClearAllVariationParams();
    185     std::map<std::string, std::string> params;
    186     params[kSuggestionsFieldTrialStateParam] =
    187         kSuggestionsFieldTrialStateEnabled;
    188     if (control_group) {
    189       params[kSuggestionsFieldTrialControlParam] =
    190           kSuggestionsFieldTrialStateEnabled;
    191     }
    192     params[kSuggestionsFieldTrialURLParam] = url;
    193     params[kSuggestionsFieldTrialCommonParamsParam] = common_params;
    194     params[kSuggestionsFieldTrialBlacklistPathParam] = blacklist_path;
    195     params[kSuggestionsFieldTrialBlacklistUrlParam] = blacklist_url_param;
    196     variations::AssociateVariationParams(kSuggestionsFieldTrialName, "Group1",
    197                                          params);
    198     field_trial_ = base::FieldTrialList::CreateFieldTrial(
    199         kSuggestionsFieldTrialName, "Group1");
    200     field_trial_->group();
    201   }
    202 
    203   // Should not be called more than once per test since it stashes the
    204   // SuggestionsStore in |mock_suggestions_store_|.
    205   SuggestionsService* CreateSuggestionsServiceWithMocks() {
    206     mock_suggestions_store_ = new StrictMock<MockSuggestionsStore>();
    207     mock_thumbnail_manager_ = new StrictMock<MockImageManager>();
    208     mock_blacklist_store_ = new MockBlacklistStore();
    209     return new SuggestionsService(
    210         request_context_.get(),
    211         scoped_ptr<SuggestionsStore>(mock_suggestions_store_),
    212         scoped_ptr<ImageManager>(mock_thumbnail_manager_),
    213         scoped_ptr<BlacklistStore>(mock_blacklist_store_));
    214   }
    215 
    216   void FetchSuggestionsDataNoTimeoutHelper(bool interleaved_requests) {
    217     // Field trial enabled with a specific suggestions URL.
    218     EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    219                      kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    220     scoped_ptr<SuggestionsService> suggestions_service(
    221         CreateSuggestionsServiceWithMocks());
    222     EXPECT_TRUE(suggestions_service != NULL);
    223     scoped_ptr<SuggestionsProfile> suggestions_profile(
    224         CreateSuggestionsProfile());
    225     // Set up net::FakeURLFetcherFactory.
    226     std::string expected_url =
    227         (std::string(kFakeSuggestionsURL) + "?") + kFakeSuggestionsCommonParams;
    228     factory_.SetFakeResponse(GURL(expected_url),
    229                              suggestions_profile->SerializeAsString(),
    230                              net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    231     // Set up expectations on the SuggestionsStore. The number depends on
    232     // whether the second request is issued (it won't be issued if the second
    233     // fetch occurs before the first request has completed).
    234     int expected_count = interleaved_requests ? 1 : 2;
    235     EXPECT_CALL(*mock_suggestions_store_,
    236                 StoreSuggestions(EqualsProto(*suggestions_profile)))
    237         .Times(expected_count)
    238         .WillRepeatedly(Return(true));
    239 
    240     // Since there are two requests below, Initialize() will be called twice.
    241     EXPECT_CALL(*mock_thumbnail_manager_,
    242                 Initialize(EqualsProto(*suggestions_profile)))
    243         .Times(expected_count);
    244 
    245     // Expect a call to the blacklist store. Return that there's nothing to
    246     // blacklist.
    247     EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_))
    248         .Times(expected_count);
    249     EXPECT_CALL(*mock_blacklist_store_, GetFirstUrlFromBlacklist(_))
    250         .Times(expected_count)
    251         .WillRepeatedly(Return(false));
    252 
    253     // Send the request. The data will be returned to the callback.
    254     suggestions_service->FetchSuggestionsDataNoTimeout(base::Bind(
    255         &SuggestionsServiceTest::CheckSuggestionsData, base::Unretained(this)));
    256 
    257     if (!interleaved_requests)
    258       io_message_loop_.RunUntilIdle();  // Let request complete.
    259 
    260     // Send the request a second time.
    261     suggestions_service->FetchSuggestionsDataNoTimeout(base::Bind(
    262         &SuggestionsServiceTest::CheckSuggestionsData, base::Unretained(this)));
    263 
    264     // (Testing only) wait until suggestion fetch is complete.
    265     io_message_loop_.RunUntilIdle();
    266 
    267     // Ensure that CheckSuggestionsData() ran twice.
    268     EXPECT_EQ(2, suggestions_data_check_count_);
    269   }
    270 
    271  protected:
    272   base::MessageLoopForIO io_message_loop_;
    273   net::FakeURLFetcherFactory factory_;
    274   // Only used if the SuggestionsService is built with mocks. Not owned.
    275   MockSuggestionsStore* mock_suggestions_store_;
    276   MockImageManager* mock_thumbnail_manager_;
    277   MockBlacklistStore* mock_blacklist_store_;
    278   scoped_refptr<net::TestURLRequestContextGetter> request_context_;
    279 
    280  private:
    281   scoped_ptr<base::FieldTrialList> field_trial_list_;
    282   scoped_refptr<base::FieldTrial> field_trial_;
    283 
    284   DISALLOW_COPY_AND_ASSIGN(SuggestionsServiceTest);
    285 };
    286 
    287 TEST_F(SuggestionsServiceTest, IsControlGroup) {
    288   // Field trial enabled.
    289   EnableFieldTrial("", "", "", "", false);
    290   EXPECT_FALSE(SuggestionsService::IsControlGroup());
    291 
    292   EnableFieldTrial("", "", "", "", true);
    293   EXPECT_TRUE(SuggestionsService::IsControlGroup());
    294 }
    295 
    296 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoTimeout) {
    297   FetchSuggestionsDataNoTimeoutHelper(false);
    298 }
    299 
    300 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoTimeoutInterleaved) {
    301   FetchSuggestionsDataNoTimeoutHelper(true);
    302 }
    303 
    304 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataRequestError) {
    305   // Field trial enabled with a specific suggestions URL.
    306   EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    307                    kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    308   scoped_ptr<SuggestionsService> suggestions_service(
    309       CreateSuggestionsServiceWithMocks());
    310   EXPECT_TRUE(suggestions_service != NULL);
    311 
    312   // Fake a request error.
    313   std::string expected_url =
    314       (std::string(kFakeSuggestionsURL) + "?") + kFakeSuggestionsCommonParams;
    315   factory_.SetFakeResponse(GURL(expected_url), "irrelevant", net::HTTP_OK,
    316                            net::URLRequestStatus::FAILED);
    317 
    318   // Set up expectations on the SuggestionsStore.
    319   EXPECT_CALL(*mock_suggestions_store_, LoadSuggestions(_))
    320       .WillOnce(Return(true));
    321   EXPECT_CALL(*mock_thumbnail_manager_, Initialize(_));
    322 
    323   // Expect a call to the blacklist store. Return that there's nothing to
    324   // blacklist.
    325   EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_));
    326   EXPECT_CALL(*mock_blacklist_store_, GetFirstUrlFromBlacklist(_))
    327       .WillOnce(Return(false));
    328 
    329   // Send the request. Empty data will be returned to the callback.
    330   suggestions_service->FetchSuggestionsData(
    331       INITIALIZED_ENABLED_HISTORY,  // Normal mode.
    332       base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile,
    333                  base::Unretained(this)));
    334 
    335   // (Testing only) wait until suggestion fetch is complete.
    336   io_message_loop_.RunUntilIdle();
    337 
    338   // Ensure that ExpectEmptySuggestionsProfile ran once.
    339   EXPECT_EQ(1, suggestions_empty_data_count_);
    340 }
    341 
    342 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataResponseNotOK) {
    343   // Field trial enabled with a specific suggestions URL.
    344   EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    345                    kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    346   scoped_ptr<SuggestionsService> suggestions_service(
    347       CreateSuggestionsServiceWithMocks());
    348   EXPECT_TRUE(suggestions_service != NULL);
    349 
    350   // Response code != 200.
    351   std::string expected_url =
    352       (std::string(kFakeSuggestionsURL) + "?") + kFakeSuggestionsCommonParams;
    353   factory_.SetFakeResponse(GURL(expected_url), "irrelevant",
    354                            net::HTTP_BAD_REQUEST,
    355                            net::URLRequestStatus::SUCCESS);
    356 
    357   // Set up expectations on the SuggestionsStore.
    358   EXPECT_CALL(*mock_suggestions_store_, ClearSuggestions());
    359 
    360   // Expect a call to the blacklist store. Return that there's nothing to
    361   // blacklist.
    362   EXPECT_CALL(*mock_blacklist_store_, GetFirstUrlFromBlacklist(_))
    363       .WillOnce(Return(false));
    364 
    365   // Send the request. Empty data will be returned to the callback.
    366   suggestions_service->FetchSuggestionsData(
    367       INITIALIZED_ENABLED_HISTORY,  // Normal mode.
    368       base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile,
    369                  base::Unretained(this)));
    370 
    371   // (Testing only) wait until suggestion fetch is complete.
    372   io_message_loop_.RunUntilIdle();
    373 
    374   // Ensure that ExpectEmptySuggestionsProfile ran once.
    375   EXPECT_EQ(1, suggestions_empty_data_count_);
    376 }
    377 
    378 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) {
    379   // Field trial enabled with a specific suggestions URL.
    380   EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    381                    kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    382   scoped_ptr<SuggestionsService> suggestions_service(
    383       CreateSuggestionsServiceWithMocks());
    384   EXPECT_TRUE(suggestions_service != NULL);
    385 
    386   // Set up expectations on the SuggestionsStore.
    387   EXPECT_CALL(*mock_suggestions_store_, ClearSuggestions());
    388 
    389   // Send the request. Cache is cleared and empty data will be returned to the
    390   // callback.
    391   suggestions_service->FetchSuggestionsData(
    392       SYNC_OR_HISTORY_SYNC_DISABLED,
    393       base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile,
    394                  base::Unretained(this)));
    395 
    396   // Wait for posted task to complete.
    397   base::MessageLoop::current()->RunUntilIdle();
    398 
    399   // Ensure that ExpectEmptySuggestionsProfile ran once.
    400   EXPECT_EQ(1, suggestions_empty_data_count_);
    401 }
    402 
    403 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
    404   // Field trial enabled with a specific suggestions URL.
    405   EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    406                    kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    407   scoped_ptr<SuggestionsService> suggestions_service(
    408       CreateSuggestionsServiceWithMocks());
    409   EXPECT_TRUE(suggestions_service != NULL);
    410   scoped_ptr<SuggestionsProfile> suggestions_profile(
    411       CreateSuggestionsProfile());
    412 
    413   // Expectations.
    414   EXPECT_CALL(*mock_suggestions_store_, LoadSuggestions(_))
    415       .WillOnce(DoAll(SetArgPointee<0>(*suggestions_profile), Return(true)));
    416   EXPECT_CALL(*mock_thumbnail_manager_,
    417               Initialize(EqualsProto(*suggestions_profile)));
    418   EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_));
    419 
    420   // Send the request. In this state, cached data will be returned to the
    421   // caller.
    422   suggestions_service->FetchSuggestionsData(
    423       NOT_INITIALIZED_ENABLED,
    424       base::Bind(&SuggestionsServiceTest::CheckSuggestionsData,
    425                  base::Unretained(this)));
    426 
    427   // Wait for posted task to complete.
    428   base::MessageLoop::current()->RunUntilIdle();
    429 
    430   // Ensure that CheckSuggestionsData ran once.
    431   EXPECT_EQ(1, suggestions_data_check_count_);
    432 }
    433 
    434 TEST_F(SuggestionsServiceTest, BlacklistURL) {
    435   EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    436                    kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    437   scoped_ptr<SuggestionsService> suggestions_service(
    438       CreateSuggestionsServiceWithMocks());
    439   EXPECT_TRUE(suggestions_service != NULL);
    440 
    441   GURL blacklist_url(kBlacklistUrl);
    442   std::string request_url = GetExpectedBlacklistRequestUrl(blacklist_url);
    443   scoped_ptr<SuggestionsProfile> suggestions_profile(
    444       CreateSuggestionsProfile());
    445   factory_.SetFakeResponse(GURL(request_url),
    446                            suggestions_profile->SerializeAsString(),
    447                            net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    448 
    449   // Set up expectations on the SuggestionsStore.
    450   EXPECT_CALL(*mock_suggestions_store_,
    451               StoreSuggestions(EqualsProto(*suggestions_profile)))
    452       .WillOnce(Return(true));
    453   EXPECT_CALL(*mock_thumbnail_manager_,
    454               Initialize(EqualsProto(*suggestions_profile)));
    455 
    456   // Expected calls to the blacklist store.
    457   EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklist_url)))
    458       .WillOnce(Return(true));
    459   EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklist_url)))
    460       .WillOnce(Return(true));
    461   EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_));
    462   EXPECT_CALL(*mock_blacklist_store_, GetFirstUrlFromBlacklist(_))
    463       .WillOnce(Return(false));
    464 
    465   // Send the request. The data will be returned to the callback.
    466   suggestions_service->BlacklistURL(
    467       blacklist_url, base::Bind(&SuggestionsServiceTest::CheckSuggestionsData,
    468                                 base::Unretained(this)));
    469 
    470   // (Testing only) wait until blacklist request is complete.
    471   io_message_loop_.RunUntilIdle();
    472 
    473   // Ensure that CheckSuggestionsData() ran once.
    474   EXPECT_EQ(1, suggestions_data_check_count_);
    475 }
    476 
    477 // Initial blacklist request fails, triggering a scheduled upload which
    478 // succeeds.
    479 TEST_F(SuggestionsServiceTest, BlacklistURLFails) {
    480   EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    481                    kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    482   scoped_ptr<SuggestionsService> suggestions_service(
    483       CreateSuggestionsServiceWithMocks());
    484   EXPECT_TRUE(suggestions_service != NULL);
    485   suggestions_service->set_blacklist_delay(0);  // Don't wait during a test!
    486   scoped_ptr<SuggestionsProfile> suggestions_profile(
    487       CreateSuggestionsProfile());
    488   GURL blacklist_url(kBlacklistUrl);
    489 
    490   // Set up behavior for the first call to blacklist.
    491   std::string request_url = GetExpectedBlacklistRequestUrl(blacklist_url);
    492   factory_.SetFakeResponse(GURL(request_url), "irrelevant", net::HTTP_OK,
    493                            net::URLRequestStatus::FAILED);
    494 
    495   // Expectations specific to the first request.
    496   EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklist_url)))
    497       .WillOnce(Return(true));
    498   EXPECT_CALL(*mock_suggestions_store_, LoadSuggestions(_))
    499       .WillOnce(DoAll(SetArgPointee<0>(*suggestions_profile), Return(true)));
    500 
    501   // Expectations specific to the second request.
    502   EXPECT_CALL(*mock_suggestions_store_,
    503               StoreSuggestions(EqualsProto(*suggestions_profile)))
    504       .WillOnce(Return(true));
    505   EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklist_url)))
    506       .WillOnce(Return(true));
    507 
    508   // Expectations pertaining to both requests.
    509   EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)).Times(2);
    510   EXPECT_CALL(*mock_blacklist_store_, GetFirstUrlFromBlacklist(_))
    511       .WillOnce(Return(true))
    512       .WillOnce(DoAll(SetArgPointee<0>(blacklist_url), Return(true)))
    513       .WillOnce(Return(false));
    514   // There will be two calls to Initialize() (one store, one load).
    515   EXPECT_CALL(*mock_thumbnail_manager_,
    516               Initialize(EqualsProto(*suggestions_profile)))
    517       .Times(2);
    518 
    519   // Send the request. The data will be returned to the callback.
    520   suggestions_service->BlacklistURL(
    521       blacklist_url, base::Bind(&SuggestionsServiceTest::CheckSuggestionsData,
    522                                 base::Unretained(this)));
    523 
    524   // The first FakeURLFetcher was created; we can now set up behavior for the
    525   // second call to blacklist.
    526   factory_.SetFakeResponse(GURL(request_url),
    527                            suggestions_profile->SerializeAsString(),
    528                            net::HTTP_OK, net::URLRequestStatus::SUCCESS);
    529 
    530   // (Testing only) wait until both requests are complete.
    531   io_message_loop_.RunUntilIdle();
    532   // ... Other task gets posted to the message loop.
    533   base::MessageLoop::current()->RunUntilIdle();
    534   // ... And completes.
    535   io_message_loop_.RunUntilIdle();
    536 
    537   // Ensure that CheckSuggestionsData() ran once.
    538   EXPECT_EQ(1, suggestions_data_check_count_);
    539 }
    540 
    541 TEST_F(SuggestionsServiceTest, GetBlacklistedUrl) {
    542   EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams,
    543                    kFakeBlacklistPath, kFakeBlacklistUrlParam, false);
    544 
    545   scoped_ptr<GURL> request_url;
    546   scoped_ptr<net::FakeURLFetcher> fetcher;
    547   GURL retrieved_url;
    548 
    549   // Not a blacklist request.
    550   request_url.reset(new GURL("http://not-blacklisting.com/a?b=c"));
    551   fetcher = CreateURLFetcher(*request_url, NULL, "", net::HTTP_OK,
    552                              net::URLRequestStatus::SUCCESS);
    553   EXPECT_FALSE(SuggestionsService::GetBlacklistedUrl(*fetcher, &retrieved_url));
    554 
    555   // An actual blacklist request.
    556   string blacklisted_url = "http://blacklisted.com/a?b=c&d=e";
    557   string encoded_blacklisted_url =
    558       "http%3A%2F%2Fblacklisted.com%2Fa%3Fb%3Dc%26d%3De";
    559   string blacklist_request_prefix =
    560       "https://mysuggestions.com/proto/blacklist?foo=bar&baz=";
    561   request_url.reset(
    562       new GURL(blacklist_request_prefix + encoded_blacklisted_url));
    563   fetcher.reset();
    564   fetcher = CreateURLFetcher(*request_url, NULL, "", net::HTTP_OK,
    565                              net::URLRequestStatus::SUCCESS);
    566   EXPECT_TRUE(SuggestionsService::GetBlacklistedUrl(*fetcher, &retrieved_url));
    567   EXPECT_EQ(blacklisted_url, retrieved_url.spec());
    568 }
    569 
    570 TEST_F(SuggestionsServiceTest, UpdateBlacklistDelay) {
    571   scoped_ptr<SuggestionsService> suggestions_service(
    572       CreateSuggestionsServiceWithMocks());
    573   int initial_delay = suggestions_service->blacklist_delay();
    574 
    575   // Delay unchanged on success.
    576   suggestions_service->UpdateBlacklistDelay(true);
    577   EXPECT_EQ(initial_delay, suggestions_service->blacklist_delay());
    578 
    579   // Delay increases on failure.
    580   suggestions_service->UpdateBlacklistDelay(false);
    581   EXPECT_GT(suggestions_service->blacklist_delay(), initial_delay);
    582 
    583   // Delay resets on success.
    584   suggestions_service->UpdateBlacklistDelay(true);
    585   EXPECT_EQ(initial_delay, suggestions_service->blacklist_delay());
    586 }
    587 
    588 TEST_F(SuggestionsServiceTest, CheckDefaultTimeStamps) {
    589   scoped_ptr<SuggestionsService> suggestions_service(
    590       CreateSuggestionsServiceWithMocks());
    591   SuggestionsProfile suggestions =
    592       CreateSuggestionsProfileWithExpiryTimestamps();
    593   suggestions_service->SetDefaultExpiryTimestamp(&suggestions,
    594                                                  kTestDefaultExpiry);
    595   EXPECT_EQ(kTestSetExpiry, suggestions.suggestions(0).expiry_ts());
    596   EXPECT_EQ(kTestDefaultExpiry, suggestions.suggestions(1).expiry_ts());
    597 }
    598 }  // namespace suggestions
    599