OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "chrome/browser/ntp_snippets/download_suggestions_provider.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/observer_list.h" | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "chrome/browser/ntp_snippets/fake_download_item.h" | |
11 #include "components/ntp_snippets/category.h" | |
12 #include "components/ntp_snippets/category_factory.h" | |
13 #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" | |
14 #include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h" | |
15 #include "components/offline_pages/client_namespace_constants.h" | |
16 #include "components/prefs/testing_pref_service.h" | |
17 #include "content/public/test/mock_download_item.h" | |
18 #include "content/public/test/mock_download_manager.h" | |
19 #include "testing/gtest/include/gtest/gtest.h" | |
20 | |
21 using content::DownloadItem; | |
22 using content::MockDownloadManager; | |
23 using ntp_snippets::Category; | |
24 using ntp_snippets::CategoryFactory; | |
25 using ntp_snippets::ContentSuggestion; | |
26 using ntp_snippets::ContentSuggestionsProvider; | |
27 using ntp_snippets::MockContentSuggestionsProviderObserver; | |
28 using ntp_snippets::OfflinePageProxy; | |
29 using ntp_snippets::test::CaptureDismissedSuggestions; | |
30 using ntp_snippets::test::FakeOfflinePageModel; | |
31 using ntp_snippets::CategoryStatus; | |
32 using offline_pages::ClientId; | |
33 using offline_pages::OfflinePageItem; | |
34 using test::FakeDownloadItem; | |
35 using testing::_; | |
36 using testing::AnyNumber; | |
37 using testing::ElementsAre; | |
38 using testing::IsEmpty; | |
39 using testing::Mock; | |
40 using testing::Return; | |
41 using testing::SizeIs; | |
42 using testing::StrictMock; | |
43 using testing::UnorderedElementsAre; | |
44 using testing::Lt; | |
45 | |
46 namespace ntp_snippets { | |
47 // These functions are implicitly used to print out values during the tests. | |
48 void PrintTo(const ContentSuggestion& value, std::ostream* os) { | |
49 *os << "{ url: " << value.url() << ", publish_date: " << value.publish_date() | |
50 << "}"; | |
51 } | |
52 | |
53 void PrintTo(const CategoryStatus& value, std::ostream* os) { | |
Marc Treib
2016/10/17 10:18:41
If multiple tests need this, it's also okay to def
vitaliii
2016/10/26 00:07:55
I will do this as a part of clean up later.
| |
54 *os << "CategoryStatus::"; | |
55 switch (value) { | |
56 case CategoryStatus::INITIALIZING: | |
57 *os << "INITIALIZING"; | |
58 break; | |
59 case CategoryStatus::AVAILABLE: | |
60 *os << "AVAILABLE"; | |
61 break; | |
62 case CategoryStatus::AVAILABLE_LOADING: | |
63 *os << "AVAILABLE_LOADING"; | |
64 break; | |
65 case CategoryStatus::NOT_PROVIDED: | |
66 *os << "NOT_PROVIDED"; | |
67 break; | |
68 case CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED: | |
69 *os << "ALL_SUGGESTIONS_EXPLICITLY_DISABLED"; | |
70 break; | |
71 case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED: | |
72 *os << "CATEGORY_EXPLICITLY_DISABLED"; | |
73 break; | |
74 case CategoryStatus::SIGNED_OUT: | |
75 *os << "SIGNED_OUT"; | |
76 break; | |
77 case CategoryStatus::LOADING_ERROR: | |
78 *os << "LOADING_ERROR"; | |
79 break; | |
80 } | |
81 } | |
82 | |
83 } // namespace ntp_snippets | |
84 | |
85 namespace { | |
86 | |
87 // TODO(vitaliii): Move this and |PrintTo| above to common file and replace | |
88 // remaining |Property(&ContentSuggestion::url, GURL("some_url"))|. | |
89 // See crbug.com/655513. | |
90 MATCHER_P(HasUrl, url, "") { | |
91 *result_listener << "expected URL: " << url | |
92 << "has URL: " << arg.url().spec(); | |
93 return arg.url().spec() == url; | |
94 } | |
95 | |
96 OfflinePageItem CreateDummyOfflinePage(int id) { | |
97 return ntp_snippets::test::CreateDummyOfflinePageItem( | |
98 id, offline_pages::kAsyncNamespace); | |
99 } | |
100 | |
101 std::vector<OfflinePageItem> CreateDummyOfflinePages( | |
102 const std::vector<int>& ids) { | |
103 std::vector<OfflinePageItem> result; | |
104 for (int id : ids) { | |
105 result.push_back(CreateDummyOfflinePage(id)); | |
106 } | |
107 return result; | |
108 } | |
109 | |
110 OfflinePageItem CreateDummyOfflinePage(int id, base::Time time) { | |
111 OfflinePageItem item = CreateDummyOfflinePage(id); | |
112 item.creation_time = time; | |
113 return item; | |
114 } | |
115 | |
116 std::unique_ptr<FakeDownloadItem> CreateDummyAssetDownload(int id) { | |
117 std::unique_ptr<FakeDownloadItem> item = base::MakeUnique<FakeDownloadItem>(); | |
118 item->SetId(id); | |
119 std::string id_string = base::IntToString(id); | |
120 item->SetTargetFilePath( | |
121 base::FilePath::FromUTF8Unsafe("folder/file" + id_string + ".mhtml")); | |
122 item->SetURL(GURL("http://dummy_file.com/" + id_string)); | |
123 item->SetEndTime(base::Time::Now()); | |
124 item->SetFileExternallyRemoved(false); | |
125 item->SetState(DownloadItem::DownloadState::COMPLETE); | |
126 return item; | |
127 } | |
128 | |
129 std::unique_ptr<FakeDownloadItem> CreateDummyAssetDownload( | |
130 int id, | |
131 const base::Time end_time) { | |
132 std::unique_ptr<FakeDownloadItem> item = CreateDummyAssetDownload(id); | |
133 item->SetEndTime(end_time); | |
134 return item; | |
135 } | |
136 | |
137 std::vector<std::unique_ptr<FakeDownloadItem>> CreateDummyAssetDownloads( | |
138 const std::vector<int>& ids) { | |
139 std::vector<std::unique_ptr<FakeDownloadItem>> result; | |
140 // The time is set to enforce the provider to cache the first items in the | |
141 // list first. | |
142 base::Time current_time = base::Time::Now(); | |
143 for (int id : ids) { | |
144 result.push_back(CreateDummyAssetDownload(id, current_time)); | |
145 current_time -= base::TimeDelta::FromDays(1); | |
146 } | |
147 return result; | |
148 } | |
149 | |
150 class ObservedMockDownloadManager : public MockDownloadManager { | |
151 public: | |
152 ObservedMockDownloadManager() {} | |
153 ~ObservedMockDownloadManager() override { | |
154 for (auto& observer : observers_) | |
155 observer.ManagerGoingDown(this); | |
156 } | |
157 | |
158 // Observer accessors. | |
159 void AddObserver(Observer* observer) override { | |
160 observers_.AddObserver(observer); | |
161 } | |
162 | |
163 void RemoveObserver(Observer* observer) override { | |
164 observers_.RemoveObserver(observer); | |
165 } | |
166 | |
167 void NotifyDownloadCreated(DownloadItem* item) { | |
168 for (auto& observer : observers_) | |
169 observer.OnDownloadCreated(this, item); | |
170 } | |
171 | |
172 std::vector<std::unique_ptr<FakeDownloadItem>>* mutable_items() { | |
173 return &items_; | |
174 } | |
175 | |
176 const std::vector<std::unique_ptr<FakeDownloadItem>>& items() const { | |
177 return items_; | |
178 } | |
179 | |
180 void GetAllDownloads(std::vector<DownloadItem*>* all_downloads) override { | |
181 all_downloads->clear(); | |
182 for (const auto& item : items_) | |
183 all_downloads->push_back(item.get()); | |
184 } | |
185 | |
186 private: | |
187 base::ObserverList<Observer> observers_; | |
188 std::vector<std::unique_ptr<FakeDownloadItem>> items_; | |
189 }; | |
190 | |
191 } // namespace | |
192 | |
193 class DownloadSuggestionsProviderTest : public testing::Test { | |
194 public: | |
195 DownloadSuggestionsProviderTest() | |
196 : pref_service_(new TestingPrefServiceSimple()) { | |
197 DownloadSuggestionsProvider::RegisterProfilePrefs( | |
198 pref_service()->registry()); | |
199 } | |
200 | |
201 void IgnoreOnCategoryStatusChanged() { | |
Marc Treib
2016/10/17 10:18:41
nit: This sounds like it ignores all changes, but
vitaliii
2016/10/26 00:07:55
Done.
| |
202 EXPECT_CALL(observer_, OnCategoryStatusChanged(_, downloads_category(), | |
203 CategoryStatus::AVAILABLE)) | |
204 .Times(AnyNumber()); | |
205 EXPECT_CALL(observer_, | |
206 OnCategoryStatusChanged(_, downloads_category(), | |
207 CategoryStatus::AVAILABLE_LOADING)) | |
208 .Times(AnyNumber()); | |
209 } | |
210 | |
211 void IgnoreOnSuggestionInvalidated() { | |
212 EXPECT_CALL(observer_, OnSuggestionInvalidated(_, _)).Times(AnyNumber()); | |
213 } | |
214 | |
215 DownloadSuggestionsProvider* CreateProvider() { | |
216 scoped_refptr<OfflinePageProxy> proxy( | |
Marc Treib
2016/10/17 10:18:41
DCHECK(!provider_) ?
vitaliii
2016/10/26 00:07:55
Done.
| |
217 new OfflinePageProxy(&offline_pages_model_)); | |
218 provider_ = base::MakeUnique<DownloadSuggestionsProvider>( | |
219 &observer_, &category_factory_, proxy, &downloads_manager_, | |
220 pref_service(), | |
221 /*download_manager_ui_enabled=*/false); | |
222 return provider_.get(); | |
223 } | |
224 | |
225 void DestroyProvider() { provider_.release(); } | |
Marc Treib
2016/10/17 10:18:41
This will leak memory - it gives up ownership and
vitaliii
2016/10/26 00:07:55
I misunderstood what |release()| does, I meant |re
| |
226 | |
227 Category downloads_category() { | |
228 return category_factory_.FromKnownCategory( | |
229 ntp_snippets::KnownCategories::DOWNLOADS); | |
230 } | |
231 | |
232 void FireOfflinePageModelChanged(const std::vector<OfflinePageItem>& items) { | |
233 DCHECK(provider_.get()); | |
Marc Treib
2016/10/17 10:18:41
nit: I think the ".get()" isn't required
vitaliii
2016/10/26 00:07:55
Done.
| |
234 provider_->OfflinePageModelChanged(items); | |
235 } | |
236 | |
237 void FireOfflinePageDeleted(const OfflinePageItem& item) { | |
238 DCHECK(provider_.get()); | |
239 provider_->OfflinePageDeleted(item.offline_id, item.client_id); | |
240 } | |
241 | |
242 void FireDownloadCreated(DownloadItem* item) { | |
243 DCHECK(provider_.get()); | |
244 downloads_manager_.NotifyDownloadCreated(item); | |
245 } | |
246 | |
247 void FireDownloadsCreated( | |
248 const std::vector<std::unique_ptr<FakeDownloadItem>>& items) { | |
249 for (const auto& item : items) | |
250 FireDownloadCreated(item.get()); | |
251 } | |
252 | |
253 ContentSuggestion::ID GetDummySuggestionId(int id, bool is_offline_page) { | |
254 return ContentSuggestion::ID( | |
255 downloads_category(), | |
256 (is_offline_page ? "O" : "D") + base::IntToString(id)); | |
257 } | |
258 | |
259 std::vector<ContentSuggestion> GetDismissedSuggestions() { | |
260 std::vector<ContentSuggestion> dismissed_suggestions; | |
261 provider()->GetDismissedSuggestionsForDebugging( | |
262 downloads_category(), | |
263 base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions)); | |
Marc Treib
2016/10/17 10:18:41
Does this always return synchronously? If so, that
vitaliii
2016/10/26 00:07:55
FakeOfflinePageModel (which we own) returns items
| |
264 return dismissed_suggestions; | |
265 } | |
266 | |
267 ContentSuggestionsProvider* provider() { | |
268 DCHECK(provider_.get()); | |
269 return provider_.get(); | |
270 } | |
271 ObservedMockDownloadManager* downloads_manager() { | |
272 return &downloads_manager_; | |
273 } | |
274 FakeOfflinePageModel* offline_pages_model() { return &offline_pages_model_; } | |
275 StrictMock<MockContentSuggestionsProviderObserver>* observer() { | |
Marc Treib
2016/10/17 10:18:41
I think just returning MockContentSuggestionsProvi
vitaliii
2016/10/26 00:07:55
Done.
| |
276 return &observer_; | |
277 } | |
278 TestingPrefServiceSimple* pref_service() { return pref_service_.get(); } | |
279 | |
280 private: | |
281 ObservedMockDownloadManager downloads_manager_; | |
282 FakeOfflinePageModel offline_pages_model_; | |
283 StrictMock<MockContentSuggestionsProviderObserver> observer_; | |
284 CategoryFactory category_factory_; | |
285 std::unique_ptr<TestingPrefServiceSimple> pref_service_; | |
286 // Last so that the dependencies are deleted after the provider. | |
287 std::unique_ptr<DownloadSuggestionsProvider> provider_; | |
288 | |
289 DISALLOW_COPY_AND_ASSIGN(DownloadSuggestionsProviderTest); | |
290 }; | |
291 | |
292 TEST_F(DownloadSuggestionsProviderTest, | |
293 ShouldConvertOfflinePagesToSuggestions) { | |
294 IgnoreOnCategoryStatusChanged(); | |
295 | |
296 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
297 EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), | |
298 UnorderedElementsAre( | |
299 HasUrl("http://dummy.com/1"), | |
300 HasUrl("http://dummy.com/2")))); | |
301 CreateProvider(); | |
302 } | |
303 | |
304 TEST_F(DownloadSuggestionsProviderTest, | |
305 ShouldConvertDownloadItemsToSuggestions) { | |
306 IgnoreOnCategoryStatusChanged(); | |
307 IgnoreOnSuggestionInvalidated(); | |
308 | |
309 EXPECT_CALL(*observer(), | |
310 OnNewSuggestions(_, downloads_category(), SizeIs(0))); | |
311 CreateProvider(); | |
312 | |
313 std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads = | |
314 CreateDummyAssetDownloads({1, 2}); | |
315 | |
316 EXPECT_CALL(*observer(), | |
317 OnNewSuggestions( | |
318 _, downloads_category(), | |
319 UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml")))); | |
320 FireDownloadCreated(asset_downloads[0].get()); | |
321 | |
322 EXPECT_CALL(*observer(), | |
323 OnNewSuggestions( | |
324 _, downloads_category(), | |
325 UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), | |
326 HasUrl("file:///folder/file2.mhtml")))); | |
327 FireDownloadCreated(asset_downloads[1].get()); | |
328 } | |
329 | |
330 TEST_F(DownloadSuggestionsProviderTest, ShouldMixInBothSources) { | |
331 IgnoreOnCategoryStatusChanged(); | |
332 IgnoreOnSuggestionInvalidated(); | |
333 | |
334 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
335 EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), | |
336 UnorderedElementsAre( | |
337 HasUrl("http://dummy.com/1"), | |
338 HasUrl("http://dummy.com/2")))); | |
339 CreateProvider(); | |
340 | |
341 std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads = | |
342 CreateDummyAssetDownloads({1, 2}); | |
343 | |
344 EXPECT_CALL(*observer(), | |
345 OnNewSuggestions( | |
346 _, downloads_category(), | |
347 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
348 HasUrl("http://dummy.com/2"), | |
349 HasUrl("file:///folder/file1.mhtml")))); | |
350 FireDownloadCreated(asset_downloads[0].get()); | |
351 | |
352 EXPECT_CALL(*observer(), | |
353 OnNewSuggestions( | |
354 _, downloads_category(), | |
355 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
356 HasUrl("http://dummy.com/2"), | |
357 HasUrl("file:///folder/file1.mhtml"), | |
358 HasUrl("file:///folder/file2.mhtml")))); | |
359 FireDownloadCreated(asset_downloads[1].get()); | |
360 } | |
361 | |
362 TEST_F(DownloadSuggestionsProviderTest, ShouldSortSuggestions) { | |
363 IgnoreOnCategoryStatusChanged(); | |
364 IgnoreOnSuggestionInvalidated(); | |
365 | |
366 base::Time now = base::Time::Now(); | |
367 base::Time yesterday = now - base::TimeDelta::FromDays(1); | |
368 base::Time tomorrow = now + base::TimeDelta::FromDays(1); | |
369 base::Time next_week = now + base::TimeDelta::FromDays(7); | |
370 | |
371 (*offline_pages_model()->mutable_items()) | |
372 .push_back(CreateDummyOfflinePage(1, yesterday)); | |
373 (*offline_pages_model()->mutable_items()) | |
374 .push_back(CreateDummyOfflinePage(2, tomorrow)); | |
375 | |
376 EXPECT_CALL(*observer(), | |
377 OnNewSuggestions(_, downloads_category(), | |
378 ElementsAre(HasUrl("http://dummy.com/2"), | |
379 HasUrl("http://dummy.com/1")))); | |
380 CreateProvider(); | |
381 | |
382 std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads; | |
383 asset_downloads.push_back(CreateDummyAssetDownload(3, next_week)); | |
384 asset_downloads.push_back(CreateDummyAssetDownload(4, now)); | |
385 | |
386 EXPECT_CALL(*observer(), | |
387 OnNewSuggestions(_, downloads_category(), | |
388 ElementsAre(HasUrl("file:///folder/file3.mhtml"), | |
389 HasUrl("http://dummy.com/2"), | |
390 HasUrl("http://dummy.com/1")))); | |
391 FireDownloadCreated(asset_downloads[0].get()); | |
392 | |
393 EXPECT_CALL(*observer(), | |
394 OnNewSuggestions(_, downloads_category(), | |
395 ElementsAre(HasUrl("file:///folder/file3.mhtml"), | |
396 HasUrl("http://dummy.com/2"), | |
397 HasUrl("file:///folder/file4.mhtml"), | |
398 HasUrl("http://dummy.com/1")))); | |
399 FireDownloadCreated(asset_downloads[1].get()); | |
400 } | |
401 | |
402 TEST_F(DownloadSuggestionsProviderTest, | |
403 ShouldDismissWithoutNotifyingObservers) { | |
404 IgnoreOnCategoryStatusChanged(); | |
405 | |
406 EXPECT_CALL(*observer(), | |
Marc Treib
2016/10/17 10:18:41
optional: If you expect calls in a particular orde
vitaliii
2016/10/26 00:07:55
It breaks my |IgnoreOn*| functions.
Marc Treib
2016/10/26 08:54:08
Interesting - why?
vitaliii
2016/10/27 15:49:19
I do not know precise reason, but I speculate that
| |
407 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) | |
408 .Times(2); | |
409 EXPECT_CALL(*observer(), | |
410 OnNewSuggestions( | |
411 _, downloads_category(), | |
412 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
413 HasUrl("http://dummy.com/2"), | |
414 HasUrl("file:///folder/file1.mhtml"), | |
415 HasUrl("file:///folder/file2.mhtml")))); | |
416 | |
417 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
418 CreateProvider(); | |
419 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); | |
420 FireDownloadsCreated(downloads_manager()->items()); | |
421 | |
422 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); | |
423 EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, _)).Times(0); | |
424 provider()->DismissSuggestion( | |
425 GetDummySuggestionId(1, /*is_offline_page=*/true)); | |
426 provider()->DismissSuggestion( | |
427 GetDummySuggestionId(1, /*is_offline_page=*/false)); | |
428 | |
429 IgnoreOnSuggestionInvalidated(); | |
Marc Treib
2016/10/17 10:18:41
Does this do anything here? If so, worth a comment
vitaliii
2016/10/26 00:07:55
No, but the reason is quite nontrivial. Basically,
| |
430 } | |
431 | |
432 TEST_F(DownloadSuggestionsProviderTest, | |
433 ShouldNotReportDismissedSuggestionsOnNewData) { | |
434 IgnoreOnCategoryStatusChanged(); | |
435 IgnoreOnSuggestionInvalidated(); | |
436 | |
437 EXPECT_CALL(*observer(), | |
438 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) | |
439 .Times(2); | |
440 EXPECT_CALL(*observer(), | |
441 OnNewSuggestions( | |
442 _, downloads_category(), | |
443 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
444 HasUrl("http://dummy.com/2"), | |
445 HasUrl("file:///folder/file1.mhtml"), | |
446 HasUrl("file:///folder/file2.mhtml")))); | |
447 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
448 CreateProvider(); | |
449 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); | |
450 FireDownloadsCreated(downloads_manager()->items()); | |
451 | |
452 provider()->DismissSuggestion( | |
453 GetDummySuggestionId(1, /*is_offline_page=*/true)); | |
454 provider()->DismissSuggestion( | |
455 GetDummySuggestionId(1, /*is_offline_page=*/false)); | |
456 | |
457 EXPECT_CALL(*observer(), | |
458 OnNewSuggestions( | |
459 _, downloads_category(), | |
460 UnorderedElementsAre(HasUrl("http://dummy.com/2"), | |
461 HasUrl("file:///folder/file2.mhtml")))); | |
462 FireOfflinePageModelChanged(offline_pages_model()->items()); | |
463 } | |
464 | |
465 TEST_F(DownloadSuggestionsProviderTest, ShouldReturnDismissedSuggestions) { | |
466 IgnoreOnCategoryStatusChanged(); | |
467 IgnoreOnSuggestionInvalidated(); | |
468 | |
469 EXPECT_CALL(*observer(), | |
470 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) | |
471 .Times(2); | |
472 EXPECT_CALL(*observer(), | |
473 OnNewSuggestions( | |
474 _, downloads_category(), | |
475 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
476 HasUrl("http://dummy.com/2"), | |
477 HasUrl("file:///folder/file1.mhtml"), | |
478 HasUrl("file:///folder/file2.mhtml")))); | |
479 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
480 CreateProvider(); | |
481 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); | |
482 FireDownloadsCreated(downloads_manager()->items()); | |
483 | |
484 provider()->DismissSuggestion( | |
485 GetDummySuggestionId(1, /*is_offline_page=*/true)); | |
486 provider()->DismissSuggestion( | |
487 GetDummySuggestionId(1, /*is_offline_page=*/false)); | |
488 | |
489 EXPECT_THAT(GetDismissedSuggestions(), | |
490 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
491 HasUrl("file:///folder/file1.mhtml"))); | |
492 } | |
493 | |
494 TEST_F(DownloadSuggestionsProviderTest, ShouldClearDismissedSuggestions) { | |
495 IgnoreOnCategoryStatusChanged(); | |
496 IgnoreOnSuggestionInvalidated(); | |
497 | |
498 EXPECT_CALL(*observer(), | |
499 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) | |
500 .Times(2); | |
501 EXPECT_CALL(*observer(), | |
502 OnNewSuggestions( | |
503 _, downloads_category(), | |
504 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
505 HasUrl("http://dummy.com/2"), | |
506 HasUrl("file:///folder/file1.mhtml"), | |
507 HasUrl("file:///folder/file2.mhtml")))); | |
508 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
509 CreateProvider(); | |
510 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); | |
511 FireDownloadsCreated(downloads_manager()->items()); | |
512 | |
513 provider()->DismissSuggestion( | |
514 GetDummySuggestionId(1, /*is_offline_page=*/true)); | |
515 provider()->DismissSuggestion( | |
516 GetDummySuggestionId(1, /*is_offline_page=*/false)); | |
517 | |
518 EXPECT_CALL(*observer(), | |
519 OnNewSuggestions( | |
520 _, downloads_category(), | |
521 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
522 HasUrl("http://dummy.com/2"), | |
523 HasUrl("file:///folder/file1.mhtml"), | |
524 HasUrl("file:///folder/file2.mhtml")))); | |
525 provider()->ClearDismissedSuggestionsForDebugging(downloads_category()); | |
526 EXPECT_THAT(GetDismissedSuggestions(), IsEmpty()); | |
527 } | |
528 | |
529 TEST_F(DownloadSuggestionsProviderTest, | |
530 ShouldNotDismissOtherTypeWithTheSameID) { | |
531 IgnoreOnCategoryStatusChanged(); | |
532 IgnoreOnSuggestionInvalidated(); | |
533 | |
534 EXPECT_CALL(*observer(), | |
535 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) | |
536 .Times(2); | |
537 EXPECT_CALL(*observer(), | |
538 OnNewSuggestions( | |
539 _, downloads_category(), | |
540 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
541 HasUrl("http://dummy.com/2"), | |
542 HasUrl("file:///folder/file1.mhtml"), | |
543 HasUrl("file:///folder/file2.mhtml")))); | |
544 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
545 CreateProvider(); | |
546 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); | |
547 FireDownloadsCreated(downloads_manager()->items()); | |
548 | |
549 provider()->DismissSuggestion( | |
550 GetDummySuggestionId(1, /*is_offline_page=*/true)); | |
551 | |
552 EXPECT_CALL(*observer(), | |
553 OnNewSuggestions( | |
554 _, downloads_category(), | |
555 UnorderedElementsAre(HasUrl("http://dummy.com/2"), | |
556 HasUrl("file:///folder/file1.mhtml"), | |
557 HasUrl("file:///folder/file2.mhtml")))); | |
558 FireOfflinePageModelChanged(offline_pages_model()->items()); | |
559 } | |
560 | |
561 TEST_F(DownloadSuggestionsProviderTest, ShouldReplaceDismissedItemWithNewData) { | |
562 IgnoreOnCategoryStatusChanged(); | |
563 IgnoreOnSuggestionInvalidated(); | |
564 | |
565 EXPECT_CALL(*observer(), | |
566 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(5ul)))) | |
567 .Times(5); | |
568 EXPECT_CALL(*observer(), | |
569 OnNewSuggestions( | |
570 _, downloads_category(), | |
571 UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), | |
572 HasUrl("file:///folder/file2.mhtml"), | |
573 HasUrl("file:///folder/file3.mhtml"), | |
574 HasUrl("file:///folder/file4.mhtml"), | |
575 HasUrl("file:///folder/file5.mhtml")))); | |
576 CreateProvider(); | |
577 // Currently the provider stores five items in its internal cache, so six | |
578 // items are needed to check whether all downloads are fetched on dismissal. | |
579 *(downloads_manager()->mutable_items()) = | |
580 CreateDummyAssetDownloads({1, 2, 3, 4, 5, 6}); | |
581 FireDownloadsCreated(downloads_manager()->items()); | |
582 | |
583 provider()->DismissSuggestion( | |
584 GetDummySuggestionId(1, /*is_offline_page=*/false)); | |
585 | |
586 // The provider is not notified about the 6th item, however, it must report | |
587 // it now. | |
588 EXPECT_CALL(*observer(), | |
589 OnNewSuggestions( | |
590 _, downloads_category(), | |
591 UnorderedElementsAre(HasUrl("file:///folder/file2.mhtml"), | |
592 HasUrl("file:///folder/file3.mhtml"), | |
593 HasUrl("file:///folder/file4.mhtml"), | |
594 HasUrl("file:///folder/file5.mhtml"), | |
595 HasUrl("file:///folder/file6.mhtml")))); | |
596 FireOfflinePageModelChanged(offline_pages_model()->items()); | |
597 } | |
598 | |
599 TEST_F(DownloadSuggestionsProviderTest, | |
600 ShouldInvalidateWhenUnderlyingItemDeleted) { | |
601 IgnoreOnCategoryStatusChanged(); | |
602 | |
603 EXPECT_CALL(*observer(), | |
604 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(3ul)))); | |
605 EXPECT_CALL(*observer(), | |
606 OnNewSuggestions( | |
607 _, downloads_category(), | |
608 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
609 HasUrl("http://dummy.com/2"), | |
610 HasUrl("file:///folder/file1.mhtml")))); | |
611 *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); | |
612 CreateProvider(); | |
613 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1}); | |
614 FireDownloadsCreated(downloads_manager()->items()); | |
615 | |
616 // We add another item manually, so that when it gets deleted it is not | |
617 // present in DownloadsManager list. | |
618 std::unique_ptr<FakeDownloadItem> removed_item = CreateDummyAssetDownload(2); | |
619 EXPECT_CALL(*observer(), | |
620 OnNewSuggestions( | |
621 _, downloads_category(), | |
622 UnorderedElementsAre(HasUrl("http://dummy.com/1"), | |
623 HasUrl("http://dummy.com/2"), | |
624 HasUrl("file:///folder/file1.mhtml"), | |
625 HasUrl("file:///folder/file2.mhtml")))); | |
626 FireDownloadCreated(removed_item.get()); | |
627 | |
628 EXPECT_CALL(*observer(), | |
629 OnSuggestionInvalidated( | |
630 _, GetDummySuggestionId(1, /*is_offline_page=*/true))); | |
631 FireOfflinePageDeleted(offline_pages_model()->items()[0]); | |
632 | |
633 EXPECT_CALL(*observer(), | |
634 OnSuggestionInvalidated( | |
635 _, GetDummySuggestionId(2, /*is_offline_page=*/false))); | |
636 // |OnDownloadItemDestroyed| is called from its destructor. | |
Marc Treib
2016/10/17 10:18:41
nit: s/its/|removed_item|'s/
vitaliii
2016/10/26 00:07:55
Done.
| |
637 removed_item.reset(); | |
638 | |
639 IgnoreOnSuggestionInvalidated(); | |
Marc Treib
2016/10/17 10:18:41
Is this necessary?
vitaliii
2016/10/26 00:07:55
The answer is the same as for the case above.
No,
| |
640 } | |
641 | |
642 TEST_F(DownloadSuggestionsProviderTest, ShouldReplaceRemovedItemWithNewData) { | |
643 IgnoreOnCategoryStatusChanged(); | |
644 IgnoreOnSuggestionInvalidated(); | |
645 | |
646 EXPECT_CALL(*observer(), | |
647 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(5ul)))) | |
648 .Times(5); | |
649 EXPECT_CALL(*observer(), | |
650 OnNewSuggestions( | |
651 _, downloads_category(), | |
652 UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), | |
653 HasUrl("file:///folder/file2.mhtml"), | |
654 HasUrl("file:///folder/file3.mhtml"), | |
655 HasUrl("file:///folder/file4.mhtml"), | |
656 HasUrl("file:///folder/file5.mhtml")))); | |
657 CreateProvider(); | |
658 *(downloads_manager()->mutable_items()) = | |
659 CreateDummyAssetDownloads({1, 2, 3, 4, 5}); | |
660 FireDownloadsCreated(downloads_manager()->items()); | |
661 | |
662 // Note that |CreateDummyAssetDownloads| creates items "downloaded" before | |
663 // |base::Time::Now()|, so for a new item the time is set in future to enforce | |
664 // the provider to show the new item. | |
665 std::unique_ptr<FakeDownloadItem> removed_item = CreateDummyAssetDownload( | |
666 100, base::Time::Now() + base::TimeDelta::FromDays(1)); | |
667 EXPECT_CALL(*observer(), | |
668 OnNewSuggestions(_, downloads_category(), | |
669 UnorderedElementsAre( | |
670 HasUrl("file:///folder/file1.mhtml"), | |
671 HasUrl("file:///folder/file2.mhtml"), | |
672 HasUrl("file:///folder/file3.mhtml"), | |
673 HasUrl("file:///folder/file4.mhtml"), | |
674 HasUrl("file:///folder/file100.mhtml")))); | |
675 FireDownloadCreated(removed_item.get()); | |
676 | |
677 // |OnDownloadDestroyed| notification is called in |DownloadItem|'s | |
678 // destructor. | |
679 removed_item.reset(); | |
680 | |
681 EXPECT_CALL(*observer(), | |
682 OnNewSuggestions( | |
683 _, downloads_category(), | |
684 UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), | |
685 HasUrl("file:///folder/file2.mhtml"), | |
686 HasUrl("file:///folder/file3.mhtml"), | |
687 HasUrl("file:///folder/file4.mhtml"), | |
688 HasUrl("file:///folder/file5.mhtml")))); | |
689 FireOfflinePageModelChanged(offline_pages_model()->items()); | |
Marc Treib
2016/10/17 10:18:41
Is the FireOfflinePageModelChanged needed here? Sh
vitaliii
2016/10/26 00:07:55
Yes, it is.
Marc Treib
2016/10/26 08:54:08
Ah yes, you're right. Destroying the download shou
vitaliii
2016/10/27 15:49:19
Acknowledged.
| |
690 } | |
691 | |
692 TEST_F(DownloadSuggestionsProviderTest, ShouldPruneOfflinePagesDismissedIDs) { | |
693 IgnoreOnCategoryStatusChanged(); | |
694 IgnoreOnSuggestionInvalidated(); | |
695 | |
696 *(offline_pages_model()->mutable_items()) = | |
697 CreateDummyOfflinePages({1, 2, 3}); | |
698 EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), | |
699 UnorderedElementsAre( | |
700 HasUrl("http://dummy.com/1"), | |
701 HasUrl("http://dummy.com/2"), | |
702 HasUrl("http://dummy.com/3")))); | |
703 CreateProvider(); | |
704 | |
705 provider()->DismissSuggestion( | |
706 GetDummySuggestionId(1, /*is_offline_page=*/true)); | |
707 provider()->DismissSuggestion( | |
708 GetDummySuggestionId(2, /*is_offline_page=*/true)); | |
709 provider()->DismissSuggestion( | |
710 GetDummySuggestionId(3, /*is_offline_page=*/true)); | |
711 EXPECT_THAT(GetDismissedSuggestions(), SizeIs(3)); | |
712 | |
713 // Prune on getting all offline pages. Note that the first suggestion is not | |
714 // removed from |offline_pages_model| storage, because otherwise | |
715 // |GetDismissedSuggestions| cannot return it. | |
716 EXPECT_CALL(*observer(), | |
717 OnNewSuggestions(_, downloads_category(), IsEmpty())); | |
718 FireOfflinePageModelChanged(CreateDummyOfflinePages({2, 3})); | |
719 EXPECT_THAT(GetDismissedSuggestions(), SizeIs(2)); | |
720 | |
721 // Prune when offline page is deleted. | |
722 FireOfflinePageDeleted(offline_pages_model()->items()[1]); | |
723 EXPECT_THAT(GetDismissedSuggestions(), SizeIs(1)); | |
724 } | |
725 | |
726 TEST_F(DownloadSuggestionsProviderTest, ShouldPruneAssetDownloadsDismissedIDs) { | |
727 IgnoreOnCategoryStatusChanged(); | |
728 IgnoreOnSuggestionInvalidated(); | |
729 | |
730 EXPECT_CALL(*observer(), | |
731 OnNewSuggestions(_, downloads_category(), SizeIs(Lt(3ul)))) | |
732 .Times(3); | |
733 CreateProvider(); | |
734 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); | |
735 FireDownloadsCreated(downloads_manager()->items()); | |
736 | |
737 provider()->DismissSuggestion( | |
738 GetDummySuggestionId(1, /*is_offline_page=*/false)); | |
739 provider()->DismissSuggestion( | |
740 GetDummySuggestionId(2, /*is_offline_page=*/false)); | |
741 EXPECT_THAT(GetDismissedSuggestions(), SizeIs(2)); | |
742 | |
743 downloads_manager()->items()[0]->NotifyDownloadDestroyed(); | |
744 EXPECT_THAT(GetDismissedSuggestions(), SizeIs(1)); | |
745 } | |
746 | |
747 TEST_F(DownloadSuggestionsProviderTest, ShouldNotFetchAssetDownloadsOnStartup) { | |
748 IgnoreOnCategoryStatusChanged(); | |
749 | |
750 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); | |
751 EXPECT_CALL(*observer(), | |
752 OnNewSuggestions(_, downloads_category(), IsEmpty())); | |
753 CreateProvider(); | |
754 } | |
755 | |
756 TEST_F(DownloadSuggestionsProviderTest, | |
757 ShouldInvalidateAssetDownloadWhenItsFileRemoved) { | |
758 IgnoreOnCategoryStatusChanged(); | |
759 | |
760 EXPECT_CALL(*observer(), | |
761 OnNewSuggestions(_, downloads_category(), IsEmpty())); | |
762 EXPECT_CALL(*observer(), | |
763 OnNewSuggestions(_, downloads_category(), SizeIs(1))); | |
764 CreateProvider(); | |
765 *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1}); | |
766 FireDownloadsCreated(downloads_manager()->items()); | |
767 | |
768 EXPECT_CALL(*observer(), | |
769 OnSuggestionInvalidated( | |
770 _, GetDummySuggestionId(1, /*is_offline_page=*/false))); | |
771 (*downloads_manager()->mutable_items())[0]->SetFileExternallyRemoved(true); | |
772 (*downloads_manager()->mutable_items())[0]->NotifyDownloadUpdated(); | |
773 } | |
OLD | NEW |