Index: components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc |
diff --git a/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b0bdda17e4bda8f432205df0ebca01d55d258bb9 |
--- /dev/null |
+++ b/components/ntp_snippets/sessions/foreign_sessions_suggestions_provider_unittest.cc |
@@ -0,0 +1,335 @@ |
+// 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 "components/ntp_snippets/sessions/foreign_sessions_suggestions_provider.h" |
+ |
+#include <map> |
+#include <utility> |
+ |
+#include "base/memory/ptr_util.h" |
+#include "components/ntp_snippets/category.h" |
+#include "components/ntp_snippets/category_factory.h" |
+#include "components/ntp_snippets/content_suggestions_provider.h" |
+#include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" |
+#include "components/prefs/testing_pref_service.h" |
+#include "components/sessions/core/serialized_navigation_entry.h" |
+#include "components/sessions/core/serialized_navigation_entry_test_helper.h" |
+#include "components/sessions/core/session_types.h" |
+#include "components/sync_sessions/open_tabs_ui_delegate.h" |
+#include "components/sync_sessions/synced_session.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using base::Time; |
+using base::TimeDelta; |
+using sessions::SerializedNavigationEntry; |
+using sessions::SessionTab; |
+using sessions::SessionWindow; |
+using sync_driver::SyncedSession; |
+using testing::IsEmpty; |
+using testing::Property; |
+using testing::ElementsAre; |
+using testing::UnorderedElementsAre; |
+using testing::_; |
+ |
+namespace ntp_snippets { |
+namespace { |
+ |
+const char kUrl1[] = "http://www.fake1.com/"; |
+const char kUrl2[] = "http://www.fake2.com/"; |
+const char kUrl3[] = "http://www.fake3.com/"; |
+const char kUrl4[] = "http://www.fake4.com/"; |
+const char kUrl5[] = "http://www.fake5.com/"; |
+const char kUrl6[] = "http://www.fake6.com/"; |
+const char kUrl7[] = "http://www.fake7.com/"; |
+const char kUrl8[] = "http://www.fake8.com/"; |
+const char kUrl9[] = "http://www.fake9.com/"; |
+const char kUrl10[] = "http://www.fake10.com/"; |
+const char kUrl11[] = "http://www.fake11.com/"; |
+const char kTitle[] = "title is ignored"; |
+ |
+SessionWindow* GetWindow(SyncedSession* session, int window_id) { |
Marc Treib
2016/09/16 12:26:49
GetOrCreateWindow?
skym
2016/09/16 18:18:49
Done.
|
+ if (session->windows.find(window_id) == session->windows.end()) { |
+ // The session deletes the windows it points at upon destruction. |
+ session->windows[window_id] = new SessionWindow(); |
+ } |
+ return session->windows[window_id]; |
+} |
+ |
+void AddTabToSession(SyncedSession* session, |
+ int window_id, |
+ const std::string& url, |
+ TimeDelta age) { |
+ SerializedNavigationEntry navigation = |
+ sessions::SerializedNavigationEntryTestHelper::CreateNavigation(url, |
+ kTitle); |
+ |
+ std::unique_ptr<SessionTab> tab = base::MakeUnique<SessionTab>(); |
+ tab->timestamp = Time::Now() - age; |
+ tab->navigations.push_back(navigation); |
+ |
+ SessionWindow* window = GetWindow(session, window_id); |
+ // The window deletes the tabs it points at upon destruction. |
+ window->tabs.push_back(tab.release()); |
+} |
+ |
+class FakeTabsDelegate |
+ : public sync_driver::OpenTabsUIDelegate, |
tschumann
2016/09/16 13:33:20
do we actually need to implement this interface?
skym
2016/09/16 18:18:49
Done. Ended up removing OpenTabsUIDelegate from th
tschumann
2016/09/17 15:52:56
:-) I think so too!
|
+ public ForeignSessionsSuggestionsProvider::OpenTabsUIDelegateProvider { |
+ public: |
+ ~FakeTabsDelegate() override {} |
+ void SetAllForeignSessions(std::vector<const SyncedSession*> sessions) { |
+ sessions_ = sessions; |
+ change_callback_.Run(this); |
+ } |
+ |
+ // OpenTabsUIDelegate implementation. |
+ bool GetSyncedFaviconForPageURL( |
+ const std::string& pageurl, |
+ scoped_refptr<base::RefCountedMemory>* favicon_png) const override { |
+ return false; |
+ } |
+ bool GetAllForeignSessions( |
+ std::vector<const SyncedSession*>* sessions) override { |
+ *sessions = sessions_; |
+ return !sessions->empty(); |
+ } |
+ bool GetForeignTab(const std::string& tag, |
+ const SessionID::id_type tab_id, |
+ const sessions::SessionTab** tab) override { |
+ return false; |
+ } |
+ void DeleteForeignSession(const std::string& tag) override {} |
+ bool GetForeignSession( |
+ const std::string& tag, |
+ std::vector<const sessions::SessionWindow*>* windows) override { |
+ return false; |
+ } |
+ bool GetForeignSessionTabs( |
+ const std::string& tag, |
+ std::vector<const sessions::SessionTab*>* tabs) override { |
+ return false; |
+ } |
+ bool GetLocalSession(const SyncedSession** local) override { return false; } |
+ |
+ // OpenTabsUIDelegateProvider implementation. |
+ sync_driver::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override { |
+ return this; |
+ } |
+ void SubscribeForForeignTabChange( |
+ const OnChangeWithDelegate& change_callback) override { |
+ change_callback_ = change_callback; |
+ } |
+ |
+ private: |
+ std::vector<const SyncedSession*> sessions_; |
+ OnChangeWithDelegate change_callback_; |
+}; |
+} // namespace |
+ |
+class ForeignSessionsSuggestionsProviderTest : public testing::Test { |
+ public: |
+ ForeignSessionsSuggestionsProviderTest() |
+ : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()) { |
+ ForeignSessionsSuggestionsProvider::RegisterProfilePrefs( |
+ pref_service_->registry()); |
+ |
+ std::unique_ptr<FakeTabsDelegate> delegate = |
+ base::MakeUnique<FakeTabsDelegate>(); |
+ fake_tabs_delegate_ = delegate.get(); |
+ |
+ // During the provider's construction the follow mock calls occur. |
Marc Treib
2016/09/16 12:26:48
nit: s/follow/following/
skym
2016/09/16 18:18:49
Done.
|
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions(_, category(), testing::IsEmpty())); |
Marc Treib
2016/09/16 12:26:49
nit: "testing::" not required, there's a "using" a
skym
2016/09/16 18:18:49
Done.
|
+ EXPECT_CALL(*observer(), OnCategoryStatusChanged( |
+ _, category(), CategoryStatus::AVAILABLE)); |
+ |
+ provider_ = base::MakeUnique<ForeignSessionsSuggestionsProvider>( |
+ &observer_, &category_factory_, std::move(delegate), |
+ pref_service_.get()); |
+ } |
+ |
+ protected: |
+ SyncedSession* GetSession(int session_id) { |
Marc Treib
2016/09/16 12:26:48
GotOrCreateSession?
skym
2016/09/16 18:18:49
Done.
|
+ if (sessions_map_.find(session_id) == sessions_map_.end()) { |
+ std::string id_as_string = std::to_string(session_id); |
Marc Treib
2016/09/16 12:26:48
Per https://chromium-cpp.appspot.com/, std::to_str
skym
2016/09/16 18:18:49
:( Done.
|
+ std::unique_ptr<SyncedSession> owned_session = |
+ base::MakeUnique<SyncedSession>(); |
+ owned_session->session_tag = id_as_string; |
+ owned_session->session_name = id_as_string; |
+ sessions_map_[session_id] = std::move(owned_session); |
+ } |
+ return sessions_map_[session_id].get(); |
+ } |
+ |
+ void AddTab(int session_id, |
+ int window_id, |
+ const std::string& url, |
+ TimeDelta age) { |
+ AddTabToSession(GetSession(session_id), window_id, url, age); |
+ } |
+ |
+ void TriggerOnChange() { |
+ std::vector<const SyncedSession*> sessions; |
+ for (const auto& kv : sessions_map_) { |
+ sessions.push_back(kv.second.get()); |
+ } |
+ fake_tabs_delegate_->SetAllForeignSessions(sessions); |
+ } |
+ |
+ void Dismiss(const std::string& url) { |
Marc Treib
2016/09/16 12:26:48
s/url/id/ ?
skym
2016/09/16 18:18:49
So someone has to decide that to create a within_c
|
+ provider_->DismissSuggestion(provider_->MakeUniqueID(category(), url)); |
+ } |
+ |
+ Category category() { |
+ return category_factory_.FromKnownCategory(KnownCategories::FOREIGN_TABS); |
+ } |
+ |
+ MockContentSuggestionsProviderObserver* observer() { return &observer_; } |
+ |
+ private: |
+ FakeTabsDelegate* fake_tabs_delegate_; |
+ MockContentSuggestionsProviderObserver observer_; |
+ CategoryFactory category_factory_; |
+ std::unique_ptr<TestingPrefServiceSimple> pref_service_; |
Marc Treib
2016/09/16 12:26:48
I think this could just be an instance rather than
skym
2016/09/16 18:18:49
Done.
|
+ std::unique_ptr<ForeignSessionsSuggestionsProvider> provider_; |
+ std::map<int, std::unique_ptr<SyncedSession>> sessions_map_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ForeignSessionsSuggestionsProviderTest); |
+}; |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, Empty) { |
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, category(), testing::IsEmpty())); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, Single) { |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, category(), |
+ ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1))))); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, Old) { |
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, category(), testing::IsEmpty())); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromHours(4)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, Ordered) { |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, category(), |
+ ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1)), |
+ Property(&ContentSuggestion::url, GURL(kUrl2)), |
+ Property(&ContentSuggestion::url, GURL(kUrl3)), |
+ Property(&ContentSuggestion::url, GURL(kUrl4))))); |
+ AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); |
+ AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); |
+ AddTab(0, 1, kUrl3, TimeDelta::FromMinutes(3)); |
+ AddTab(1, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, MaxPerDevice) { |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, category(), |
+ ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1)), |
+ Property(&ContentSuggestion::url, GURL(kUrl2)), |
+ Property(&ContentSuggestion::url, GURL(kUrl3))))); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); |
+ AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); |
+ AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, MaxTotal) { |
+ EXPECT_CALL( |
Marc Treib
2016/09/16 12:26:49
nit: I think all of these tests deserve a one-line
skym
2016/09/16 18:18:49
Done.
|
+ *observer(), |
+ OnNewSuggestions( |
+ _, category(), |
+ ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1)), |
+ Property(&ContentSuggestion::url, GURL(kUrl2)), |
+ Property(&ContentSuggestion::url, GURL(kUrl3)), |
+ Property(&ContentSuggestion::url, GURL(kUrl4)), |
+ Property(&ContentSuggestion::url, GURL(kUrl5)), |
+ Property(&ContentSuggestion::url, GURL(kUrl6)), |
+ Property(&ContentSuggestion::url, GURL(kUrl7)), |
+ Property(&ContentSuggestion::url, GURL(kUrl8)), |
+ Property(&ContentSuggestion::url, GURL(kUrl9)), |
+ Property(&ContentSuggestion::url, GURL(kUrl10))))); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); |
+ AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); |
+ AddTab(1, 0, kUrl4, TimeDelta::FromMinutes(4)); |
+ AddTab(1, 0, kUrl5, TimeDelta::FromMinutes(5)); |
+ AddTab(1, 0, kUrl6, TimeDelta::FromMinutes(6)); |
+ AddTab(2, 0, kUrl7, TimeDelta::FromMinutes(7)); |
+ AddTab(2, 0, kUrl8, TimeDelta::FromMinutes(8)); |
+ AddTab(2, 0, kUrl9, TimeDelta::FromMinutes(9)); |
+ AddTab(3, 0, kUrl10, TimeDelta::FromMinutes(10)); |
+ AddTab(3, 0, kUrl11, TimeDelta::FromMinutes(11)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, Duplicates) { |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, category(), |
+ ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1))))); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ AddTab(0, 1, kUrl1, TimeDelta::FromMinutes(2)); |
+ AddTab(1, 1, kUrl1, TimeDelta::FromMinutes(3)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, DuplicatesChangingOtherSession) { |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, category(), |
+ ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl3)), |
+ Property(&ContentSuggestion::url, GURL(kUrl1)), |
+ Property(&ContentSuggestion::url, GURL(kUrl2)), |
+ Property(&ContentSuggestion::url, GURL(kUrl4))))); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); |
+ AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); |
+ // Normally |kUrl4| wouldn't show up, because session_id=0 already has 3 |
+ // younger tabs, but session_id=1 has a younger |kUrl3| which gives |kUrl4| a |
+ // spot. |
+ AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); |
+ AddTab(1, 0, kUrl3, TimeDelta::FromMinutes(0)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, Dismissed) { |
+ EXPECT_CALL(*observer(), OnNewSuggestions(_, category(), testing::IsEmpty())); |
+ Dismiss(kUrl1); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ TriggerOnChange(); |
+} |
+ |
+TEST_F(ForeignSessionsSuggestionsProviderTest, DismissedChangingOwnSession) { |
+ EXPECT_CALL(*observer(), |
+ OnNewSuggestions( |
+ _, category(), |
+ ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl2)), |
+ Property(&ContentSuggestion::url, GURL(kUrl3)), |
+ Property(&ContentSuggestion::url, GURL(kUrl5))))); |
+ Dismiss(kUrl1); |
+ Dismiss(kUrl4); |
+ AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); |
+ AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); |
+ AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); |
+ AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); |
+ AddTab(0, 0, kUrl5, TimeDelta::FromMinutes(5)); |
+ AddTab(0, 0, kUrl6, TimeDelta::FromMinutes(6)); |
+ TriggerOnChange(); |
+} |
+ |
+} // namespace ntp_snippets |