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 |