Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3213)

Unified Diff: chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc

Issue 2360263002: [NTPSnippets] Show all downloads on the NTP (3/3): Downloads provider. (Closed)
Patch Set: the freshest rebase. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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();
+}

Powered by Google App Engine
This is Rietveld 408576698