Index: chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc |
diff --git a/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5e5f13ac35aaafc2d644754832812c114ac3bbcc |
--- /dev/null |
+++ b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc |
@@ -0,0 +1,768 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/ntp_snippets/download_suggestions_provider.h" |
+ |
+#include "base/bind.h" |
+#include "base/observer_list.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "chrome/browser/ntp_snippets/fake_download_item.h" |
+#include "components/ntp_snippets/category.h" |
+#include "components/ntp_snippets/category_factory.h" |
+#include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" |
+#include "components/ntp_snippets/offline_pages/offline_pages_test_utils.h" |
+#include "components/offline_pages/client_namespace_constants.h" |
+#include "components/prefs/testing_pref_service.h" |
+#include "content/public/test/mock_download_item.h" |
+#include "content/public/test/mock_download_manager.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using content::DownloadItem; |
+using content::MockDownloadManager; |
+using ntp_snippets::Category; |
+using ntp_snippets::CategoryFactory; |
+using ntp_snippets::ContentSuggestion; |
+using ntp_snippets::ContentSuggestionsProvider; |
+using ntp_snippets::MockContentSuggestionsProviderObserver; |
+using ntp_snippets::OfflinePageProxy; |
+using ntp_snippets::test::CaptureDismissedSuggestions; |
+using ntp_snippets::test::FakeOfflinePageModel; |
+using ntp_snippets::CategoryStatus; |
+using offline_pages::ClientId; |
+using offline_pages::OfflinePageItem; |
+using test::FakeDownloadItem; |
+using testing::_; |
+using testing::AnyNumber; |
+using testing::ElementsAre; |
+using testing::IsEmpty; |
+using testing::Mock; |
+using testing::Return; |
+using testing::SizeIs; |
+using testing::StrictMock; |
+using testing::UnorderedElementsAre; |
+using testing::Lt; |
+ |
+namespace ntp_snippets { |
+// These functions are implicitly used to print out values during the tests. |
+std::ostream& operator<<(std::ostream& os, const ContentSuggestion& value) { |
+ os << "{ url: " << value.url() << ", publish_date: " << value.publish_date() |
+ << "}"; |
+ return os; |
+} |
+ |
+std::ostream& operator<<(std::ostream& os, const CategoryStatus& value) { |
+ os << "CategoryStatus::"; |
+ switch (value) { |
+ case CategoryStatus::INITIALIZING: |
+ os << "INITIALIZING"; |
+ break; |
+ case CategoryStatus::AVAILABLE: |
+ os << "AVAILABLE"; |
+ break; |
+ case CategoryStatus::AVAILABLE_LOADING: |
+ os << "AVAILABLE_LOADING"; |
+ break; |
+ case CategoryStatus::NOT_PROVIDED: |
+ os << "NOT_PROVIDED"; |
+ break; |
+ case CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED: |
+ os << "ALL_SUGGESTIONS_EXPLICITLY_DISABLED"; |
+ break; |
+ case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED: |
+ os << "CATEGORY_EXPLICITLY_DISABLED"; |
+ break; |
+ case CategoryStatus::SIGNED_OUT: |
+ os << "SIGNED_OUT"; |
+ break; |
+ case CategoryStatus::LOADING_ERROR: |
+ os << "LOADING_ERROR"; |
+ break; |
+ } |
+ return os; |
+} |
+ |
+} // namespace ntp_snippets |
+ |
+namespace { |
+ |
+// TODO(vitaliii): Move this and outputting functions above to common file and |
+// replace remaining |Property(&ContentSuggestion::url, GURL("some_url"))|. |
+// See crbug.com/655513. |
+MATCHER_P(HasUrl, url, "") { |
+ *result_listener << "expected URL: " << url |
+ << "has URL: " << arg.url().spec(); |
+ return arg.url().spec() == url; |
+} |
+ |
+OfflinePageItem CreateDummyOfflinePage(int id) { |
+ return ntp_snippets::test::CreateDummyOfflinePageItem( |
+ id, offline_pages::kAsyncNamespace); |
+} |
+ |
+std::vector<OfflinePageItem> CreateDummyOfflinePages( |
+ const std::vector<int>& ids) { |
+ std::vector<OfflinePageItem> result; |
+ for (int id : ids) |
+ result.push_back(CreateDummyOfflinePage(id)); |
+ return result; |
+} |
+ |
+OfflinePageItem CreateDummyOfflinePage(int id, base::Time time) { |
+ OfflinePageItem item = CreateDummyOfflinePage(id); |
+ item.creation_time = time; |
+ return item; |
+} |
+ |
+std::unique_ptr<FakeDownloadItem> CreateDummyAssetDownload(int id) { |
+ std::unique_ptr<FakeDownloadItem> item = base::MakeUnique<FakeDownloadItem>(); |
+ item->SetId(id); |
+ std::string id_string = base::IntToString(id); |
+ item->SetTargetFilePath( |
+ base::FilePath::FromUTF8Unsafe("folder/file" + id_string + ".mhtml")); |
+ item->SetURL(GURL("http://dummy_file.com/" + id_string)); |
+ item->SetEndTime(base::Time::Now()); |
+ item->SetFileExternallyRemoved(false); |
+ item->SetState(DownloadItem::DownloadState::COMPLETE); |
+ return item; |
+} |
+ |
+std::unique_ptr<FakeDownloadItem> CreateDummyAssetDownload( |
+ int id, |
+ const base::Time end_time) { |
+ std::unique_ptr<FakeDownloadItem> item = CreateDummyAssetDownload(id); |
+ item->SetEndTime(end_time); |
+ return item; |
+} |
+ |
+std::vector<std::unique_ptr<FakeDownloadItem>> CreateDummyAssetDownloads( |
+ const std::vector<int>& ids) { |
+ std::vector<std::unique_ptr<FakeDownloadItem>> result; |
+ // The time is set to enforce the provider to cache the first items in the |
+ // list first. |
+ base::Time current_time = base::Time::Now(); |
+ for (int id : ids) { |
+ result.push_back(CreateDummyAssetDownload(id, current_time)); |
+ current_time -= base::TimeDelta::FromDays(1); |
+ } |
+ return result; |
+} |
+ |
+class ObservedMockDownloadManager : public MockDownloadManager { |
+ public: |
+ ObservedMockDownloadManager() {} |
+ ~ObservedMockDownloadManager() override { |
+ for (auto& observer : observers_) |
+ observer.ManagerGoingDown(this); |
+ } |
+ |
+ // Observer accessors. |
+ void AddObserver(Observer* observer) override { |
+ observers_.AddObserver(observer); |
+ } |
+ |
+ void RemoveObserver(Observer* observer) override { |
+ observers_.RemoveObserver(observer); |
+ } |
+ |
+ void NotifyDownloadCreated(DownloadItem* item) { |
+ for (auto& observer : observers_) |
+ observer.OnDownloadCreated(this, item); |
+ } |
+ |
+ std::vector<std::unique_ptr<FakeDownloadItem>>* mutable_items() { |
+ return &items_; |
+ } |
+ |
+ const std::vector<std::unique_ptr<FakeDownloadItem>>& items() const { |
+ return items_; |
+ } |
+ |
+ void GetAllDownloads(std::vector<DownloadItem*>* all_downloads) override { |
+ all_downloads->clear(); |
+ for (const auto& item : items_) |
+ all_downloads->push_back(item.get()); |
+ } |
+ |
+ private: |
+ base::ObserverList<Observer> observers_; |
+ std::vector<std::unique_ptr<FakeDownloadItem>> items_; |
+}; |
+ |
+} // namespace |
+ |
+class DownloadSuggestionsProviderTest : public testing::Test { |
+ public: |
+ DownloadSuggestionsProviderTest() |
+ : pref_service_(new TestingPrefServiceSimple()) { |
+ DownloadSuggestionsProvider::RegisterProfilePrefs( |
+ pref_service()->registry()); |
+ } |
+ |
+ void IgnoreOnCategoryStatusChangedToAvailable() { |
+ EXPECT_CALL(observer_, OnCategoryStatusChanged(_, downloads_category(), |
+ CategoryStatus::AVAILABLE)) |
+ .Times(AnyNumber()); |
+ EXPECT_CALL(observer_, |
+ OnCategoryStatusChanged(_, downloads_category(), |
+ CategoryStatus::AVAILABLE_LOADING)) |
+ .Times(AnyNumber()); |
+ } |
+ |
+ void IgnoreOnSuggestionInvalidated() { |
+ EXPECT_CALL(observer_, OnSuggestionInvalidated(_, _)).Times(AnyNumber()); |
+ } |
+ |
+ DownloadSuggestionsProvider* CreateProvider() { |
+ DCHECK(!provider_); |
+ scoped_refptr<OfflinePageProxy> proxy( |
+ new OfflinePageProxy(&offline_pages_model_)); |
+ provider_ = base::MakeUnique<DownloadSuggestionsProvider>( |
+ &observer_, &category_factory_, proxy, &downloads_manager_, |
+ pref_service(), |
+ /*download_manager_ui_enabled=*/false); |
+ return provider_.get(); |
+ } |
+ |
+ void DestroyProvider() { provider_.reset(); } |
+ |
+ Category downloads_category() { |
+ return category_factory_.FromKnownCategory( |
+ ntp_snippets::KnownCategories::DOWNLOADS); |
+ } |
+ |
+ void FireOfflinePageModelChanged(const std::vector<OfflinePageItem>& items) { |
+ DCHECK(provider_); |
+ provider_->OfflinePageModelChanged(items); |
+ } |
+ |
+ void FireOfflinePageDeleted(const OfflinePageItem& item) { |
+ DCHECK(provider_); |
+ provider_->OfflinePageDeleted(item.offline_id, item.client_id); |
+ } |
+ |
+ void FireDownloadCreated(DownloadItem* item) { |
+ DCHECK(provider_); |
+ downloads_manager_.NotifyDownloadCreated(item); |
+ } |
+ |
+ void FireDownloadsCreated( |
+ const std::vector<std::unique_ptr<FakeDownloadItem>>& items) { |
+ for (const auto& item : items) |
+ FireDownloadCreated(item.get()); |
+ } |
+ |
+ ContentSuggestion::ID GetDummySuggestionId(int id, bool is_offline_page) { |
+ return ContentSuggestion::ID( |
+ downloads_category(), |
+ (is_offline_page ? "O" : "D") + base::IntToString(id)); |
+ } |
+ |
+ std::vector<ContentSuggestion> GetDismissedSuggestions() { |
+ std::vector<ContentSuggestion> dismissed_suggestions; |
+ // This works synchronously because both fake data sources were designed so. |
+ provider()->GetDismissedSuggestionsForDebugging( |
+ downloads_category(), |
+ base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions)); |
+ return dismissed_suggestions; |
+ } |
+ |
+ ContentSuggestionsProvider* provider() { |
+ DCHECK(provider_); |
+ return provider_.get(); |
+ } |
+ ObservedMockDownloadManager* downloads_manager() { |
+ return &downloads_manager_; |
+ } |
+ FakeOfflinePageModel* offline_pages_model() { return &offline_pages_model_; } |
+ MockContentSuggestionsProviderObserver* observer() { return &observer_; } |
+ TestingPrefServiceSimple* pref_service() { return pref_service_.get(); } |
+ |
+ private: |
+ ObservedMockDownloadManager downloads_manager_; |
+ FakeOfflinePageModel offline_pages_model_; |
+ StrictMock<MockContentSuggestionsProviderObserver> observer_; |
+ CategoryFactory category_factory_; |
+ std::unique_ptr<TestingPrefServiceSimple> pref_service_; |
+ // Last so that the dependencies are deleted after the provider. |
+ std::unique_ptr<DownloadSuggestionsProvider> provider_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DownloadSuggestionsProviderTest); |
+}; |
+ |
+TEST_F(DownloadSuggestionsProviderTest, |
+ ShouldConvertOfflinePagesToSuggestions) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), |
+ UnorderedElementsAre( |
+ HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2")))); |
+ CreateProvider(); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, |
+ ShouldConvertDownloadItemsToSuggestions) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ IgnoreOnSuggestionInvalidated(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(0))); |
+ CreateProvider(); |
+ |
+ std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads = |
+ CreateDummyAssetDownloads({1, 2}); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml")))); |
+ FireDownloadCreated(asset_downloads[0].get()); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ FireDownloadCreated(asset_downloads[1].get()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldMixInBothSources) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ IgnoreOnSuggestionInvalidated(); |
+ |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), |
+ UnorderedElementsAre( |
+ HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2")))); |
+ CreateProvider(); |
+ |
+ std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads = |
+ CreateDummyAssetDownloads({1, 2}); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml")))); |
+ FireDownloadCreated(asset_downloads[0].get()); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ FireDownloadCreated(asset_downloads[1].get()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldSortSuggestions) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ IgnoreOnSuggestionInvalidated(); |
+ |
+ base::Time now = base::Time::Now(); |
+ base::Time yesterday = now - base::TimeDelta::FromDays(1); |
+ base::Time tomorrow = now + base::TimeDelta::FromDays(1); |
+ base::Time next_week = now + base::TimeDelta::FromDays(7); |
+ |
+ (*offline_pages_model()->mutable_items()) |
+ .push_back(CreateDummyOfflinePage(1, yesterday)); |
+ (*offline_pages_model()->mutable_items()) |
+ .push_back(CreateDummyOfflinePage(2, tomorrow)); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), |
+ ElementsAre(HasUrl("http://dummy.com/2"), |
+ HasUrl("http://dummy.com/1")))); |
+ CreateProvider(); |
+ |
+ std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads; |
+ asset_downloads.push_back(CreateDummyAssetDownload(3, next_week)); |
+ asset_downloads.push_back(CreateDummyAssetDownload(4, now)); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), |
+ ElementsAre(HasUrl("file:///folder/file3.mhtml"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("http://dummy.com/1")))); |
+ FireDownloadCreated(asset_downloads[0].get()); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), |
+ ElementsAre(HasUrl("file:///folder/file3.mhtml"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file4.mhtml"), |
+ HasUrl("http://dummy.com/1")))); |
+ FireDownloadCreated(asset_downloads[1].get()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, |
+ ShouldDismissWithoutNotifyingObservers) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) |
+ .Times(2); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); |
+ EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, _)).Times(0); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/true)); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/false)); |
+ |
+ // |downloads_manager_| is destroyed after the |provider_|, so the provider |
+ // will not observe download items being destroyed. |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, |
+ ShouldNotReportDismissedSuggestionsOnNewData) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) |
+ .Times(2); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/true)); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/false)); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ FireOfflinePageModelChanged(offline_pages_model()->items()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldReturnDismissedSuggestions) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) |
+ .Times(2); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/true)); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/false)); |
+ |
+ EXPECT_THAT(GetDismissedSuggestions(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("file:///folder/file1.mhtml"))); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldClearDismissedSuggestions) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) |
+ .Times(2); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/true)); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/false)); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ provider()->ClearDismissedSuggestionsForDebugging(downloads_category()); |
+ EXPECT_THAT(GetDismissedSuggestions(), IsEmpty()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, |
+ ShouldNotDismissOtherTypeWithTheSameID) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(4ul)))) |
+ .Times(2); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/true)); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ FireOfflinePageModelChanged(offline_pages_model()->items()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldReplaceDismissedItemWithNewData) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(5ul)))) |
+ .Times(5); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml"), |
+ HasUrl("file:///folder/file3.mhtml"), |
+ HasUrl("file:///folder/file4.mhtml"), |
+ HasUrl("file:///folder/file5.mhtml")))); |
+ CreateProvider(); |
+ // Currently the provider stores five items in its internal cache, so six |
+ // items are needed to check whether all downloads are fetched on dismissal. |
+ *(downloads_manager()->mutable_items()) = |
+ CreateDummyAssetDownloads({1, 2, 3, 4, 5, 6}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/false)); |
+ |
+ // The provider is not notified about the 6th item, however, it must report |
+ // it now. |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("file:///folder/file2.mhtml"), |
+ HasUrl("file:///folder/file3.mhtml"), |
+ HasUrl("file:///folder/file4.mhtml"), |
+ HasUrl("file:///folder/file5.mhtml"), |
+ HasUrl("file:///folder/file6.mhtml")))); |
+ FireOfflinePageModelChanged(offline_pages_model()->items()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, |
+ ShouldInvalidateWhenUnderlyingItemDeleted) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(3ul)))); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml")))); |
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2}); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ // We add another item manually, so that when it gets deleted it is not |
+ // present in DownloadsManager list. |
+ std::unique_ptr<FakeDownloadItem> removed_item = CreateDummyAssetDownload(2); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml")))); |
+ FireDownloadCreated(removed_item.get()); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnSuggestionInvalidated( |
+ _, GetDummySuggestionId(1, /*is_offline_page=*/true))); |
+ FireOfflinePageDeleted(offline_pages_model()->items()[0]); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnSuggestionInvalidated( |
+ _, GetDummySuggestionId(2, /*is_offline_page=*/false))); |
+ // |OnDownloadItemDestroyed| is called from |removed_item|'s destructor. |
+ removed_item.reset(); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldReplaceRemovedItemWithNewData) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ IgnoreOnSuggestionInvalidated(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(5ul)))) |
+ .Times(5); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml"), |
+ HasUrl("file:///folder/file3.mhtml"), |
+ HasUrl("file:///folder/file4.mhtml"), |
+ HasUrl("file:///folder/file5.mhtml")))); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = |
+ CreateDummyAssetDownloads({1, 2, 3, 4, 5}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ // Note that |CreateDummyAssetDownloads| creates items "downloaded" before |
+ // |base::Time::Now()|, so for a new item the time is set in future to enforce |
+ // the provider to show the new item. |
+ std::unique_ptr<FakeDownloadItem> removed_item = CreateDummyAssetDownload( |
+ 100, base::Time::Now() + base::TimeDelta::FromDays(1)); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), |
+ UnorderedElementsAre( |
+ HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml"), |
+ HasUrl("file:///folder/file3.mhtml"), |
+ HasUrl("file:///folder/file4.mhtml"), |
+ HasUrl("file:///folder/file100.mhtml")))); |
+ FireDownloadCreated(removed_item.get()); |
+ |
+ // |OnDownloadDestroyed| notification is called in |DownloadItem|'s |
+ // destructor. |
+ removed_item.reset(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, downloads_category(), |
+ UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"), |
+ HasUrl("file:///folder/file2.mhtml"), |
+ HasUrl("file:///folder/file3.mhtml"), |
+ HasUrl("file:///folder/file4.mhtml"), |
+ HasUrl("file:///folder/file5.mhtml")))); |
+ FireOfflinePageModelChanged(offline_pages_model()->items()); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldPruneOfflinePagesDismissedIDs) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ IgnoreOnSuggestionInvalidated(); |
+ |
+ *(offline_pages_model()->mutable_items()) = |
+ CreateDummyOfflinePages({1, 2, 3}); |
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), |
+ UnorderedElementsAre( |
+ HasUrl("http://dummy.com/1"), |
+ HasUrl("http://dummy.com/2"), |
+ HasUrl("http://dummy.com/3")))); |
+ CreateProvider(); |
+ |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/true)); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(2, /*is_offline_page=*/true)); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(3, /*is_offline_page=*/true)); |
+ EXPECT_THAT(GetDismissedSuggestions(), SizeIs(3)); |
+ |
+ // Prune on getting all offline pages. Note that the first suggestion is not |
+ // removed from |offline_pages_model| storage, because otherwise |
+ // |GetDismissedSuggestions| cannot return it. |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), IsEmpty())); |
+ FireOfflinePageModelChanged(CreateDummyOfflinePages({2, 3})); |
+ EXPECT_THAT(GetDismissedSuggestions(), SizeIs(2)); |
+ |
+ // Prune when offline page is deleted. |
+ FireOfflinePageDeleted(offline_pages_model()->items()[1]); |
+ EXPECT_THAT(GetDismissedSuggestions(), SizeIs(1)); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldPruneAssetDownloadsDismissedIDs) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ IgnoreOnSuggestionInvalidated(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(Lt(3ul)))) |
+ .Times(3); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(1, /*is_offline_page=*/false)); |
+ provider()->DismissSuggestion( |
+ GetDummySuggestionId(2, /*is_offline_page=*/false)); |
+ EXPECT_THAT(GetDismissedSuggestions(), SizeIs(2)); |
+ |
+ downloads_manager()->items()[0]->NotifyDownloadDestroyed(); |
+ EXPECT_THAT(GetDismissedSuggestions(), SizeIs(1)); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, ShouldNotFetchAssetDownloadsOnStartup) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2}); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), IsEmpty())); |
+ CreateProvider(); |
+} |
+ |
+TEST_F(DownloadSuggestionsProviderTest, |
+ ShouldInvalidateAssetDownloadWhenItsFileRemoved) { |
+ IgnoreOnCategoryStatusChangedToAvailable(); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), IsEmpty())); |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, downloads_category(), SizeIs(1))); |
+ CreateProvider(); |
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1}); |
+ FireDownloadsCreated(downloads_manager()->items()); |
+ |
+ EXPECT_CALL(*observer(), |
+ OnSuggestionInvalidated( |
+ _, GetDummySuggestionId(1, /*is_offline_page=*/false))); |
+ (*downloads_manager()->mutable_items())[0]->SetFileExternallyRemoved(true); |
+ (*downloads_manager()->mutable_items())[0]->NotifyDownloadUpdated(); |
+} |