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
32using testing::DoAll;
33using ::testing::Eq;
34using ::testing::Return;
35using testing::SetArgPointee;
36using ::testing::NiceMock;
37using ::testing::StrictMock;
38using ::testing::_;
39
40namespace {
41
42const char kFakeSuggestionsURL[] = "https://mysuggestions.com/proto";
43const char kFakeSuggestionsCommonParams[] = "foo=bar";
44const char kFakeBlacklistPath[] = "/blacklist";
45const char kFakeBlacklistUrlParam[] = "baz";
46
47const char kTestTitle[] = "a title";
48const char kTestUrl[] = "http://go.com";
49const char kBlacklistUrl[] = "http://blacklist.com";
50const int64 kTestDefaultExpiry = 1402200000000000;
51const int64 kTestSetExpiry = 1404792000000000;
52
53scoped_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
69std::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.
78MATCHER_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
90namespace suggestions {
91
92scoped_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.
102SuggestionsProfile 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
116class 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
123class 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
133class 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
141class 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
287TEST_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
296TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoTimeout) {
297  FetchSuggestionsDataNoTimeoutHelper(false);
298}
299
300TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoTimeoutInterleaved) {
301  FetchSuggestionsDataNoTimeoutHelper(true);
302}
303
304TEST_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
342TEST_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
378TEST_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
403TEST_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
434TEST_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.
479TEST_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
541TEST_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
570TEST_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
588TEST_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