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 |