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

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: Marc's comments + tests + some corrections. Created 4 years, 2 months 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..6ec47c5d1c1697c97f789c304cf1c202e366bd58
--- /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 "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::MockDownloadItem;
+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 offline_pages::ClientId;
+using offline_pages::OfflinePageItem;
+using testing::_;
+using testing::AnyNumber;
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::Mock;
+using testing::Return;
+using testing::SizeIs;
+using testing::UnorderedElementsAre;
+
+namespace ntp_snippets {
+
+void PrintTo(const ContentSuggestion& value, std::ostream* os) {
Marc Treib 2016/10/13 12:11:26 Is this used anywhere?
vitaliii 2016/10/15 18:36:31 Yes, the testing framework uses this when printing
Marc Treib 2016/10/17 10:18:40 I think defining an "operator<<" also does the tri
vitaliii 2016/10/26 00:07:54 Done.
+ *os << "{ url: " << value.url() << ", publish_date: " << value.publish_date()
+ << "}";
+}
+
+} // namespace ntp_snippets
+
+namespace {
+
+// TODO(vitaliii): Move this and |PrintTo| 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;
+}
+
+class FakeDownloadItem : public DownloadItem {
Marc Treib 2016/10/13 12:11:26 Since this is so huge, maybe worth putting it into
vitaliii 2016/10/15 18:36:31 Done.
+ public:
+ FakeDownloadItem() {}
+ ~FakeDownloadItem() override {
+ NotifyOnDownloadRemoved();
+ FOR_EACH_OBSERVER(Observer, observers_, OnDownloadDestroyed(this));
+ }
+
+ void AddObserver(Observer* observer) override {
+ observers_.AddObserver(observer);
+ }
+
+ void RemoveObserver(Observer* observer) override {
+ observers_.RemoveObserver(observer);
+ }
+
+ void NotifyOnDownloadRemoved() {
+ FOR_EACH_OBSERVER(Observer, observers_, OnDownloadRemoved(this));
+ }
+
+ void NotifyOnDownloadUpdated() { UpdateObservers(); }
+
+ void UpdateObservers() override {
+ FOR_EACH_OBSERVER(Observer, observers_, OnDownloadUpdated(this));
+ }
+
+ void SetId(uint32_t id) { id_ = id; }
+ uint32_t GetId() const override { return id_; }
+
+ void SetURL(GURL url) { url_ = url; }
+ const GURL& GetURL() const override { return url_; }
+
+ void SetTargetFilePath(const base::FilePath& file_path) {
+ file_path_ = file_path;
+ }
+ const base::FilePath& GetTargetFilePath() const override {
+ return file_path_;
+ }
+
+ void SetFileExternallyRemoved(bool is_file_externally_removed) {
+ is_file_externally_removed_ = is_file_externally_removed;
+ }
+ bool GetFileExternallyRemoved() const override {
+ return is_file_externally_removed_;
+ }
+
+ void SetEndTime(const base::Time& end_time) { end_time_ = end_time; }
+ base::Time GetEndTime() const override { return end_time_; }
+
+ void SetState(const DownloadState& state) { download_state_ = state; }
+ DownloadState GetState() const override { return download_state_; }
+
+ // The methods below are not supported and are not expected to be called.
+ void ValidateDangerousDownload() override { NOTREACHED(); }
+ void StealDangerousDownload(const AcquireFileCallback& callback) override {
+ NOTREACHED();
+ callback.Run(base::FilePath());
+ }
+ void Pause() override { NOTREACHED(); }
+ void Resume() override { NOTREACHED(); }
+ void Cancel(bool user_cancel) override { NOTREACHED(); }
+ void Remove() override { NOTREACHED(); }
+ void OpenDownload() override { NOTREACHED(); }
+ void ShowDownloadInShell() override { NOTREACHED(); }
+ const std::string& GetGuid() const override {
+ NOTREACHED();
+ return dummy_string;
+ }
+ content::DownloadInterruptReason GetLastReason() const override {
+ NOTREACHED();
+ return content::DownloadInterruptReason();
+ }
+ bool IsPaused() const override {
+ NOTREACHED();
+ return false;
+ }
+ bool IsTemporary() const override {
+ NOTREACHED();
+ return false;
+ }
+ bool CanResume() const override {
+ NOTREACHED();
+ return false;
+ }
+ bool IsDone() const override {
+ NOTREACHED();
+ return true;
+ }
+ const std::vector<GURL>& GetUrlChain() const override {
+ NOTREACHED();
+ return dummy_url_vector;
+ }
+ const GURL& GetOriginalUrl() const override {
+ NOTREACHED();
+ return dummy_url;
+ }
+ const GURL& GetReferrerUrl() const override {
+ NOTREACHED();
+ return dummy_url;
+ }
+ const GURL& GetSiteUrl() const override {
+ NOTREACHED();
+ return dummy_url;
+ }
+ const GURL& GetTabUrl() const override {
+ NOTREACHED();
+ return dummy_url;
+ }
+ const GURL& GetTabReferrerUrl() const override {
+ NOTREACHED();
+ return dummy_url;
+ }
+ std::string GetSuggestedFilename() const override {
+ NOTREACHED();
+ return std::string();
+ }
+ std::string GetContentDisposition() const override {
+ NOTREACHED();
+ return std::string();
+ }
+ std::string GetMimeType() const override {
+ NOTREACHED();
+ return std::string();
+ }
+ std::string GetOriginalMimeType() const override {
+ NOTREACHED();
+ return std::string();
+ }
+ std::string GetRemoteAddress() const override {
+ NOTREACHED();
+ return std::string();
+ }
+ bool HasUserGesture() const override {
+ NOTREACHED();
+ return false;
+ }
+ ui::PageTransition GetTransitionType() const override {
+ NOTREACHED();
+ return ui::PageTransition();
+ }
+ const std::string& GetLastModifiedTime() const override {
+ NOTREACHED();
+ return dummy_string;
+ }
+ const std::string& GetETag() const override {
+ NOTREACHED();
+ return dummy_string;
+ }
+ bool IsSavePackageDownload() const override {
+ NOTREACHED();
+ return false;
+ }
+ const base::FilePath& GetFullPath() const override {
+ NOTREACHED();
+ return dummy_file_path;
+ }
+ const base::FilePath& GetForcedFilePath() const override {
+ NOTREACHED();
+ return dummy_file_path;
+ }
+ base::FilePath GetFileNameToReportUser() const override {
+ NOTREACHED();
+ return base::FilePath();
+ }
+ TargetDisposition GetTargetDisposition() const override {
+ NOTREACHED();
+ return TargetDisposition();
+ }
+ const std::string& GetHash() const override {
+ NOTREACHED();
+ return dummy_string;
+ }
+ void DeleteFile(const base::Callback<void(bool)>& callback) override {
+ NOTREACHED();
+ callback.Run(false);
+ }
+ bool IsDangerous() const override {
+ NOTREACHED();
+ return false;
+ }
+ content::DownloadDangerType GetDangerType() const override {
+ NOTREACHED();
+ return content::DownloadDangerType();
+ }
+ bool TimeRemaining(base::TimeDelta* remaining) const override {
+ NOTREACHED();
+ return false;
+ }
+ int64_t CurrentSpeed() const override {
+ NOTREACHED();
+ return 1;
+ }
+ int PercentComplete() const override {
+ NOTREACHED();
+ return 1;
+ }
+ bool AllDataSaved() const override {
+ NOTREACHED();
+ return true;
+ }
+ int64_t GetTotalBytes() const override {
+ NOTREACHED();
+ return 1;
+ }
+ int64_t GetReceivedBytes() const override {
+ NOTREACHED();
+ return 1;
+ }
+ base::Time GetStartTime() const override {
+ NOTREACHED();
+ return base::Time();
+ }
+ bool CanShowInFolder() override {
+ NOTREACHED();
+ return true;
+ }
+ bool CanOpenDownload() override {
+ NOTREACHED();
+ return true;
+ }
+ bool ShouldOpenFileBasedOnExtension() override {
+ NOTREACHED();
+ return true;
+ }
+ bool GetOpenWhenComplete() const override {
+ NOTREACHED();
+ return false;
+ }
+ bool GetAutoOpened() override {
+ NOTREACHED();
+ return false;
+ }
+ bool GetOpened() const override {
+ NOTREACHED();
+ return false;
+ }
+ content::BrowserContext* GetBrowserContext() const override {
+ NOTREACHED();
+ return nullptr;
+ }
+ content::WebContents* GetWebContents() const override {
+ NOTREACHED();
+ return nullptr;
+ }
+ void OnContentCheckCompleted(
+ content::DownloadDangerType danger_type) override {
+ NOTREACHED();
+ }
+ void SetOpenWhenComplete(bool open) override { NOTREACHED(); }
+ void SetOpened(bool opened) override { NOTREACHED(); }
+ void SetDisplayName(const base::FilePath& name) override { NOTREACHED(); }
+ std::string DebugString(bool verbose) const override {
+ NOTREACHED();
+ return std::string();
+ }
+
+ private:
+ base::ObserverList<Observer> observers_;
+ uint32_t id_;
+ GURL url_;
+ base::FilePath file_path_;
+ bool is_file_externally_removed_;
+ base::Time end_time_;
+ DownloadState download_state_;
+
+ // The members bellow are to be returned by methods, which return links.
Marc Treib 2016/10/13 12:11:26 s/bellow/below/ s/links/pointers/ ? I assume that'
vitaliii 2016/10/15 18:36:31 First - yes. Second - no, I meant by reference.
+ std::string dummy_string;
+ std::vector<GURL> dummy_url_vector;
+ GURL dummy_url;
+ base::FilePath dummy_file_path;
+};
+
+std::unique_ptr<FakeDownloadItem> CreateDummyAssetDownload(int id) {
Marc Treib 2016/10/13 12:11:26 nit: Please move this down to the other CreateDumm
vitaliii 2016/10/15 18:36:31 Done.
+ 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;
+}
+
+class ObservedMockDownloadManager : public MockDownloadManager {
+ public:
+ ObservedMockDownloadManager() {}
+ ~ObservedMockDownloadManager() override {
+ FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown(this));
+ }
+
+ // Observer accessors.
+ void AddObserver(Observer* observer) override {
+ observers_.AddObserver(observer);
+ }
+
+ void RemoveObserver(Observer* observer) override {
+ observers_.RemoveObserver(observer);
+ }
+
+ void NotifyOnDownloadCreated(DownloadItem* item) {
+ FOR_EACH_OBSERVER(Observer, observers_, 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_;
+};
+
+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;
+ for (int id : ids) {
+ result.push_back(CreateDummyAssetDownload(id));
+ }
+ return result;
+}
+
+} // namespace
+
+class DownloadSuggestionsProviderTest : public testing::Test {
+ public:
+ DownloadSuggestionsProviderTest()
+ : pref_service_(new TestingPrefServiceSimple()) {
+ DownloadSuggestionsProvider::RegisterProfilePrefs(
+ pref_service()->registry());
+
+ scoped_refptr<OfflinePageProxy> proxy(
+ new OfflinePageProxy(&offline_pages_model_));
+
+ provider_.reset(
+ new DownloadSuggestionsProvider(&observer_, &category_factory_, proxy,
+ &downloads_manager_, pref_service(),
+ /*download_manager_ui_enabled=*/false));
+ }
+
+ Category downloads_category() {
+ return category_factory_.FromKnownCategory(
+ ntp_snippets::KnownCategories::DOWNLOADS);
+ }
+
+ void FireOfflinePageModelChanged(const std::vector<OfflinePageItem>& items) {
+ provider_->OfflinePageModelChanged(items);
+ }
+
+ void FireOfflinePageDeleted(const OfflinePageItem& item) {
+ provider_->OfflinePageDeleted(item.offline_id, item.client_id);
+ }
+
+ void FireOnDownloadCreated(DownloadItem* item) {
+ downloads_manager_.NotifyOnDownloadCreated(item);
+ }
+
+ void FireDownloadRemoved(FakeDownloadItem* item) {
Marc Treib 2016/10/13 12:11:26 nit: either FireOnDownloadRemoved (add "On"), or r
vitaliii 2016/10/15 18:36:30 Done.
+ item->NotifyOnDownloadRemoved();
+ }
+
+ void FireOnDownloadsCreated(
+ const std::vector<std::unique_ptr<FakeDownloadItem>>& items) {
+ for (const auto& item : items)
+ FireOnDownloadCreated(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::set<std::string> ReadDismissedIDsFromPrefs(bool for_offline_pages) {
+ return provider_->ReadDismissedIDsFromPrefs(for_offline_pages);
+ }
+
+ ContentSuggestionsProvider* 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_;
+ 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) {
+ std::vector<OfflinePageItem> offline_pages = CreateDummyOfflinePages({1, 2});
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(),
+ UnorderedElementsAre(
+ HasUrl("http://dummy.com/1"),
+ HasUrl("http://dummy.com/2"))));
+ FireOfflinePageModelChanged(offline_pages);
+}
+
+TEST_F(DownloadSuggestionsProviderTest,
+ ShouldConvertDownloadItemsToSuggestions) {
+ std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads =
+ CreateDummyAssetDownloads({1, 2});
+
+ FireOnDownloadCreated(asset_downloads[0].get());
Marc Treib 2016/10/13 12:11:26 Shouldn't the observer already get a call from thi
vitaliii 2016/10/15 18:36:31 Yes, it should. I use a StrictMock now.
+
+ EXPECT_CALL(*observer(),
+ OnNewSuggestions(
+ _, downloads_category(),
+ UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"),
+ HasUrl("file:///folder/file2.mhtml"))));
+
+ FireOnDownloadCreated(asset_downloads[1].get());
+}
+
+TEST_F(DownloadSuggestionsProviderTest, ShouldMixInBothSources) {
+ std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads =
+ CreateDummyAssetDownloads({1, 2});
+ std::vector<OfflinePageItem> offline_pages = CreateDummyOfflinePages({1, 2});
+
+ FireOnDownloadCreated(asset_downloads[0].get());
+ FireOnDownloadCreated(asset_downloads[1].get());
+
+ EXPECT_CALL(*observer(),
+ OnNewSuggestions(
+ _, downloads_category(),
+ UnorderedElementsAre(HasUrl("file:///folder/file1.mhtml"),
+ HasUrl("file:///folder/file2.mhtml"),
+ HasUrl("http://dummy.com/1"),
+ HasUrl("http://dummy.com/2"))));
+
+ FireOfflinePageModelChanged(offline_pages);
+}
+
+TEST_F(DownloadSuggestionsProviderTest, ShouldSortSuggestions) {
+ 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);
+
+ std::vector<OfflinePageItem> offline_pages;
+ offline_pages.push_back(CreateDummyOfflinePage(1, yesterday));
+ offline_pages.push_back(CreateDummyOfflinePage(2, tomorrow));
+ std::vector<std::unique_ptr<FakeDownloadItem>> asset_downloads;
+ asset_downloads.push_back(CreateDummyAssetDownload(3, next_week));
+ asset_downloads.push_back(CreateDummyAssetDownload(4, now));
+
+ FireOnDownloadCreated(asset_downloads[0].get());
+ FireOnDownloadCreated(asset_downloads[1].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"))));
+
+ FireOfflinePageModelChanged(offline_pages);
+}
+
+TEST_F(DownloadSuggestionsProviderTest, ShouldDismiss) {
+ // OfflinePageModel is initialised here because
+ // |GetDismissedSuggestionsForDebugging| may need to call |GetAllPages|
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2});
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({3, 4});
+ FireOnDownloadsCreated(downloads_manager()->items());
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0);
+ provider()->DismissSuggestion(
+ GetDummySuggestionId(1, /*is_offline_page=*/true));
+ provider()->DismissSuggestion(
+ GetDummySuggestionId(3, /*is_offline_page=*/false));
+ Mock::VerifyAndClearExpectations(observer());
+
+ // The dismissed suggestions should not be in the reported suggestions.
+ EXPECT_CALL(*observer(),
+ OnNewSuggestions(
+ _, downloads_category(),
+ UnorderedElementsAre(HasUrl("http://dummy.com/2"),
+ HasUrl("file:///folder/file4.mhtml"))));
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+ Mock::VerifyAndClearExpectations(observer());
+
+ // Instead they should be reported as dismissed.
+ std::vector<ContentSuggestion> dismissed_suggestions;
+ provider()->GetDismissedSuggestionsForDebugging(
+ downloads_category(),
+ base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions));
+ EXPECT_THAT(dismissed_suggestions,
+ UnorderedElementsAre(HasUrl("http://dummy.com/1"),
+ HasUrl("file:///folder/file3.mhtml")));
+
+ provider()->ClearDismissedSuggestionsForDebugging(downloads_category());
+
+ // No more dismissed suggestions.
+ dismissed_suggestions.clear();
+ provider()->GetDismissedSuggestionsForDebugging(
+ downloads_category(),
+ base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions));
+ EXPECT_THAT(dismissed_suggestions, IsEmpty());
+
+ // And they should be reported now.
+ EXPECT_CALL(*observer(),
+ OnNewSuggestions(_, downloads_category(), SizeIs(4)));
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+ Mock::VerifyAndClearExpectations(observer());
Marc Treib 2016/10/13 12:11:26 Not needed
vitaliii 2016/10/15 18:36:31 Done.
+}
+
+TEST_F(DownloadSuggestionsProviderTest,
+ ShouldNotDismissOtherTypeWithTheSameID) {
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2});
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({1, 2});
+ FireOnDownloadsCreated(downloads_manager()->items());
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0);
+ provider()->DismissSuggestion(
+ GetDummySuggestionId(1, /*is_offline_page=*/true));
+ Mock::VerifyAndClearExpectations(observer());
Marc Treib 2016/10/13 12:11:26 nit: This and the EXPECT_CALL above aren't really
vitaliii 2016/10/15 18:36:31 Done.
+
+ 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) {
+ *(downloads_manager()->mutable_items()) =
+ CreateDummyAssetDownloads({1, 2, 3, 4, 5, 6});
Marc Treib 2016/10/13 12:11:26 Took me a while to see why you need 6 :D Maybe add
vitaliii 2016/10/15 18:36:31 Done.
+ FireOnDownloadsCreated(downloads_manager()->items());
+
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0);
+ provider()->DismissSuggestion(
+ GetDummySuggestionId(1, /*is_offline_page=*/true));
Marc Treib 2016/10/13 12:11:26 Wait, this dismisses an offline page - does the te
vitaliii 2016/10/15 18:36:30 {2, 3, 4, 5, 6} were shown by the provider. I cha
+ Mock::VerifyAndClearExpectations(observer());
Marc Treib 2016/10/13 12:11:26 Also here. Generally, it's better to avoid VerifyA
vitaliii 2016/10/15 18:36:30 Done. Why?
Marc Treib 2016/10/17 10:18:40 It's a hint that the test is testing more than one
vitaliii 2016/10/26 00:07:54 I see, good point!
+
+ // The provider is not notified about the 6th item for the second time,
+ // however, it must report it.
+ 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"))));
Marc Treib 2016/10/13 12:11:26 Maybe also verify before dismissing that items 1-5
vitaliii 2016/10/15 18:36:31 Done.
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+}
+
+TEST_F(DownloadSuggestionsProviderTest,
+ ShouldInvalidateWhenUnderlyingItemDeleted) {
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({1, 2});
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+
+ *(downloads_manager()->mutable_items()) = CreateDummyAssetDownloads({3, 4});
+ FireOnDownloadsCreated(downloads_manager()->items());
+
+ EXPECT_CALL(*observer(),
+ OnSuggestionInvalidated(
+ _, GetDummySuggestionId(1, /*is_offline_page=*/true)));
+ FireOfflinePageDeleted(offline_pages_model()->items()[0]);
+ EXPECT_CALL(*observer(),
+ OnSuggestionInvalidated(
+ _, GetDummySuggestionId(3, /*is_offline_page=*/false)));
+ FireDownloadRemoved(downloads_manager()->items()[0].get());
+}
+
+TEST_F(DownloadSuggestionsProviderTest, ShouldReplaceRemovedItemWithNewData) {
+ *(downloads_manager()->mutable_items()) =
+ CreateDummyAssetDownloads({1, 2, 3, 4, 5, 6});
+ FireOnDownloadsCreated(downloads_manager()->items());
Marc Treib 2016/10/13 12:11:26 Also here: Verify that actually items 1-5 get repo
vitaliii 2016/10/15 18:36:31 Done.
+
+ EXPECT_CALL(*observer(),
+ OnSuggestionInvalidated(
+ _, GetDummySuggestionId(1, /*is_offline_page=*/false)));
+ // |OnDownloadRemoved| notification is done in |DownloadItem|'s destructor.
+ downloads_manager()->mutable_items()->erase(
+ downloads_manager()->mutable_items()->begin());
+
+ 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, ShouldPruneOfflinePagesDismissedIDs) {
+ *(offline_pages_model()->mutable_items()) =
+ CreateDummyOfflinePages({1, 2, 3});
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+
+ 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(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/true), SizeIs(3));
Marc Treib 2016/10/13 12:11:26 IMO the prefs are an implementation detail that do
vitaliii 2016/10/15 18:36:30 I use GetDismissedSuggestions now. However, this m
+
+ // Prune on getting all offline pages.
+ *(offline_pages_model()->mutable_items()) = CreateDummyOfflinePages({2, 3});
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+ EXPECT_THAT(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/true), SizeIs(2));
+
+ // Prune when offline page is deleted.
+ FireOfflinePageDeleted(offline_pages_model()->items()[0]);
+ EXPECT_THAT(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/true), SizeIs(1));
+
+ // Clear when explicitly requested.
+ provider()->ClearDismissedSuggestionsForDebugging(downloads_category());
+ EXPECT_THAT(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/true), SizeIs(0));
+}
+
+TEST_F(DownloadSuggestionsProviderTest, ShouldPruneAssetDownloadsDismissedIDs) {
+ *(downloads_manager()->mutable_items()) =
+ CreateDummyAssetDownloads({4, 5, 6, 7, 8, 9});
+ FireOnDownloadsCreated(downloads_manager()->items());
+
+ provider()->DismissSuggestion(
+ GetDummySuggestionId(4, /*is_offline_page=*/false));
+ provider()->DismissSuggestion(
+ GetDummySuggestionId(5, /*is_offline_page=*/false));
+ EXPECT_THAT(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/false),
+ SizeIs(2));
+
+ // Prune when item is deleted.
+ FireDownloadRemoved(downloads_manager()->items()[0].get());
+ EXPECT_THAT(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/false),
+ SizeIs(1));
+
+ // Clear when explicitly requested.
+ provider()->ClearDismissedSuggestionsForDebugging(downloads_category());
+ EXPECT_THAT(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/false),
+ SizeIs(0));
+
+ // Simulate that we miss |OnDownloadRemoved|. The pruning should occur when
+ // all pages are fetched.
+ std::unique_ptr<FakeDownloadItem> missed_item = CreateDummyAssetDownload(100);
+ FireOnDownloadCreated(missed_item.get());
+
+ provider()->DismissSuggestion(
+ GetDummySuggestionId(100, /*is_offline_page=*/false));
+ FireOfflinePageModelChanged(offline_pages_model()->items());
+ // After OfflinePage model change all download items request must have
+ // occurred. Note that |missed_item| is not in |downloads_manager| and so the
+ // provider does not receive it.
+ EXPECT_THAT(ReadDismissedIDsFromPrefs(/*for_offline_pages=*/false),
+ SizeIs(0));
+}

Powered by Google App Engine
This is Rietveld 408576698