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/offline_pages/offline_page_suggestions_provide r.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/files/file_path.h" | |
12 #include "base/guid.h" | |
13 #include "base/strings/string_number_conversions.h" | |
14 #include "base/strings/utf_string_conversions.h" | |
15 #include "base/time/time.h" | |
16 #include "components/ntp_snippets/category.h" | |
17 #include "components/ntp_snippets/category_factory.h" | |
18 #include "components/ntp_snippets/content_suggestions_provider.h" | |
19 #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h" | |
20 #include "components/offline_pages/client_namespace_constants.h" | |
21 #include "components/offline_pages/offline_page_item.h" | |
22 #include "components/offline_pages/stub_offline_page_model.h" | |
23 #include "components/prefs/testing_pref_service.h" | |
24 #include "testing/gmock/include/gmock/gmock.h" | |
25 #include "testing/gtest/include/gtest/gtest.h" | |
26 | |
27 using offline_pages::ClientId; | |
28 using offline_pages::MultipleOfflinePageItemCallback; | |
29 using offline_pages::OfflinePageItem; | |
30 using offline_pages::StubOfflinePageModel; | |
31 using testing::AllOf; | |
32 using testing::Eq; | |
33 using testing::Invoke; | |
34 using testing::IsEmpty; | |
35 using testing::Property; | |
36 using testing::ElementsAre; | |
37 using testing::Mock; | |
38 using testing::UnorderedElementsAre; | |
39 using testing::UnorderedElementsAreArray; | |
40 using testing::SizeIs; | |
41 using testing::WhenSortedBy; | |
42 using testing::_; | |
43 | |
44 namespace ntp_snippets { | |
45 | |
46 namespace { | |
47 | |
48 struct OrderByMostRecentlyVisited { | |
49 bool operator()(const OfflinePageItem* left, | |
50 const OfflinePageItem* right) const { | |
51 return left->last_access_time > right->last_access_time; | |
52 } | |
53 }; | |
54 | |
55 OfflinePageItem CreateDummyItem(std::string name_space, int id) { | |
56 std::string strid = base::IntToString(id); | |
57 return OfflinePageItem(GURL("http://dummy.com/" + strid), id, | |
58 ClientId(name_space, base::GenerateGUID()), | |
59 base::FilePath("some/folder/test" + strid + ".mhtml"), | |
60 0, base::Time::Now()); | |
61 } | |
62 | |
63 OfflinePageItem CreateDummyRecentTab(int id) { | |
64 return CreateDummyItem(offline_pages::kLastNNamespace, id); | |
65 } | |
66 | |
67 OfflinePageItem CreateDummyRecentTab(int id, base::Time time) { | |
68 OfflinePageItem item = CreateDummyRecentTab(id); | |
69 item.last_access_time = time; | |
70 return item; | |
71 } | |
72 | |
73 OfflinePageItem CreateDummyDownload(int id) { | |
74 return CreateDummyItem(offline_pages::kAsyncNamespace, id); | |
75 } | |
76 | |
77 } // namespace | |
78 | |
79 class MockOfflinePageModel : public StubOfflinePageModel { | |
80 public: | |
81 MockOfflinePageModel() {} | |
82 | |
83 void GetAllPages(const MultipleOfflinePageItemCallback& callback) override { | |
84 callback.Run(items_); | |
85 } | |
86 | |
87 std::vector<OfflinePageItem>* items() { return &items_; } | |
88 | |
89 private: | |
90 std::vector<OfflinePageItem> items_; | |
91 }; | |
92 | |
93 class OfflinePageSuggestionsProviderTest : public testing::Test { | |
94 public: | |
95 OfflinePageSuggestionsProviderTest() | |
96 : pref_service_(new TestingPrefServiceSimple()) { | |
97 OfflinePageSuggestionsProvider::RegisterProfilePrefs( | |
98 pref_service()->registry()); | |
99 CreateProvider(true, true, true); | |
100 } | |
101 | |
102 void RecreateProvider(bool recent_tabs_enabled, | |
103 bool downloads_enabled, | |
104 bool download_manager_ui_enabled) { | |
105 provider_.reset(); | |
106 CreateProvider(recent_tabs_enabled, downloads_enabled, | |
107 download_manager_ui_enabled); | |
108 } | |
109 | |
110 void CreateProvider(bool recent_tabs_enabled, | |
111 bool downloads_enabled, | |
112 bool download_manager_ui_enabled) { | |
113 DCHECK(!provider_); | |
114 | |
115 provider_.reset(new OfflinePageSuggestionsProvider( | |
116 recent_tabs_enabled, downloads_enabled, download_manager_ui_enabled, | |
117 &observer_, &category_factory_, &model_, pref_service())); | |
118 } | |
119 | |
120 Category recent_tabs_category() { | |
121 return category_factory_.FromKnownCategory(KnownCategories::RECENT_TABS); | |
122 } | |
123 | |
124 Category downloads_category() { | |
125 return category_factory_.FromKnownCategory(KnownCategories::DOWNLOADS); | |
126 } | |
127 | |
128 void AddItem(OfflinePageItem item) { model()->items()->push_back(item); } | |
129 | |
130 std::string GetDummySuggestionId(Category category, int id) { | |
131 return provider_->MakeUniqueID(category, base::IntToString(id)); | |
132 } | |
133 | |
134 ContentSuggestion CreateDummySuggestion(Category category, int id) { | |
135 std::string strid = base::IntToString(id); | |
136 ContentSuggestion result( | |
137 GetDummySuggestionId(category, id), | |
138 GURL("file:///some/folder/test" + strid + ".mhtml")); | |
139 result.set_title(base::UTF8ToUTF16("http://dummy.com/" + strid)); | |
140 return result; | |
141 } | |
142 | |
143 void FireOfflinePageModelChanged() { | |
144 provider_->OfflinePageModelChanged(model()); | |
145 } | |
146 | |
147 void FireOfflinePageDeleted(const OfflinePageItem& item) { | |
148 provider_->OfflinePageDeleted(item.offline_id, item.client_id); | |
149 } | |
150 | |
151 std::set<std::string> ReadDismissedIDsFromPrefs(Category category) { | |
152 return provider_->ReadDismissedIDsFromPrefs(category); | |
153 } | |
154 | |
155 void ReceiveDismissedSuggestions( | |
156 std::vector<ContentSuggestion> dismissed_suggestions) { | |
157 ReceivedDismissedSuggestions(dismissed_suggestions); | |
158 } | |
159 MOCK_METHOD1( | |
Marc Treib
2016/08/26 12:52:03
nit: Add a comment explaining the workaround?
It m
Philipp Keck
2016/08/26 17:19:55
Done.
Marc Treib
2016/08/29 09:28:48
No better ideas, no...
One suggestion Tim had in a
Philipp Keck
2016/08/29 11:32:42
I tried, but this std::list workaround is not poss
tschumann
2016/08/29 12:45:14
in this particular case, I wouldn't use the MOCK m
Marc Treib
2016/08/29 13:01:51
I disagree: I find a mock method and EXPECT_CALLs
Philipp Keck
2016/08/29 13:35:06
@Tim: Yes, currently, there are no other methods m
tschumann
2016/08/29 14:13:32
IMO, adding MOCK methods on a test is abusing the
| |
160 ReceivedDismissedSuggestions, | |
161 void(const std::vector<ContentSuggestion>& dismissed_suggestions)); | |
162 | |
163 ContentSuggestionsProvider* provider() { return provider_.get(); } | |
164 MockOfflinePageModel* model() { return &model_; } | |
165 MockContentSuggestionsProviderObserver* observer() { return &observer_; } | |
166 TestingPrefServiceSimple* pref_service() { return pref_service_.get(); } | |
167 | |
168 private: | |
169 MockOfflinePageModel model_; | |
170 MockContentSuggestionsProviderObserver observer_; | |
171 CategoryFactory category_factory_; | |
172 std::unique_ptr<TestingPrefServiceSimple> pref_service_; | |
173 std::unique_ptr<OfflinePageSuggestionsProvider> provider_; | |
174 | |
175 DISALLOW_COPY_AND_ASSIGN(OfflinePageSuggestionsProviderTest); | |
176 }; | |
177 | |
178 TEST_F(OfflinePageSuggestionsProviderTest, ShouldSplitAndConvertToSuggestions) { | |
179 AddItem(CreateDummyRecentTab(1)); | |
180 AddItem(CreateDummyRecentTab(2)); | |
181 AddItem(CreateDummyRecentTab(3)); | |
182 AddItem(CreateDummyDownload(101)); | |
183 | |
184 EXPECT_CALL( | |
185 *observer(), | |
186 OnNewSuggestions(_, recent_tabs_category(), | |
187 UnorderedElementsAre( | |
188 Property(&ContentSuggestion::url, | |
189 GURL("file:///some/folder/test1.mhtml")), | |
190 Property(&ContentSuggestion::url, | |
191 GURL("file:///some/folder/test2.mhtml")), | |
192 Property(&ContentSuggestion::url, | |
193 GURL("file:///some/folder/test3.mhtml"))))); | |
194 | |
195 EXPECT_CALL(*observer(), | |
196 OnNewSuggestions( | |
197 _, downloads_category(), | |
198 UnorderedElementsAre(AllOf( | |
199 Property(&ContentSuggestion::url, | |
200 GURL("file:///some/folder/test101.mhtml")), | |
201 Property(&ContentSuggestion::title, | |
202 base::UTF8ToUTF16("http://dummy.com/101")))))); | |
203 | |
204 FireOfflinePageModelChanged(); | |
205 } | |
206 | |
207 TEST_F(OfflinePageSuggestionsProviderTest, ShouldIgnoreDisabledCategories) { | |
208 AddItem(CreateDummyRecentTab(1)); | |
209 AddItem(CreateDummyRecentTab(2)); | |
210 AddItem(CreateDummyRecentTab(3)); | |
211 AddItem(CreateDummyDownload(101)); | |
212 | |
213 // Disable recent tabs, enable downloads. | |
214 EXPECT_CALL(*observer(), OnNewSuggestions(_, recent_tabs_category(), _)) | |
215 .Times(0); | |
216 EXPECT_CALL( | |
217 *observer(), | |
218 OnNewSuggestions(_, downloads_category(), | |
219 UnorderedElementsAre(Property( | |
220 &ContentSuggestion::url, | |
221 GURL("file:///some/folder/test101.mhtml"))))); | |
222 RecreateProvider(false, true, true); | |
223 Mock::VerifyAndClearExpectations(observer()); | |
224 | |
225 // Enable recent tabs, disable downloads. | |
226 EXPECT_CALL( | |
227 *observer(), | |
228 OnNewSuggestions(_, recent_tabs_category(), | |
229 UnorderedElementsAre( | |
230 Property(&ContentSuggestion::url, | |
231 GURL("file:///some/folder/test1.mhtml")), | |
232 Property(&ContentSuggestion::url, | |
233 GURL("file:///some/folder/test2.mhtml")), | |
234 Property(&ContentSuggestion::url, | |
235 GURL("file:///some/folder/test3.mhtml"))))); | |
236 EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), _)) | |
237 .Times(0); | |
238 RecreateProvider(true, false, true); | |
239 Mock::VerifyAndClearExpectations(observer()); | |
240 } | |
241 | |
242 TEST_F(OfflinePageSuggestionsProviderTest, ShouldSortByMostRecentlyVisited) { | |
243 base::Time now = base::Time::Now(); | |
244 base::Time yesterday = now - base::TimeDelta::FromDays(1); | |
245 base::Time tomorrow = now + base::TimeDelta::FromDays(1); | |
246 AddItem(CreateDummyRecentTab(1, now)); | |
247 AddItem(CreateDummyRecentTab(2, yesterday)); | |
248 AddItem(CreateDummyRecentTab(3, tomorrow)); | |
249 | |
250 EXPECT_CALL( | |
251 *observer(), | |
252 OnNewSuggestions( | |
253 _, recent_tabs_category(), | |
254 ElementsAre(Property(&ContentSuggestion::url, | |
255 GURL("file:///some/folder/test3.mhtml")), | |
256 Property(&ContentSuggestion::url, | |
257 GURL("file:///some/folder/test1.mhtml")), | |
258 Property(&ContentSuggestion::url, | |
259 GURL("file:///some/folder/test2.mhtml"))))); | |
260 EXPECT_CALL(*observer(), OnNewSuggestions(_, downloads_category(), _)); | |
261 FireOfflinePageModelChanged(); | |
262 } | |
263 | |
264 TEST_F(OfflinePageSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) { | |
265 EXPECT_FALSE( | |
266 provider()->GetCategoryInfo(recent_tabs_category()).has_more_button()); | |
267 EXPECT_TRUE( | |
268 provider()->GetCategoryInfo(downloads_category()).has_more_button()); | |
269 RecreateProvider(true, true, false); | |
270 EXPECT_FALSE( | |
271 provider()->GetCategoryInfo(recent_tabs_category()).has_more_button()); | |
272 EXPECT_FALSE( | |
273 provider()->GetCategoryInfo(downloads_category()).has_more_button()); | |
274 } | |
275 | |
276 TEST_F(OfflinePageSuggestionsProviderTest, ShouldDismiss) { | |
277 AddItem(CreateDummyRecentTab(1)); | |
278 AddItem(CreateDummyRecentTab(2)); | |
279 AddItem(CreateDummyRecentTab(3)); | |
280 AddItem(CreateDummyRecentTab(4)); | |
281 FireOfflinePageModelChanged(); | |
282 | |
283 // Dismiss 2 and 3. | |
284 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); | |
285 provider()->DismissSuggestion( | |
286 GetDummySuggestionId(recent_tabs_category(), 2)); | |
287 provider()->DismissSuggestion( | |
288 GetDummySuggestionId(recent_tabs_category(), 3)); | |
289 Mock::VerifyAndClearExpectations(observer()); | |
290 | |
291 // They should disappear from the reported suggestions. | |
292 EXPECT_CALL( | |
293 *observer(), | |
294 OnNewSuggestions(_, recent_tabs_category(), | |
295 UnorderedElementsAre( | |
296 Property(&ContentSuggestion::url, | |
297 GURL("file:///some/folder/test1.mhtml")), | |
298 Property(&ContentSuggestion::url, | |
299 GURL("file:///some/folder/test4.mhtml"))))); | |
300 EXPECT_CALL(*observer(), | |
301 OnNewSuggestions(_, downloads_category(), IsEmpty())); | |
302 FireOfflinePageModelChanged(); | |
303 Mock::VerifyAndClearExpectations(observer()); | |
304 | |
305 // And appear in the dismissed suggestions for the right category. | |
306 EXPECT_CALL(*this, ReceivedDismissedSuggestions(UnorderedElementsAre( | |
307 Property(&ContentSuggestion::url, | |
308 GURL("file:///some/folder/test2.mhtml")), | |
309 Property(&ContentSuggestion::url, | |
310 GURL("file:///some/folder/test3.mhtml"))))); | |
311 provider()->GetDismissedSuggestionsForDebugging( | |
312 recent_tabs_category(), | |
313 base::Bind( | |
314 &OfflinePageSuggestionsProviderTest::ReceiveDismissedSuggestions, | |
315 base::Unretained(this))); | |
316 Mock::VerifyAndClearExpectations(this); | |
317 | |
318 // The other category should have no dismissed suggestions. | |
319 EXPECT_CALL(*this, ReceivedDismissedSuggestions(IsEmpty())); | |
320 provider()->GetDismissedSuggestionsForDebugging( | |
321 downloads_category(), | |
322 base::Bind( | |
323 &OfflinePageSuggestionsProviderTest::ReceiveDismissedSuggestions, | |
324 base::Unretained(this))); | |
325 Mock::VerifyAndClearExpectations(this); | |
326 | |
327 // Clear dismissed suggestions. | |
328 provider()->ClearDismissedSuggestionsForDebugging(recent_tabs_category()); | |
329 | |
330 // They should be gone from the dismissed suggestions. | |
331 EXPECT_CALL(*this, ReceivedDismissedSuggestions(IsEmpty())); | |
332 provider()->GetDismissedSuggestionsForDebugging( | |
333 recent_tabs_category(), | |
334 base::Bind( | |
335 &OfflinePageSuggestionsProviderTest::ReceiveDismissedSuggestions, | |
336 base::Unretained(this))); | |
337 Mock::VerifyAndClearExpectations(this); | |
338 | |
339 // And appear in the reported suggestions for the category again. | |
340 EXPECT_CALL(*observer(), | |
341 OnNewSuggestions(_, recent_tabs_category(), SizeIs(4))); | |
342 EXPECT_CALL(*observer(), | |
343 OnNewSuggestions(_, downloads_category(), IsEmpty())); | |
344 FireOfflinePageModelChanged(); | |
345 Mock::VerifyAndClearExpectations(observer()); | |
346 } | |
347 | |
348 TEST_F(OfflinePageSuggestionsProviderTest, | |
349 ShouldInvalidateWhenOfflinePageDeleted) { | |
350 AddItem(CreateDummyRecentTab(1)); | |
351 AddItem(CreateDummyRecentTab(2)); | |
352 AddItem(CreateDummyRecentTab(3)); | |
353 FireOfflinePageModelChanged(); | |
354 | |
355 // Invalidation of suggestion 2 should be forwarded. | |
356 EXPECT_CALL( | |
357 *observer(), | |
358 OnSuggestionInvalidated(_, recent_tabs_category(), | |
359 GetDummySuggestionId(recent_tabs_category(), 2))); | |
360 FireOfflinePageDeleted(model()->items()->at(1)); | |
361 } | |
362 | |
363 TEST_F(OfflinePageSuggestionsProviderTest, ShouldClearDismissedOnInvalidate) { | |
364 AddItem(CreateDummyRecentTab(1)); | |
365 AddItem(CreateDummyRecentTab(2)); | |
366 AddItem(CreateDummyRecentTab(3)); | |
367 FireOfflinePageModelChanged(); | |
368 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty()); | |
369 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty()); | |
370 | |
371 provider()->DismissSuggestion( | |
372 GetDummySuggestionId(recent_tabs_category(), 2)); | |
373 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), SizeIs(1)); | |
374 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty()); | |
375 | |
376 FireOfflinePageDeleted(model()->items()->at(1)); | |
377 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty()); | |
378 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty()); | |
379 } | |
380 | |
381 TEST_F(OfflinePageSuggestionsProviderTest, ShouldClearDismissedOnFetch) { | |
382 AddItem(CreateDummyDownload(1)); | |
383 AddItem(CreateDummyDownload(2)); | |
384 AddItem(CreateDummyDownload(3)); | |
385 FireOfflinePageModelChanged(); | |
386 | |
387 provider()->DismissSuggestion(GetDummySuggestionId(downloads_category(), 2)); | |
388 provider()->DismissSuggestion(GetDummySuggestionId(downloads_category(), 3)); | |
389 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty()); | |
390 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), SizeIs(2)); | |
391 | |
392 model()->items()->clear(); | |
393 AddItem(CreateDummyDownload(2)); | |
394 FireOfflinePageModelChanged(); | |
395 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty()); | |
396 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), SizeIs(1)); | |
397 | |
398 model()->items()->clear(); | |
399 FireOfflinePageModelChanged(); | |
400 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty()); | |
401 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty()); | |
402 } | |
403 | |
404 } // namespace ntp_snippets | |
OLD | NEW |