Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/ntp_snippets/sessions/foreign_sessions_suggestions_provider .h" | |
| 6 | |
| 7 #include <map> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/memory/ptr_util.h" | |
| 11 #include "components/ntp_snippets/category.h" | |
| 12 #include "components/ntp_snippets/category_factory.h" | |
| 13 #include "components/ntp_snippets/content_suggestions_provider.h" | |
| 14 #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" | |
| 15 #include "components/prefs/testing_pref_service.h" | |
| 16 #include "components/sessions/core/serialized_navigation_entry.h" | |
| 17 #include "components/sessions/core/serialized_navigation_entry_test_helper.h" | |
| 18 #include "components/sessions/core/session_types.h" | |
| 19 #include "components/sync_sessions/open_tabs_ui_delegate.h" | |
| 20 #include "components/sync_sessions/synced_session.h" | |
| 21 #include "testing/gmock/include/gmock/gmock.h" | |
| 22 #include "testing/gtest/include/gtest/gtest.h" | |
| 23 | |
| 24 using base::Time; | |
| 25 using base::TimeDelta; | |
| 26 using sessions::SerializedNavigationEntry; | |
| 27 using sessions::SessionTab; | |
| 28 using sessions::SessionWindow; | |
| 29 using sync_driver::SyncedSession; | |
| 30 using testing::IsEmpty; | |
| 31 using testing::Property; | |
| 32 using testing::ElementsAre; | |
| 33 using testing::UnorderedElementsAre; | |
| 34 using testing::_; | |
| 35 | |
| 36 namespace ntp_snippets { | |
| 37 namespace { | |
| 38 | |
| 39 const char kUrl1[] = "http://www.fake1.com/"; | |
| 40 const char kUrl2[] = "http://www.fake2.com/"; | |
| 41 const char kUrl3[] = "http://www.fake3.com/"; | |
| 42 const char kUrl4[] = "http://www.fake4.com/"; | |
| 43 const char kUrl5[] = "http://www.fake5.com/"; | |
| 44 const char kUrl6[] = "http://www.fake6.com/"; | |
| 45 const char kUrl7[] = "http://www.fake7.com/"; | |
| 46 const char kUrl8[] = "http://www.fake8.com/"; | |
| 47 const char kUrl9[] = "http://www.fake9.com/"; | |
| 48 const char kUrl10[] = "http://www.fake10.com/"; | |
| 49 const char kUrl11[] = "http://www.fake11.com/"; | |
| 50 const char kTitle[] = "title is ignored"; | |
| 51 | |
| 52 SessionWindow* GetWindow(SyncedSession* session, int window_id) { | |
|
Marc Treib
2016/09/16 12:26:49
GetOrCreateWindow?
skym
2016/09/16 18:18:49
Done.
| |
| 53 if (session->windows.find(window_id) == session->windows.end()) { | |
| 54 // The session deletes the windows it points at upon destruction. | |
| 55 session->windows[window_id] = new SessionWindow(); | |
| 56 } | |
| 57 return session->windows[window_id]; | |
| 58 } | |
| 59 | |
| 60 void AddTabToSession(SyncedSession* session, | |
| 61 int window_id, | |
| 62 const std::string& url, | |
| 63 TimeDelta age) { | |
| 64 SerializedNavigationEntry navigation = | |
| 65 sessions::SerializedNavigationEntryTestHelper::CreateNavigation(url, | |
| 66 kTitle); | |
| 67 | |
| 68 std::unique_ptr<SessionTab> tab = base::MakeUnique<SessionTab>(); | |
| 69 tab->timestamp = Time::Now() - age; | |
| 70 tab->navigations.push_back(navigation); | |
| 71 | |
| 72 SessionWindow* window = GetWindow(session, window_id); | |
| 73 // The window deletes the tabs it points at upon destruction. | |
| 74 window->tabs.push_back(tab.release()); | |
| 75 } | |
| 76 | |
| 77 class FakeTabsDelegate | |
| 78 : 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!
| |
| 79 public ForeignSessionsSuggestionsProvider::OpenTabsUIDelegateProvider { | |
| 80 public: | |
| 81 ~FakeTabsDelegate() override {} | |
| 82 void SetAllForeignSessions(std::vector<const SyncedSession*> sessions) { | |
| 83 sessions_ = sessions; | |
| 84 change_callback_.Run(this); | |
| 85 } | |
| 86 | |
| 87 // OpenTabsUIDelegate implementation. | |
| 88 bool GetSyncedFaviconForPageURL( | |
| 89 const std::string& pageurl, | |
| 90 scoped_refptr<base::RefCountedMemory>* favicon_png) const override { | |
| 91 return false; | |
| 92 } | |
| 93 bool GetAllForeignSessions( | |
| 94 std::vector<const SyncedSession*>* sessions) override { | |
| 95 *sessions = sessions_; | |
| 96 return !sessions->empty(); | |
| 97 } | |
| 98 bool GetForeignTab(const std::string& tag, | |
| 99 const SessionID::id_type tab_id, | |
| 100 const sessions::SessionTab** tab) override { | |
| 101 return false; | |
| 102 } | |
| 103 void DeleteForeignSession(const std::string& tag) override {} | |
| 104 bool GetForeignSession( | |
| 105 const std::string& tag, | |
| 106 std::vector<const sessions::SessionWindow*>* windows) override { | |
| 107 return false; | |
| 108 } | |
| 109 bool GetForeignSessionTabs( | |
| 110 const std::string& tag, | |
| 111 std::vector<const sessions::SessionTab*>* tabs) override { | |
| 112 return false; | |
| 113 } | |
| 114 bool GetLocalSession(const SyncedSession** local) override { return false; } | |
| 115 | |
| 116 // OpenTabsUIDelegateProvider implementation. | |
| 117 sync_driver::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override { | |
| 118 return this; | |
| 119 } | |
| 120 void SubscribeForForeignTabChange( | |
| 121 const OnChangeWithDelegate& change_callback) override { | |
| 122 change_callback_ = change_callback; | |
| 123 } | |
| 124 | |
| 125 private: | |
| 126 std::vector<const SyncedSession*> sessions_; | |
| 127 OnChangeWithDelegate change_callback_; | |
| 128 }; | |
| 129 } // namespace | |
| 130 | |
| 131 class ForeignSessionsSuggestionsProviderTest : public testing::Test { | |
| 132 public: | |
| 133 ForeignSessionsSuggestionsProviderTest() | |
| 134 : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()) { | |
| 135 ForeignSessionsSuggestionsProvider::RegisterProfilePrefs( | |
| 136 pref_service_->registry()); | |
| 137 | |
| 138 std::unique_ptr<FakeTabsDelegate> delegate = | |
| 139 base::MakeUnique<FakeTabsDelegate>(); | |
| 140 fake_tabs_delegate_ = delegate.get(); | |
| 141 | |
| 142 // 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.
| |
| 143 EXPECT_CALL(*observer(), | |
| 144 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.
| |
| 145 EXPECT_CALL(*observer(), OnCategoryStatusChanged( | |
| 146 _, category(), CategoryStatus::AVAILABLE)); | |
| 147 | |
| 148 provider_ = base::MakeUnique<ForeignSessionsSuggestionsProvider>( | |
| 149 &observer_, &category_factory_, std::move(delegate), | |
| 150 pref_service_.get()); | |
| 151 } | |
| 152 | |
| 153 protected: | |
| 154 SyncedSession* GetSession(int session_id) { | |
|
Marc Treib
2016/09/16 12:26:48
GotOrCreateSession?
skym
2016/09/16 18:18:49
Done.
| |
| 155 if (sessions_map_.find(session_id) == sessions_map_.end()) { | |
| 156 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.
| |
| 157 std::unique_ptr<SyncedSession> owned_session = | |
| 158 base::MakeUnique<SyncedSession>(); | |
| 159 owned_session->session_tag = id_as_string; | |
| 160 owned_session->session_name = id_as_string; | |
| 161 sessions_map_[session_id] = std::move(owned_session); | |
| 162 } | |
| 163 return sessions_map_[session_id].get(); | |
| 164 } | |
| 165 | |
| 166 void AddTab(int session_id, | |
| 167 int window_id, | |
| 168 const std::string& url, | |
| 169 TimeDelta age) { | |
| 170 AddTabToSession(GetSession(session_id), window_id, url, age); | |
| 171 } | |
| 172 | |
| 173 void TriggerOnChange() { | |
| 174 std::vector<const SyncedSession*> sessions; | |
| 175 for (const auto& kv : sessions_map_) { | |
| 176 sessions.push_back(kv.second.get()); | |
| 177 } | |
| 178 fake_tabs_delegate_->SetAllForeignSessions(sessions); | |
| 179 } | |
| 180 | |
| 181 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
| |
| 182 provider_->DismissSuggestion(provider_->MakeUniqueID(category(), url)); | |
| 183 } | |
| 184 | |
| 185 Category category() { | |
| 186 return category_factory_.FromKnownCategory(KnownCategories::FOREIGN_TABS); | |
| 187 } | |
| 188 | |
| 189 MockContentSuggestionsProviderObserver* observer() { return &observer_; } | |
| 190 | |
| 191 private: | |
| 192 FakeTabsDelegate* fake_tabs_delegate_; | |
| 193 MockContentSuggestionsProviderObserver observer_; | |
| 194 CategoryFactory category_factory_; | |
| 195 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.
| |
| 196 std::unique_ptr<ForeignSessionsSuggestionsProvider> provider_; | |
| 197 std::map<int, std::unique_ptr<SyncedSession>> sessions_map_; | |
| 198 | |
| 199 DISALLOW_COPY_AND_ASSIGN(ForeignSessionsSuggestionsProviderTest); | |
| 200 }; | |
| 201 | |
| 202 TEST_F(ForeignSessionsSuggestionsProviderTest, Empty) { | |
| 203 EXPECT_CALL(*observer(), OnNewSuggestions(_, category(), testing::IsEmpty())); | |
| 204 TriggerOnChange(); | |
| 205 } | |
| 206 | |
| 207 TEST_F(ForeignSessionsSuggestionsProviderTest, Single) { | |
| 208 EXPECT_CALL(*observer(), | |
| 209 OnNewSuggestions( | |
| 210 _, category(), | |
| 211 ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1))))); | |
| 212 AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 213 TriggerOnChange(); | |
| 214 } | |
| 215 | |
| 216 TEST_F(ForeignSessionsSuggestionsProviderTest, Old) { | |
| 217 EXPECT_CALL(*observer(), OnNewSuggestions(_, category(), testing::IsEmpty())); | |
| 218 AddTab(0, 0, kUrl1, TimeDelta::FromHours(4)); | |
| 219 TriggerOnChange(); | |
| 220 } | |
| 221 | |
| 222 TEST_F(ForeignSessionsSuggestionsProviderTest, Ordered) { | |
| 223 EXPECT_CALL(*observer(), | |
| 224 OnNewSuggestions( | |
| 225 _, category(), | |
| 226 ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1)), | |
| 227 Property(&ContentSuggestion::url, GURL(kUrl2)), | |
| 228 Property(&ContentSuggestion::url, GURL(kUrl3)), | |
| 229 Property(&ContentSuggestion::url, GURL(kUrl4))))); | |
| 230 AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); | |
| 231 AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); | |
| 232 AddTab(0, 1, kUrl3, TimeDelta::FromMinutes(3)); | |
| 233 AddTab(1, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 234 TriggerOnChange(); | |
| 235 } | |
| 236 | |
| 237 TEST_F(ForeignSessionsSuggestionsProviderTest, MaxPerDevice) { | |
| 238 EXPECT_CALL(*observer(), | |
| 239 OnNewSuggestions( | |
| 240 _, category(), | |
| 241 ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1)), | |
| 242 Property(&ContentSuggestion::url, GURL(kUrl2)), | |
| 243 Property(&ContentSuggestion::url, GURL(kUrl3))))); | |
| 244 AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 245 AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); | |
| 246 AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); | |
| 247 AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); | |
| 248 TriggerOnChange(); | |
| 249 } | |
| 250 | |
| 251 TEST_F(ForeignSessionsSuggestionsProviderTest, MaxTotal) { | |
| 252 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.
| |
| 253 *observer(), | |
| 254 OnNewSuggestions( | |
| 255 _, category(), | |
| 256 ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1)), | |
| 257 Property(&ContentSuggestion::url, GURL(kUrl2)), | |
| 258 Property(&ContentSuggestion::url, GURL(kUrl3)), | |
| 259 Property(&ContentSuggestion::url, GURL(kUrl4)), | |
| 260 Property(&ContentSuggestion::url, GURL(kUrl5)), | |
| 261 Property(&ContentSuggestion::url, GURL(kUrl6)), | |
| 262 Property(&ContentSuggestion::url, GURL(kUrl7)), | |
| 263 Property(&ContentSuggestion::url, GURL(kUrl8)), | |
| 264 Property(&ContentSuggestion::url, GURL(kUrl9)), | |
| 265 Property(&ContentSuggestion::url, GURL(kUrl10))))); | |
| 266 AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 267 AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); | |
| 268 AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); | |
| 269 AddTab(1, 0, kUrl4, TimeDelta::FromMinutes(4)); | |
| 270 AddTab(1, 0, kUrl5, TimeDelta::FromMinutes(5)); | |
| 271 AddTab(1, 0, kUrl6, TimeDelta::FromMinutes(6)); | |
| 272 AddTab(2, 0, kUrl7, TimeDelta::FromMinutes(7)); | |
| 273 AddTab(2, 0, kUrl8, TimeDelta::FromMinutes(8)); | |
| 274 AddTab(2, 0, kUrl9, TimeDelta::FromMinutes(9)); | |
| 275 AddTab(3, 0, kUrl10, TimeDelta::FromMinutes(10)); | |
| 276 AddTab(3, 0, kUrl11, TimeDelta::FromMinutes(11)); | |
| 277 TriggerOnChange(); | |
| 278 } | |
| 279 | |
| 280 TEST_F(ForeignSessionsSuggestionsProviderTest, Duplicates) { | |
| 281 EXPECT_CALL(*observer(), | |
| 282 OnNewSuggestions( | |
| 283 _, category(), | |
| 284 ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl1))))); | |
| 285 AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 286 AddTab(0, 1, kUrl1, TimeDelta::FromMinutes(2)); | |
| 287 AddTab(1, 1, kUrl1, TimeDelta::FromMinutes(3)); | |
| 288 TriggerOnChange(); | |
| 289 } | |
| 290 | |
| 291 TEST_F(ForeignSessionsSuggestionsProviderTest, DuplicatesChangingOtherSession) { | |
| 292 EXPECT_CALL(*observer(), | |
| 293 OnNewSuggestions( | |
| 294 _, category(), | |
| 295 ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl3)), | |
| 296 Property(&ContentSuggestion::url, GURL(kUrl1)), | |
| 297 Property(&ContentSuggestion::url, GURL(kUrl2)), | |
| 298 Property(&ContentSuggestion::url, GURL(kUrl4))))); | |
| 299 AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 300 AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); | |
| 301 AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); | |
| 302 // Normally |kUrl4| wouldn't show up, because session_id=0 already has 3 | |
| 303 // younger tabs, but session_id=1 has a younger |kUrl3| which gives |kUrl4| a | |
| 304 // spot. | |
| 305 AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); | |
| 306 AddTab(1, 0, kUrl3, TimeDelta::FromMinutes(0)); | |
| 307 TriggerOnChange(); | |
| 308 } | |
| 309 | |
| 310 TEST_F(ForeignSessionsSuggestionsProviderTest, Dismissed) { | |
| 311 EXPECT_CALL(*observer(), OnNewSuggestions(_, category(), testing::IsEmpty())); | |
| 312 Dismiss(kUrl1); | |
| 313 AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 314 TriggerOnChange(); | |
| 315 } | |
| 316 | |
| 317 TEST_F(ForeignSessionsSuggestionsProviderTest, DismissedChangingOwnSession) { | |
| 318 EXPECT_CALL(*observer(), | |
| 319 OnNewSuggestions( | |
| 320 _, category(), | |
| 321 ElementsAre(Property(&ContentSuggestion::url, GURL(kUrl2)), | |
| 322 Property(&ContentSuggestion::url, GURL(kUrl3)), | |
| 323 Property(&ContentSuggestion::url, GURL(kUrl5))))); | |
| 324 Dismiss(kUrl1); | |
| 325 Dismiss(kUrl4); | |
| 326 AddTab(0, 0, kUrl1, TimeDelta::FromMinutes(1)); | |
| 327 AddTab(0, 0, kUrl2, TimeDelta::FromMinutes(2)); | |
| 328 AddTab(0, 0, kUrl3, TimeDelta::FromMinutes(3)); | |
| 329 AddTab(0, 0, kUrl4, TimeDelta::FromMinutes(4)); | |
| 330 AddTab(0, 0, kUrl5, TimeDelta::FromMinutes(5)); | |
| 331 AddTab(0, 0, kUrl6, TimeDelta::FromMinutes(6)); | |
| 332 TriggerOnChange(); | |
| 333 } | |
| 334 | |
| 335 } // namespace ntp_snippets | |
| OLD | NEW |