Chromium Code Reviews| 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)); |
| +} |