| OLD | NEW |
| (Empty) |
| 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 <stdint.h> | |
| 8 | |
| 9 #include <memory> | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/feature_list.h" | |
| 14 #include "base/macros.h" | |
| 15 #include "base/memory/ptr_util.h" | |
| 16 #include "base/run_loop.h" | |
| 17 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" | |
| 18 #include "components/suggestions/blacklist_store.h" | |
| 19 #include "components/suggestions/image_manager.h" | |
| 20 #include "components/suggestions/proto/suggestions.pb.h" | |
| 21 #include "components/suggestions/suggestions_store.h" | |
| 22 #include "components/sync/driver/fake_sync_service.h" | |
| 23 #include "components/sync/driver/sync_service.h" | |
| 24 #include "net/base/escape.h" | |
| 25 #include "net/http/http_response_headers.h" | |
| 26 #include "net/http/http_status_code.h" | |
| 27 #include "net/url_request/test_url_fetcher_factory.h" | |
| 28 #include "net/url_request/url_request_status.h" | |
| 29 #include "net/url_request/url_request_test_util.h" | |
| 30 #include "testing/gmock/include/gmock/gmock.h" | |
| 31 #include "testing/gtest/include/gtest/gtest.h" | |
| 32 #include "ui/gfx/image/image.h" | |
| 33 | |
| 34 using testing::DoAll; | |
| 35 using ::testing::AnyNumber; | |
| 36 using ::testing::Eq; | |
| 37 using ::testing::Return; | |
| 38 using testing::SetArgPointee; | |
| 39 using ::testing::NiceMock; | |
| 40 using ::testing::StrictMock; | |
| 41 using ::testing::_; | |
| 42 | |
| 43 namespace { | |
| 44 | |
| 45 // SuggestionsService::AccessTokenFetcher provides an empty account ID if its | |
| 46 // SigninManager is null. | |
| 47 const char kAccountId[] = ""; | |
| 48 const char kTestTitle[] = "a title"; | |
| 49 const char kTestUrl[] = "http://go.com"; | |
| 50 const char kTestFaviconUrl[] = | |
| 51 "https://s2.googleusercontent.com/s2/favicons?domain_url=" | |
| 52 "http://go.com&alt=s&sz=32"; | |
| 53 const char kBlacklistedUrl[] = "http://blacklist.com"; | |
| 54 const char kBlacklistedUrlAlt[] = "http://blacklist-atl.com"; | |
| 55 const int64_t kTestDefaultExpiry = 1402200000000000; | |
| 56 const int64_t kTestSetExpiry = 1404792000000000; | |
| 57 | |
| 58 std::unique_ptr<net::FakeURLFetcher> CreateURLFetcher( | |
| 59 const GURL& url, | |
| 60 net::URLFetcherDelegate* delegate, | |
| 61 const std::string& response_data, | |
| 62 net::HttpStatusCode response_code, | |
| 63 net::URLRequestStatus::Status status) { | |
| 64 std::unique_ptr<net::FakeURLFetcher> fetcher(new net::FakeURLFetcher( | |
| 65 url, delegate, response_data, response_code, status)); | |
| 66 | |
| 67 if (response_code == net::HTTP_OK) { | |
| 68 scoped_refptr<net::HttpResponseHeaders> download_headers( | |
| 69 new net::HttpResponseHeaders("")); | |
| 70 download_headers->AddHeader("Content-Type: text/html"); | |
| 71 fetcher->set_response_headers(download_headers); | |
| 72 } | |
| 73 return fetcher; | |
| 74 } | |
| 75 | |
| 76 // GMock matcher for protobuf equality. | |
| 77 MATCHER_P(EqualsProto, message, "") { | |
| 78 // This implementation assumes protobuf serialization is deterministic, which | |
| 79 // is true in practice but technically not something that code is supposed | |
| 80 // to rely on. However, it vastly simplifies the implementation. | |
| 81 std::string expected_serialized, actual_serialized; | |
| 82 message.SerializeToString(&expected_serialized); | |
| 83 arg.SerializeToString(&actual_serialized); | |
| 84 return expected_serialized == actual_serialized; | |
| 85 } | |
| 86 | |
| 87 } // namespace | |
| 88 | |
| 89 namespace suggestions { | |
| 90 | |
| 91 SuggestionsProfile CreateSuggestionsProfile() { | |
| 92 SuggestionsProfile profile; | |
| 93 profile.set_timestamp(123); | |
| 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; | |
| 99 } | |
| 100 | |
| 101 // Creates one suggestion with expiry timestamp and one without. | |
| 102 SuggestionsProfile CreateSuggestionsProfileWithExpiryTimestamps() { | |
| 103 SuggestionsProfile profile; | |
| 104 profile.set_timestamp(123); | |
| 105 ChromeSuggestion* suggestion = profile.add_suggestions(); | |
| 106 suggestion->set_title(kTestTitle); | |
| 107 suggestion->set_url(kTestUrl); | |
| 108 suggestion->set_expiry_ts(kTestSetExpiry); | |
| 109 | |
| 110 suggestion = profile.add_suggestions(); | |
| 111 suggestion->set_title(kTestTitle); | |
| 112 suggestion->set_url(kTestUrl); | |
| 113 | |
| 114 return profile; | |
| 115 } | |
| 116 | |
| 117 class MockSyncService : public syncer::FakeSyncService { | |
| 118 public: | |
| 119 MockSyncService() {} | |
| 120 virtual ~MockSyncService() {} | |
| 121 MOCK_CONST_METHOD0(CanSyncStart, bool()); | |
| 122 MOCK_CONST_METHOD0(IsSyncActive, bool()); | |
| 123 MOCK_CONST_METHOD0(ConfigurationDone, bool()); | |
| 124 MOCK_CONST_METHOD0(GetActiveDataTypes, syncer::ModelTypeSet()); | |
| 125 }; | |
| 126 | |
| 127 class TestSuggestionsStore : public suggestions::SuggestionsStore { | |
| 128 public: | |
| 129 TestSuggestionsStore() { | |
| 130 cached_suggestions = CreateSuggestionsProfile(); | |
| 131 } | |
| 132 bool LoadSuggestions(SuggestionsProfile* suggestions) override { | |
| 133 suggestions->CopyFrom(cached_suggestions); | |
| 134 return cached_suggestions.suggestions_size(); | |
| 135 } | |
| 136 bool StoreSuggestions(const SuggestionsProfile& suggestions) override { | |
| 137 cached_suggestions.CopyFrom(suggestions); | |
| 138 return true; | |
| 139 } | |
| 140 void ClearSuggestions() override { | |
| 141 cached_suggestions = SuggestionsProfile(); | |
| 142 } | |
| 143 | |
| 144 SuggestionsProfile cached_suggestions; | |
| 145 }; | |
| 146 | |
| 147 class MockImageManager : public suggestions::ImageManager { | |
| 148 public: | |
| 149 MockImageManager() {} | |
| 150 virtual ~MockImageManager() {} | |
| 151 MOCK_METHOD1(Initialize, void(const SuggestionsProfile&)); | |
| 152 MOCK_METHOD2(GetImageForURL, | |
| 153 void(const GURL&, | |
| 154 base::Callback<void(const GURL&, const gfx::Image&)>)); | |
| 155 MOCK_METHOD2(AddImageURL, void(const GURL&, const GURL&)); | |
| 156 }; | |
| 157 | |
| 158 class MockBlacklistStore : public suggestions::BlacklistStore { | |
| 159 public: | |
| 160 MOCK_METHOD1(BlacklistUrl, bool(const GURL&)); | |
| 161 MOCK_METHOD0(IsEmpty, bool()); | |
| 162 MOCK_METHOD1(GetTimeUntilReadyForUpload, bool(base::TimeDelta*)); | |
| 163 MOCK_METHOD2(GetTimeUntilURLReadyForUpload, | |
| 164 bool(const GURL&, base::TimeDelta*)); | |
| 165 MOCK_METHOD1(GetCandidateForUpload, bool(GURL*)); | |
| 166 MOCK_METHOD1(RemoveUrl, bool(const GURL&)); | |
| 167 MOCK_METHOD1(FilterSuggestions, void(SuggestionsProfile*)); | |
| 168 MOCK_METHOD0(ClearBlacklist, void()); | |
| 169 }; | |
| 170 | |
| 171 class SuggestionsServiceTest : public testing::Test { | |
| 172 public: | |
| 173 void CheckCallback(const SuggestionsProfile& suggestions_profile) { | |
| 174 ++suggestions_data_callback_count_; | |
| 175 if (suggestions_profile.suggestions_size() == 0) | |
| 176 ++suggestions_empty_data_count_; | |
| 177 } | |
| 178 | |
| 179 void CheckSuggestionsData() { | |
| 180 SuggestionsProfile suggestions_profile; | |
| 181 test_suggestions_store_->LoadSuggestions(&suggestions_profile); | |
| 182 EXPECT_EQ(1, suggestions_profile.suggestions_size()); | |
| 183 EXPECT_EQ(kTestTitle, suggestions_profile.suggestions(0).title()); | |
| 184 EXPECT_EQ(kTestUrl, suggestions_profile.suggestions(0).url()); | |
| 185 EXPECT_EQ(kTestFaviconUrl, | |
| 186 suggestions_profile.suggestions(0).favicon_url()); | |
| 187 } | |
| 188 | |
| 189 int suggestions_data_callback_count_; | |
| 190 int suggestions_empty_data_count_; | |
| 191 bool blacklisting_failed_; | |
| 192 bool undo_blacklisting_failed_; | |
| 193 | |
| 194 protected: | |
| 195 SuggestionsServiceTest() | |
| 196 : suggestions_data_callback_count_(0), | |
| 197 suggestions_empty_data_count_(0), | |
| 198 blacklisting_failed_(false), | |
| 199 undo_blacklisting_failed_(false), | |
| 200 factory_(nullptr, base::Bind(&CreateURLFetcher)), | |
| 201 mock_sync_service_(nullptr), | |
| 202 mock_thumbnail_manager_(nullptr), | |
| 203 mock_blacklist_store_(nullptr), | |
| 204 test_suggestions_store_(nullptr) { | |
| 205 token_service_.UpdateCredentials(kAccountId, "refresh_token"); | |
| 206 token_service_.set_auto_post_fetch_response_on_message_loop(true); | |
| 207 } | |
| 208 | |
| 209 ~SuggestionsServiceTest() override {} | |
| 210 | |
| 211 void SetUp() override { | |
| 212 request_context_ = | |
| 213 new net::TestURLRequestContextGetter(io_message_loop_.task_runner()); | |
| 214 } | |
| 215 | |
| 216 SuggestionsService* CreateSuggestionsServiceWithMocks() { | |
| 217 mock_sync_service_.reset(new MockSyncService); | |
| 218 ON_CALL(*mock_sync_service_, CanSyncStart()).WillByDefault(Return(true)); | |
| 219 ON_CALL(*mock_sync_service_, IsSyncActive()).WillByDefault(Return(true)); | |
| 220 ON_CALL(*mock_sync_service_, ConfigurationDone()) | |
| 221 .WillByDefault(Return(true)); | |
| 222 ON_CALL(*mock_sync_service_, GetActiveDataTypes()) | |
| 223 .WillByDefault( | |
| 224 Return(syncer::ModelTypeSet(syncer::HISTORY_DELETE_DIRECTIVES))); | |
| 225 | |
| 226 // These objects are owned by the returned SuggestionsService, but we keep | |
| 227 // the pointer around for testing. | |
| 228 test_suggestions_store_ = new TestSuggestionsStore(); | |
| 229 mock_thumbnail_manager_ = new StrictMock<MockImageManager>(); | |
| 230 mock_blacklist_store_ = new StrictMock<MockBlacklistStore>(); | |
| 231 return new SuggestionsService( | |
| 232 nullptr /* signin_manager */, &token_service_, mock_sync_service_.get(), | |
| 233 request_context_.get(), base::WrapUnique(test_suggestions_store_), | |
| 234 base::WrapUnique(mock_thumbnail_manager_), | |
| 235 base::WrapUnique(mock_blacklist_store_)); | |
| 236 } | |
| 237 | |
| 238 void Blacklist(SuggestionsService* suggestions_service, GURL url) { | |
| 239 blacklisting_failed_ = !suggestions_service->BlacklistURL(url); | |
| 240 } | |
| 241 | |
| 242 void UndoBlacklist(SuggestionsService* suggestions_service, GURL url) { | |
| 243 undo_blacklisting_failed_ = !suggestions_service->UndoBlacklistURL(url); | |
| 244 } | |
| 245 | |
| 246 // Helper for Undo failure tests. Depending on |is_uploaded|, tests either | |
| 247 // the case where the URL is no longer in the local blacklist or the case | |
| 248 // in which it's not yet candidate for upload. | |
| 249 void UndoBlacklistURLFailsHelper(bool is_uploaded) { | |
| 250 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 251 CreateSuggestionsServiceWithMocks()); | |
| 252 EXPECT_TRUE(suggestions_service != nullptr); | |
| 253 // Ensure scheduling the request doesn't happen before undo. | |
| 254 base::TimeDelta delay = base::TimeDelta::FromHours(1); | |
| 255 suggestions_service->set_blacklist_delay(delay); | |
| 256 | |
| 257 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 258 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 259 | |
| 260 SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); | |
| 261 GURL blacklisted_url(kBlacklistedUrl); | |
| 262 | |
| 263 // Blacklist expectations. | |
| 264 EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) | |
| 265 .WillOnce(Return(true)); | |
| 266 EXPECT_CALL(*mock_thumbnail_manager_, | |
| 267 Initialize(EqualsProto(suggestions_profile))); | |
| 268 EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)); | |
| 269 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 270 .WillOnce(DoAll(SetArgPointee<0>(delay), Return(true))); | |
| 271 // Undo expectations. | |
| 272 if (is_uploaded) { | |
| 273 // URL is not in local blacklist. | |
| 274 EXPECT_CALL(*mock_blacklist_store_, | |
| 275 GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _)) | |
| 276 .WillOnce(Return(false)); | |
| 277 } else { | |
| 278 // URL is not yet candidate for upload. | |
| 279 base::TimeDelta negative_delay = base::TimeDelta::FromHours(-1); | |
| 280 EXPECT_CALL(*mock_blacklist_store_, | |
| 281 GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _)) | |
| 282 .WillOnce(DoAll(SetArgPointee<1>(negative_delay), Return(true))); | |
| 283 } | |
| 284 | |
| 285 Blacklist(suggestions_service.get(), blacklisted_url); | |
| 286 UndoBlacklist(suggestions_service.get(), blacklisted_url); | |
| 287 | |
| 288 EXPECT_EQ(1, suggestions_data_callback_count_); | |
| 289 EXPECT_FALSE(blacklisting_failed_); | |
| 290 EXPECT_TRUE(undo_blacklisting_failed_); | |
| 291 } | |
| 292 | |
| 293 bool HasPendingSuggestionsRequest(SuggestionsService* suggestions_service) { | |
| 294 return !!suggestions_service->pending_request_.get(); | |
| 295 } | |
| 296 | |
| 297 protected: | |
| 298 base::MessageLoopForIO io_message_loop_; | |
| 299 net::FakeURLFetcherFactory factory_; | |
| 300 FakeProfileOAuth2TokenService token_service_; | |
| 301 std::unique_ptr<MockSyncService> mock_sync_service_; | |
| 302 // Only used if the SuggestionsService is built with mocks. Not owned. | |
| 303 MockImageManager* mock_thumbnail_manager_; | |
| 304 MockBlacklistStore* mock_blacklist_store_; | |
| 305 TestSuggestionsStore* test_suggestions_store_; | |
| 306 scoped_refptr<net::TestURLRequestContextGetter> request_context_; | |
| 307 | |
| 308 private: | |
| 309 DISALLOW_COPY_AND_ASSIGN(SuggestionsServiceTest); | |
| 310 }; | |
| 311 | |
| 312 TEST_F(SuggestionsServiceTest, FetchSuggestionsData) { | |
| 313 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 314 CreateSuggestionsServiceWithMocks()); | |
| 315 ASSERT_TRUE(suggestions_service != nullptr); | |
| 316 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 317 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 318 | |
| 319 SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); | |
| 320 | |
| 321 // Set up net::FakeURLFetcherFactory. | |
| 322 factory_.SetFakeResponse(SuggestionsService::BuildSuggestionsURL(), | |
| 323 suggestions_profile.SerializeAsString(), | |
| 324 net::HTTP_OK, net::URLRequestStatus::SUCCESS); | |
| 325 | |
| 326 // Expectations. | |
| 327 EXPECT_CALL(*mock_thumbnail_manager_, Initialize(_)); | |
| 328 EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)); | |
| 329 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 330 .WillOnce(Return(false)); | |
| 331 | |
| 332 // Send the request. The data should be returned to the callback. | |
| 333 suggestions_service->FetchSuggestionsData(); | |
| 334 | |
| 335 // Let the network request run. | |
| 336 base::RunLoop().RunUntilIdle(); | |
| 337 | |
| 338 // Ensure that CheckCallback() ran once. | |
| 339 EXPECT_EQ(1, suggestions_data_callback_count_); | |
| 340 | |
| 341 CheckSuggestionsData(); | |
| 342 } | |
| 343 | |
| 344 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) { | |
| 345 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 346 CreateSuggestionsServiceWithMocks()); | |
| 347 ASSERT_TRUE(suggestions_service != nullptr); | |
| 348 EXPECT_CALL(*mock_sync_service_, IsSyncActive()) | |
| 349 .WillRepeatedly(Return(false)); | |
| 350 | |
| 351 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 352 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 353 | |
| 354 // Try to fetch suggestions. Since sync is not active, no network request | |
| 355 // should be sent. | |
| 356 suggestions_service->FetchSuggestionsData(); | |
| 357 | |
| 358 // Let any network request run. | |
| 359 base::RunLoop().RunUntilIdle(); | |
| 360 | |
| 361 // Ensure that CheckCallback() didn't run. | |
| 362 EXPECT_EQ(0, suggestions_data_callback_count_); | |
| 363 | |
| 364 // |test_suggestions_store_| should still contain the default values. | |
| 365 SuggestionsProfile suggestions; | |
| 366 test_suggestions_store_->LoadSuggestions(&suggestions); | |
| 367 EXPECT_EQ(CreateSuggestionsProfile().SerializeAsString(), | |
| 368 suggestions.SerializeAsString()); | |
| 369 } | |
| 370 | |
| 371 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) { | |
| 372 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 373 CreateSuggestionsServiceWithMocks()); | |
| 374 ASSERT_TRUE(suggestions_service != nullptr); | |
| 375 EXPECT_CALL(*mock_sync_service_, CanSyncStart()) | |
| 376 .WillRepeatedly(Return(false)); | |
| 377 | |
| 378 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 379 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 380 | |
| 381 // Tell SuggestionsService that the sync state changed. The cache should be | |
| 382 // cleared and empty data returned to the callback. | |
| 383 suggestions_service->OnStateChanged(); | |
| 384 | |
| 385 // Ensure that CheckCallback ran once with empty data. | |
| 386 EXPECT_EQ(1, suggestions_data_callback_count_); | |
| 387 EXPECT_EQ(1, suggestions_empty_data_count_); | |
| 388 | |
| 389 // Try to fetch suggestions. Since sync is not active, no network request | |
| 390 // should be sent. | |
| 391 suggestions_service->FetchSuggestionsData(); | |
| 392 | |
| 393 // Let any network request run. | |
| 394 base::RunLoop().RunUntilIdle(); | |
| 395 | |
| 396 // Ensure that CheckCallback didn't run again. | |
| 397 EXPECT_EQ(1, suggestions_data_callback_count_); | |
| 398 } | |
| 399 | |
| 400 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataNoAccessToken) { | |
| 401 token_service_.RevokeCredentials(kAccountId); | |
| 402 | |
| 403 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 404 CreateSuggestionsServiceWithMocks()); | |
| 405 ASSERT_TRUE(suggestions_service != nullptr); | |
| 406 | |
| 407 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 408 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 409 | |
| 410 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 411 .WillOnce(Return(false)); | |
| 412 | |
| 413 suggestions_service->FetchSuggestionsData(); | |
| 414 | |
| 415 // No network request should be sent. | |
| 416 base::RunLoop().RunUntilIdle(); | |
| 417 EXPECT_FALSE(HasPendingSuggestionsRequest(suggestions_service.get())); | |
| 418 EXPECT_EQ(0, suggestions_data_callback_count_); | |
| 419 } | |
| 420 | |
| 421 TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingError) { | |
| 422 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 423 CreateSuggestionsServiceWithMocks()); | |
| 424 ASSERT_TRUE(suggestions_service != nullptr); | |
| 425 | |
| 426 // Fake a request error. | |
| 427 factory_.SetFakeResponse(SuggestionsService::BuildSuggestionsURL(), | |
| 428 "irrelevant", net::HTTP_OK, | |
| 429 net::URLRequestStatus::FAILED); | |
| 430 | |
| 431 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 432 .WillOnce(Return(false)); | |
| 433 | |
| 434 // Send the request. Empty data will be returned to the callback. | |
| 435 suggestions_service->IssueRequestIfNoneOngoing( | |
| 436 SuggestionsService::BuildSuggestionsURL()); | |
| 437 | |
| 438 // (Testing only) wait until suggestion fetch is complete. | |
| 439 base::RunLoop().RunUntilIdle(); | |
| 440 } | |
| 441 | |
| 442 TEST_F(SuggestionsServiceTest, IssueRequestIfNoneOngoingResponseNotOK) { | |
| 443 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 444 CreateSuggestionsServiceWithMocks()); | |
| 445 ASSERT_TRUE(suggestions_service != nullptr); | |
| 446 | |
| 447 // Fake a non-200 response code. | |
| 448 factory_.SetFakeResponse(SuggestionsService::BuildSuggestionsURL(), | |
| 449 "irrelevant", net::HTTP_BAD_REQUEST, | |
| 450 net::URLRequestStatus::SUCCESS); | |
| 451 | |
| 452 // Expect that an upload to the blacklist is scheduled. | |
| 453 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 454 .WillOnce(Return(false)); | |
| 455 | |
| 456 // Send the request. Empty data will be returned to the callback. | |
| 457 suggestions_service->IssueRequestIfNoneOngoing( | |
| 458 SuggestionsService::BuildSuggestionsURL()); | |
| 459 | |
| 460 // (Testing only) wait until suggestion fetch is complete. | |
| 461 base::RunLoop().RunUntilIdle(); | |
| 462 | |
| 463 // Expect no suggestions in the cache. | |
| 464 SuggestionsProfile empty_suggestions; | |
| 465 EXPECT_FALSE(test_suggestions_store_->LoadSuggestions(&empty_suggestions)); | |
| 466 } | |
| 467 | |
| 468 TEST_F(SuggestionsServiceTest, BlacklistURL) { | |
| 469 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 470 CreateSuggestionsServiceWithMocks()); | |
| 471 EXPECT_TRUE(suggestions_service != nullptr); | |
| 472 base::TimeDelta no_delay = base::TimeDelta::FromSeconds(0); | |
| 473 suggestions_service->set_blacklist_delay(no_delay); | |
| 474 | |
| 475 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 476 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 477 | |
| 478 GURL blacklisted_url(kBlacklistedUrl); | |
| 479 GURL request_url( | |
| 480 SuggestionsService::BuildSuggestionsBlacklistURL(blacklisted_url)); | |
| 481 SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); | |
| 482 factory_.SetFakeResponse(request_url, | |
| 483 suggestions_profile.SerializeAsString(), | |
| 484 net::HTTP_OK, net::URLRequestStatus::SUCCESS); | |
| 485 EXPECT_CALL(*mock_thumbnail_manager_, Initialize(_)).Times(2); | |
| 486 | |
| 487 // Expected calls to the blacklist store. | |
| 488 EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) | |
| 489 .WillOnce(Return(true)); | |
| 490 EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)).Times(2); | |
| 491 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 492 .WillOnce(DoAll(SetArgPointee<0>(no_delay), Return(true))) | |
| 493 .WillOnce(Return(false)); | |
| 494 EXPECT_CALL(*mock_blacklist_store_, GetCandidateForUpload(_)) | |
| 495 .WillOnce(DoAll(SetArgPointee<0>(blacklisted_url), Return(true))); | |
| 496 EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklisted_url))) | |
| 497 .WillOnce(Return(true)); | |
| 498 | |
| 499 Blacklist(suggestions_service.get(), blacklisted_url); | |
| 500 EXPECT_EQ(1, suggestions_data_callback_count_); | |
| 501 | |
| 502 // Wait on the upload task, the blacklist request and the next blacklist | |
| 503 // scheduling task. This only works when the scheduling task is not for future | |
| 504 // execution (note how both the SuggestionsService's scheduling delay and the | |
| 505 // BlacklistStore's candidacy delay are zero). | |
| 506 base::RunLoop().RunUntilIdle(); | |
| 507 | |
| 508 EXPECT_EQ(2, suggestions_data_callback_count_); | |
| 509 EXPECT_FALSE(blacklisting_failed_); | |
| 510 CheckSuggestionsData(); | |
| 511 } | |
| 512 | |
| 513 TEST_F(SuggestionsServiceTest, BlacklistURLFails) { | |
| 514 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 515 CreateSuggestionsServiceWithMocks()); | |
| 516 ASSERT_TRUE(suggestions_service != nullptr); | |
| 517 | |
| 518 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 519 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 520 | |
| 521 GURL blacklisted_url(kBlacklistedUrl); | |
| 522 EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) | |
| 523 .WillOnce(Return(false)); | |
| 524 | |
| 525 Blacklist(suggestions_service.get(), blacklisted_url); | |
| 526 | |
| 527 EXPECT_TRUE(blacklisting_failed_); | |
| 528 EXPECT_EQ(0, suggestions_data_callback_count_); | |
| 529 } | |
| 530 | |
| 531 // Initial blacklist request fails, triggering a second which succeeds. | |
| 532 TEST_F(SuggestionsServiceTest, BlacklistURLRequestFails) { | |
| 533 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 534 CreateSuggestionsServiceWithMocks()); | |
| 535 ASSERT_TRUE(suggestions_service != nullptr); | |
| 536 base::TimeDelta no_delay = base::TimeDelta::FromSeconds(0); | |
| 537 suggestions_service->set_blacklist_delay(no_delay); | |
| 538 | |
| 539 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 540 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 541 | |
| 542 GURL blacklisted_url(kBlacklistedUrl); | |
| 543 GURL request_url( | |
| 544 SuggestionsService::BuildSuggestionsBlacklistURL(blacklisted_url)); | |
| 545 GURL blacklisted_url_alt(kBlacklistedUrlAlt); | |
| 546 GURL request_url_alt( | |
| 547 SuggestionsService::BuildSuggestionsBlacklistURL(blacklisted_url_alt)); | |
| 548 SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); | |
| 549 | |
| 550 // Note: we want to set the response for the blacklist URL to first | |
| 551 // succeed, then fail. This doesn't seem possible. For simplicity of testing, | |
| 552 // we'll pretend the URL changed in the BlacklistStore between the first and | |
| 553 // the second request, and adjust expectations accordingly. | |
| 554 factory_.SetFakeResponse(request_url, "irrelevant", net::HTTP_OK, | |
| 555 net::URLRequestStatus::FAILED); | |
| 556 factory_.SetFakeResponse(request_url_alt, | |
| 557 suggestions_profile.SerializeAsString(), | |
| 558 net::HTTP_OK, net::URLRequestStatus::SUCCESS); | |
| 559 | |
| 560 // Expectations. | |
| 561 EXPECT_CALL(*mock_thumbnail_manager_, Initialize(_)).Times(2); | |
| 562 EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) | |
| 563 .WillOnce(Return(true)); | |
| 564 EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)).Times(2); | |
| 565 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 566 .WillOnce(DoAll(SetArgPointee<0>(no_delay), Return(true))) | |
| 567 .WillOnce(DoAll(SetArgPointee<0>(no_delay), Return(true))) | |
| 568 .WillOnce(Return(false)); | |
| 569 EXPECT_CALL(*mock_blacklist_store_, GetCandidateForUpload(_)) | |
| 570 .WillOnce(DoAll(SetArgPointee<0>(blacklisted_url), Return(true))) | |
| 571 .WillOnce(DoAll(SetArgPointee<0>(blacklisted_url_alt), Return(true))); | |
| 572 EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklisted_url_alt))) | |
| 573 .WillOnce(Return(true)); | |
| 574 | |
| 575 // Blacklist call, first request attempt. | |
| 576 Blacklist(suggestions_service.get(), blacklisted_url); | |
| 577 EXPECT_EQ(1, suggestions_data_callback_count_); | |
| 578 EXPECT_FALSE(blacklisting_failed_); | |
| 579 | |
| 580 // Wait for the first scheduling, the first request, the second scheduling, | |
| 581 // second request and the third scheduling. Again, note that calling | |
| 582 // RunUntilIdle on the MessageLoop only works when the task is not posted for | |
| 583 // the future. | |
| 584 base::RunLoop().RunUntilIdle(); | |
| 585 CheckSuggestionsData(); | |
| 586 } | |
| 587 | |
| 588 TEST_F(SuggestionsServiceTest, UndoBlacklistURL) { | |
| 589 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 590 CreateSuggestionsServiceWithMocks()); | |
| 591 ASSERT_TRUE(suggestions_service != nullptr); | |
| 592 // Ensure scheduling the request doesn't happen before undo. | |
| 593 base::TimeDelta delay = base::TimeDelta::FromHours(1); | |
| 594 suggestions_service->set_blacklist_delay(delay); | |
| 595 | |
| 596 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 597 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 598 | |
| 599 SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); | |
| 600 GURL blacklisted_url(kBlacklistedUrl); | |
| 601 | |
| 602 // Blacklist expectations. | |
| 603 EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) | |
| 604 .WillOnce(Return(true)); | |
| 605 EXPECT_CALL(*mock_thumbnail_manager_, | |
| 606 Initialize(EqualsProto(suggestions_profile))) | |
| 607 .Times(AnyNumber()); | |
| 608 EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)) | |
| 609 .Times(AnyNumber()); | |
| 610 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 611 .WillOnce(DoAll(SetArgPointee<0>(delay), Return(true))); | |
| 612 // Undo expectations. | |
| 613 EXPECT_CALL(*mock_blacklist_store_, | |
| 614 GetTimeUntilURLReadyForUpload(Eq(blacklisted_url), _)) | |
| 615 .WillOnce(DoAll(SetArgPointee<1>(delay), Return(true))); | |
| 616 EXPECT_CALL(*mock_blacklist_store_, RemoveUrl(Eq(blacklisted_url))) | |
| 617 .WillOnce(Return(true)); | |
| 618 | |
| 619 Blacklist(suggestions_service.get(), blacklisted_url); | |
| 620 UndoBlacklist(suggestions_service.get(), blacklisted_url); | |
| 621 | |
| 622 EXPECT_EQ(2, suggestions_data_callback_count_); | |
| 623 EXPECT_FALSE(blacklisting_failed_); | |
| 624 EXPECT_FALSE(undo_blacklisting_failed_); | |
| 625 } | |
| 626 | |
| 627 TEST_F(SuggestionsServiceTest, ClearBlacklist) { | |
| 628 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 629 CreateSuggestionsServiceWithMocks()); | |
| 630 ASSERT_TRUE(suggestions_service != nullptr); | |
| 631 // Ensure scheduling the request doesn't happen before undo. | |
| 632 base::TimeDelta delay = base::TimeDelta::FromHours(1); | |
| 633 suggestions_service->set_blacklist_delay(delay); | |
| 634 | |
| 635 auto subscription = suggestions_service->AddCallback(base::Bind( | |
| 636 &SuggestionsServiceTest::CheckCallback, base::Unretained(this))); | |
| 637 | |
| 638 SuggestionsProfile suggestions_profile = CreateSuggestionsProfile(); | |
| 639 GURL blacklisted_url(kBlacklistedUrl); | |
| 640 | |
| 641 factory_.SetFakeResponse( | |
| 642 SuggestionsService::BuildSuggestionsBlacklistClearURL(), | |
| 643 suggestions_profile.SerializeAsString(), net::HTTP_OK, | |
| 644 net::URLRequestStatus::SUCCESS); | |
| 645 | |
| 646 // Blacklist expectations. | |
| 647 EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklisted_url))) | |
| 648 .WillOnce(Return(true)); | |
| 649 EXPECT_CALL(*mock_thumbnail_manager_, | |
| 650 Initialize(EqualsProto(suggestions_profile))) | |
| 651 .Times(AnyNumber()); | |
| 652 EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)).Times(AnyNumber()); | |
| 653 EXPECT_CALL(*mock_blacklist_store_, GetTimeUntilReadyForUpload(_)) | |
| 654 .WillOnce(DoAll(SetArgPointee<0>(delay), Return(true))); | |
| 655 EXPECT_CALL(*mock_blacklist_store_, ClearBlacklist()); | |
| 656 | |
| 657 Blacklist(suggestions_service.get(), blacklisted_url); | |
| 658 suggestions_service->ClearBlacklist(); | |
| 659 | |
| 660 EXPECT_EQ(2, suggestions_data_callback_count_); | |
| 661 EXPECT_FALSE(blacklisting_failed_); | |
| 662 } | |
| 663 | |
| 664 TEST_F(SuggestionsServiceTest, UndoBlacklistURLFailsIfNotInBlacklist) { | |
| 665 UndoBlacklistURLFailsHelper(true); | |
| 666 } | |
| 667 | |
| 668 TEST_F(SuggestionsServiceTest, UndoBlacklistURLFailsIfAlreadyCandidate) { | |
| 669 UndoBlacklistURLFailsHelper(false); | |
| 670 } | |
| 671 | |
| 672 TEST_F(SuggestionsServiceTest, GetBlacklistedUrl) { | |
| 673 std::unique_ptr<GURL> request_url; | |
| 674 std::unique_ptr<net::FakeURLFetcher> fetcher; | |
| 675 GURL retrieved_url; | |
| 676 | |
| 677 // Not a blacklist request. | |
| 678 request_url.reset(new GURL("http://not-blacklisting.com/a?b=c")); | |
| 679 fetcher = CreateURLFetcher(*request_url, nullptr, "", net::HTTP_OK, | |
| 680 net::URLRequestStatus::SUCCESS); | |
| 681 EXPECT_FALSE(SuggestionsService::GetBlacklistedUrl(*fetcher, &retrieved_url)); | |
| 682 | |
| 683 // An actual blacklist request. | |
| 684 std::string blacklisted_url = "http://blacklisted.com/a?b=c&d=e"; | |
| 685 std::string encoded_blacklisted_url = | |
| 686 "http%3A%2F%2Fblacklisted.com%2Fa%3Fb%3Dc%26d%3De"; | |
| 687 std::string blacklist_request_prefix( | |
| 688 SuggestionsService::BuildSuggestionsBlacklistURLPrefix()); | |
| 689 request_url.reset( | |
| 690 new GURL(blacklist_request_prefix + encoded_blacklisted_url)); | |
| 691 fetcher.reset(); | |
| 692 fetcher = CreateURLFetcher(*request_url, nullptr, "", net::HTTP_OK, | |
| 693 net::URLRequestStatus::SUCCESS); | |
| 694 EXPECT_TRUE(SuggestionsService::GetBlacklistedUrl(*fetcher, &retrieved_url)); | |
| 695 EXPECT_EQ(blacklisted_url, retrieved_url.spec()); | |
| 696 } | |
| 697 | |
| 698 TEST_F(SuggestionsServiceTest, UpdateBlacklistDelay) { | |
| 699 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 700 CreateSuggestionsServiceWithMocks()); | |
| 701 base::TimeDelta initial_delay = suggestions_service->blacklist_delay(); | |
| 702 | |
| 703 // Delay unchanged on success. | |
| 704 suggestions_service->UpdateBlacklistDelay(true); | |
| 705 EXPECT_EQ(initial_delay, suggestions_service->blacklist_delay()); | |
| 706 | |
| 707 // Delay increases on failure. | |
| 708 suggestions_service->UpdateBlacklistDelay(false); | |
| 709 EXPECT_GT(suggestions_service->blacklist_delay(), initial_delay); | |
| 710 | |
| 711 // Delay resets on success. | |
| 712 suggestions_service->UpdateBlacklistDelay(true); | |
| 713 EXPECT_EQ(initial_delay, suggestions_service->blacklist_delay()); | |
| 714 } | |
| 715 | |
| 716 TEST_F(SuggestionsServiceTest, CheckDefaultTimeStamps) { | |
| 717 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 718 CreateSuggestionsServiceWithMocks()); | |
| 719 SuggestionsProfile suggestions = | |
| 720 CreateSuggestionsProfileWithExpiryTimestamps(); | |
| 721 suggestions_service->SetDefaultExpiryTimestamp(&suggestions, | |
| 722 kTestDefaultExpiry); | |
| 723 EXPECT_EQ(kTestSetExpiry, suggestions.suggestions(0).expiry_ts()); | |
| 724 EXPECT_EQ(kTestDefaultExpiry, suggestions.suggestions(1).expiry_ts()); | |
| 725 } | |
| 726 | |
| 727 TEST_F(SuggestionsServiceTest, GetPageThumbnail) { | |
| 728 std::unique_ptr<SuggestionsService> suggestions_service( | |
| 729 CreateSuggestionsServiceWithMocks()); | |
| 730 ASSERT_TRUE(suggestions_service != nullptr); | |
| 731 | |
| 732 GURL test_url(kTestUrl); | |
| 733 GURL thumbnail_url("https://www.thumbnails.com/thumb.jpg"); | |
| 734 base::Callback<void(const GURL&, const gfx::Image&)> dummy_callback; | |
| 735 | |
| 736 EXPECT_CALL(*mock_thumbnail_manager_, GetImageForURL(test_url, _)); | |
| 737 suggestions_service->GetPageThumbnail(test_url, dummy_callback); | |
| 738 | |
| 739 EXPECT_CALL(*mock_thumbnail_manager_, AddImageURL(test_url, thumbnail_url)); | |
| 740 EXPECT_CALL(*mock_thumbnail_manager_, GetImageForURL(test_url, _)); | |
| 741 suggestions_service->GetPageThumbnailWithURL(test_url, thumbnail_url, | |
| 742 dummy_callback); | |
| 743 | |
| 744 } | |
| 745 | |
| 746 } // namespace suggestions | |
| OLD | NEW |