Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(151)

Side by Side Diff: components/ntp_snippets/offline_pages/offline_page_suggestions_provider_unittest.cc

Issue 2279223002: Add OfflinePageSuggestionsProviderTest (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 MockDismissedSuggestionsCallback
Marc Treib 2016/08/26 12:14:50 I find this pattern pretty weird - why not just ma
Philipp Keck 2016/08/26 12:41:45 Normally, a testing::MockFunction would do the job
94 : public ContentSuggestionsProvider::DismissedSuggestionsCallback {
95 public:
96 void Run(std::vector<ContentSuggestion> dismissed_suggestions) {
97 Call(dismissed_suggestions);
98 }
99 MOCK_METHOD1(
100 Call,
101 void(const std::vector<ContentSuggestion>& dismissed_suggestions));
102 };
103
104 class OfflinePageSuggestionsProviderTest : public testing::Test {
105 public:
106 OfflinePageSuggestionsProviderTest()
107 : pref_service_(new TestingPrefServiceSimple()) {
108 OfflinePageSuggestionsProvider::RegisterProfilePrefs(
109 pref_service()->registry());
110 }
111
112 ~OfflinePageSuggestionsProviderTest() override { provider_.reset(); }
Marc Treib 2016/08/26 12:14:50 I don't think this is necessary?
Philipp Keck 2016/08/26 12:41:45 Done.
113
114 void SetUp() override { CreateProvider(true, true, true); }
Marc Treib 2016/08/26 12:14:50 Can this just happen in the ctor instead?
Philipp Keck 2016/08/26 12:41:45 Done.
115
116 void RecreateProvider(bool recent_tabs_enabled,
117 bool downloads_enabled,
118 bool download_manager_ui_enabled) {
119 provider_.reset();
120 CreateProvider(recent_tabs_enabled, downloads_enabled,
121 download_manager_ui_enabled);
122 }
123
124 void CreateProvider(bool recent_tabs_enabled,
125 bool downloads_enabled,
126 bool download_manager_ui_enabled) {
127 DCHECK(!provider_);
128
129 provider_.reset(new OfflinePageSuggestionsProvider(
130 recent_tabs_enabled, downloads_enabled, download_manager_ui_enabled,
131 &observer_, &category_factory_, &model_, pref_service()));
132 }
133
134 Category recent_tabs_category() {
135 return category_factory_.FromKnownCategory(KnownCategories::RECENT_TABS);
136 }
137
138 Category downloads_category() {
139 return category_factory_.FromKnownCategory(KnownCategories::DOWNLOADS);
140 }
141
142 void AddItem(OfflinePageItem item) { model()->items()->push_back(item); }
143
144 std::string GetDummySuggestionId(Category category, int id) {
145 return provider_->MakeUniqueID(category, base::IntToString(id));
146 }
147
148 ContentSuggestion CreateDummySuggestion(Category category, int id) {
149 std::string strid = base::IntToString(id);
150 ContentSuggestion result(
151 GetDummySuggestionId(category, id),
152 GURL("file:///some/folder/test" + strid + ".mhtml"));
153 result.set_title(base::UTF8ToUTF16("http://dummy.com/" + strid));
154 return result;
155 }
156
157 void FireOfflinePageModelChanged() {
158 provider_->OfflinePageModelChanged(model());
159 }
160
161 void FireOfflinePageDeleted(const OfflinePageItem& item) {
162 provider_->OfflinePageDeleted(item.offline_id, item.client_id);
163 }
164
165 std::set<std::string> ReadDismissedIDsFromPrefs(Category category) {
166 return provider_->ReadDismissedIDsFromPrefs(category);
167 }
168
169 protected:
Marc Treib 2016/08/26 12:14:50 What's the difference between public and protected
Philipp Keck 2016/08/26 12:41:45 None. Moved all to public.
170 ContentSuggestionsProvider* provider() { return provider_.get(); }
171 MockOfflinePageModel* model() { return &model_; }
172 MockContentSuggestionsProviderObserver* observer() { return &observer_; }
173 TestingPrefServiceSimple* pref_service() { return pref_service_.get(); }
174
175 private:
176 MockOfflinePageModel model_;
177 MockContentSuggestionsProviderObserver observer_;
178 CategoryFactory category_factory_;
179 std::unique_ptr<TestingPrefServiceSimple> pref_service_;
180 // Last so that the dependencies are deleted after the provider.
Marc Treib 2016/08/26 12:14:50 I think this will actually not be the case if you
Philipp Keck 2016/08/26 12:41:45 Removed the comment.
Marc Treib 2016/08/26 12:52:03 Now that the dtor is gone, the comment might actua
Philipp Keck 2016/08/26 17:19:55 The comment applies now. But is it important? I'm
Marc Treib 2016/08/29 09:28:48 Yes, it is important: The provider calls model_->R
Philipp Keck 2016/08/29 11:32:42 Done.
181 std::unique_ptr<OfflinePageSuggestionsProvider> provider_;
182
183 DISALLOW_COPY_AND_ASSIGN(OfflinePageSuggestionsProviderTest);
184 };
185
186 TEST_F(OfflinePageSuggestionsProviderTest, ShouldSplitAndConvertToSuggestions) {
187 AddItem(CreateDummyRecentTab(1));
188 AddItem(CreateDummyRecentTab(2));
189 AddItem(CreateDummyRecentTab(3));
190 AddItem(CreateDummyDownload(101));
191
192 EXPECT_CALL(
193 *(observer()),
Marc Treib 2016/08/26 12:14:50 nit: parens not necessary
Philipp Keck 2016/08/26 12:41:45 Done.
194 OnNewSuggestions(_, recent_tabs_category(),
195 UnorderedElementsAre(
196 Property(&ContentSuggestion::url,
197 GURL("file:///some/folder/test1.mhtml")),
198 Property(&ContentSuggestion::url,
199 GURL("file:///some/folder/test2.mhtml")),
200 Property(&ContentSuggestion::url,
201 GURL("file:///some/folder/test3.mhtml")))));
202
203 EXPECT_CALL(*(observer()),
Marc Treib 2016/08/26 12:14:50 also here (and a bunch more cases below)
Philipp Keck 2016/08/26 12:41:45 Done.
204 OnNewSuggestions(
205 _, downloads_category(),
206 UnorderedElementsAre(AllOf(
207 Property(&ContentSuggestion::url,
208 GURL("file:///some/folder/test101.mhtml")),
209 Property(&ContentSuggestion::title,
210 base::UTF8ToUTF16("http://dummy.com/101"))))));
211
212 RecreateProvider(true, true, true);
Marc Treib 2016/08/26 12:14:50 Why do you need to recreate the provider here? Wou
Philipp Keck 2016/08/26 12:41:45 Done.
213 }
214
215 TEST_F(OfflinePageSuggestionsProviderTest, ShouldIgnoreDisabledCategories) {
216 AddItem(CreateDummyRecentTab(1));
217 AddItem(CreateDummyRecentTab(2));
218 AddItem(CreateDummyRecentTab(3));
219 AddItem(CreateDummyDownload(101));
220
221 // Disable recent tabs, enable downloads.
222 EXPECT_CALL(*(observer()), OnNewSuggestions(_, recent_tabs_category(), _))
223 .Times(0);
224 EXPECT_CALL(
225 *(observer()),
226 OnNewSuggestions(_, downloads_category(),
227 UnorderedElementsAre(Property(
228 &ContentSuggestion::url,
229 GURL("file:///some/folder/test101.mhtml")))));
230 RecreateProvider(false, true, true);
231 Mock::VerifyAndClearExpectations(observer());
232
233 // Enable recent tabs, dsiable downloads.
Marc Treib 2016/08/26 12:14:50 typo
Philipp Keck 2016/08/26 12:41:45 Done.
234 EXPECT_CALL(
235 *(observer()),
236 OnNewSuggestions(_, recent_tabs_category(),
237 UnorderedElementsAre(
238 Property(&ContentSuggestion::url,
239 GURL("file:///some/folder/test1.mhtml")),
240 Property(&ContentSuggestion::url,
241 GURL("file:///some/folder/test2.mhtml")),
242 Property(&ContentSuggestion::url,
243 GURL("file:///some/folder/test3.mhtml")))));
244 EXPECT_CALL(*(observer()), OnNewSuggestions(_, downloads_category(), _))
245 .Times(0);
246 RecreateProvider(true, false, true);
247 Mock::VerifyAndClearExpectations(observer());
248 }
249
250 TEST_F(OfflinePageSuggestionsProviderTest, ShouldSortByMostRecentlyVisited) {
251 base::Time now = base::Time::Now();
252 base::Time yesterday = now - base::TimeDelta::FromDays(1);
253 base::Time tomorrow = now + base::TimeDelta::FromDays(1);
254 AddItem(CreateDummyRecentTab(1, now));
255 AddItem(CreateDummyRecentTab(2, yesterday));
256 AddItem(CreateDummyRecentTab(3, tomorrow));
257
258 EXPECT_CALL(
259 *(observer()),
260 OnNewSuggestions(
261 _, recent_tabs_category(),
262 ElementsAre(Property(&ContentSuggestion::url,
263 GURL("file:///some/folder/test3.mhtml")),
264 Property(&ContentSuggestion::url,
265 GURL("file:///some/folder/test1.mhtml")),
266 Property(&ContentSuggestion::url,
267 GURL("file:///some/folder/test2.mhtml")))));
268 EXPECT_CALL(*(observer()), OnNewSuggestions(_, downloads_category(), _));
269 FireOfflinePageModelChanged();
270 }
271
272 TEST_F(OfflinePageSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) {
273 EXPECT_THAT(
Marc Treib 2016/08/26 12:14:50 EXPECT_FALSE?
Philipp Keck 2016/08/26 12:41:45 Done.
274 provider()->GetCategoryInfo(recent_tabs_category()).has_more_button(),
275 Eq(false));
276 EXPECT_THAT(
277 provider()->GetCategoryInfo(downloads_category()).has_more_button(),
278 Eq(true));
279 RecreateProvider(true, true, false);
280 EXPECT_THAT(
281 provider()->GetCategoryInfo(recent_tabs_category()).has_more_button(),
282 Eq(false));
283 EXPECT_THAT(
284 provider()->GetCategoryInfo(downloads_category()).has_more_button(),
285 Eq(false));
286 }
287
288 TEST_F(OfflinePageSuggestionsProviderTest, ShouldDismiss) {
289 AddItem(CreateDummyRecentTab(1));
290 AddItem(CreateDummyRecentTab(2));
291 AddItem(CreateDummyRecentTab(3));
292 AddItem(CreateDummyRecentTab(4));
293 FireOfflinePageModelChanged();
294
295 // Dismiss 2 and 3.
296 EXPECT_CALL(*(observer()), OnNewSuggestions(_, _, _)).Times(0);
297 provider()->DismissSuggestion(
298 GetDummySuggestionId(recent_tabs_category(), 2));
299 provider()->DismissSuggestion(
300 GetDummySuggestionId(recent_tabs_category(), 3));
301 Mock::VerifyAndClearExpectations(observer());
302
303 // They should disappear from the reported suggestions.
304 EXPECT_CALL(
305 *(observer()),
306 OnNewSuggestions(_, recent_tabs_category(),
307 UnorderedElementsAre(
308 Property(&ContentSuggestion::url,
309 GURL("file:///some/folder/test1.mhtml")),
310 Property(&ContentSuggestion::url,
311 GURL("file:///some/folder/test4.mhtml")))));
312 EXPECT_CALL(*(observer()),
313 OnNewSuggestions(_, downloads_category(), IsEmpty()));
314 FireOfflinePageModelChanged();
315 Mock::VerifyAndClearExpectations(observer());
316
317 // And appear in the dismissed suggestions for the right category.
318 MockDismissedSuggestionsCallback callback;
319 EXPECT_CALL(
320 callback,
321 Call(ElementsAre(Property(&ContentSuggestion::url,
Marc Treib 2016/08/26 12:14:50 Unordered?
Philipp Keck 2016/08/26 12:41:45 Done.
322 GURL("file:///some/folder/test2.mhtml")),
323 Property(&ContentSuggestion::url,
324 GURL("file:///some/folder/test3.mhtml")))));
325 provider()->GetDismissedSuggestionsForDebugging(
326 recent_tabs_category(), base::Bind(&MockDismissedSuggestionsCallback::Run,
327 base::Unretained(&callback)));
328 Mock::VerifyAndClearExpectations(&callback);
329
330 // The other category should have no dismissed suggestions.
331 EXPECT_CALL(callback, Call(IsEmpty()));
332 provider()->GetDismissedSuggestionsForDebugging(
333 downloads_category(), base::Bind(&MockDismissedSuggestionsCallback::Run,
334 base::Unretained(&callback)));
335 Mock::VerifyAndClearExpectations(&callback);
336
337 // Clear dismissed suggestions.
338 provider()->ClearDismissedSuggestionsForDebugging(recent_tabs_category());
339
340 // They should be gone from the dismissed suggestions.
341 EXPECT_CALL(callback, Call(IsEmpty()));
342 provider()->GetDismissedSuggestionsForDebugging(
343 recent_tabs_category(), base::Bind(&MockDismissedSuggestionsCallback::Run,
344 base::Unretained(&callback)));
345 Mock::VerifyAndClearExpectations(&callback);
346
347 // And appear in the reported suggestions for the category again.
348 EXPECT_CALL(*(observer()),
349 OnNewSuggestions(_, recent_tabs_category(), SizeIs(4)));
350 EXPECT_CALL(*(observer()),
351 OnNewSuggestions(_, downloads_category(), IsEmpty()));
352 FireOfflinePageModelChanged();
353 Mock::VerifyAndClearExpectations(observer());
354 }
355
356 TEST_F(OfflinePageSuggestionsProviderTest,
357 ShouldInvalidateWhenOfflinePageDeleted) {
358 AddItem(CreateDummyRecentTab(1));
359 AddItem(CreateDummyRecentTab(2));
360 AddItem(CreateDummyRecentTab(3));
361 FireOfflinePageModelChanged();
362
363 // Invalidation of suggestion 2 should be forwarded.
364 EXPECT_CALL(
365 *(observer()),
366 OnSuggestionInvalidated(_, recent_tabs_category(),
367 GetDummySuggestionId(recent_tabs_category(), 2)));
368 FireOfflinePageDeleted(model()->items()->at(1));
369 }
370
371 TEST_F(OfflinePageSuggestionsProviderTest, ShouldClearDismissedOnInvalidate) {
372 AddItem(CreateDummyRecentTab(1));
373 AddItem(CreateDummyRecentTab(2));
374 AddItem(CreateDummyRecentTab(3));
375 FireOfflinePageModelChanged();
376 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty());
377 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty());
378
379 provider()->DismissSuggestion(
380 GetDummySuggestionId(recent_tabs_category(), 2));
381 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), SizeIs(1));
382 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty());
383
384 FireOfflinePageDeleted(model()->items()->at(1));
385 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty());
386 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty());
387 }
388
389 TEST_F(OfflinePageSuggestionsProviderTest, ShouldClearDismissedOnFetch) {
390 AddItem(CreateDummyDownload(1));
391 AddItem(CreateDummyDownload(2));
392 AddItem(CreateDummyDownload(3));
393 FireOfflinePageModelChanged();
394
395 provider()->DismissSuggestion(GetDummySuggestionId(downloads_category(), 2));
396 provider()->DismissSuggestion(GetDummySuggestionId(downloads_category(), 3));
397 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty());
398 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), SizeIs(2));
399
400 model()->items()->clear();
401 AddItem(CreateDummyDownload(2));
402 FireOfflinePageModelChanged();
403 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty());
404 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), SizeIs(1));
405
406 model()->items()->clear();
407 FireOfflinePageModelChanged();
408 EXPECT_THAT(ReadDismissedIDsFromPrefs(recent_tabs_category()), IsEmpty());
409 EXPECT_THAT(ReadDismissedIDsFromPrefs(downloads_category()), IsEmpty());
410 }
411
412 } // namespace ntp_snippets
OLDNEW
« no previous file with comments | « components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698