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