| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.
h" | 5 #include "components/ntp_snippets/offline_pages/recent_tab_suggestions_provider.
h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 } | 73 } |
| 74 | 74 |
| 75 Category recent_tabs_category() { | 75 Category recent_tabs_category() { |
| 76 return category_factory_.FromKnownCategory(KnownCategories::RECENT_TABS); | 76 return category_factory_.FromKnownCategory(KnownCategories::RECENT_TABS); |
| 77 } | 77 } |
| 78 | 78 |
| 79 ContentSuggestion::ID GetDummySuggestionId(int id) { | 79 ContentSuggestion::ID GetDummySuggestionId(int id) { |
| 80 return ContentSuggestion::ID(recent_tabs_category(), base::IntToString(id)); | 80 return ContentSuggestion::ID(recent_tabs_category(), base::IntToString(id)); |
| 81 } | 81 } |
| 82 | 82 |
| 83 void FireOfflinePageModelChanged(const std::vector<OfflinePageItem>& items) { | 83 void AddOfflinePageToModel(const OfflinePageItem& item) { |
| 84 *(model_.mutable_items()) = items; | 84 model_.mutable_items()->push_back(item); |
| 85 provider_->OfflinePageModelChanged(&model_); | 85 provider_->OfflinePageAdded(&model_, item); |
| 86 } | 86 } |
| 87 | 87 |
| 88 void FireOfflinePageDeleted(const OfflinePageItem& item) { | 88 void FireOfflinePageDeleted(const OfflinePageItem& item) { |
| 89 auto iter = std::remove(model_.mutable_items()->begin(), |
| 90 model_.mutable_items()->end(), item); |
| 91 auto end = model_.mutable_items()->end(); |
| 92 model_.mutable_items()->erase(iter, end); |
| 93 |
| 89 provider_->OfflinePageDeleted(item.offline_id, item.client_id); | 94 provider_->OfflinePageDeleted(item.offline_id, item.client_id); |
| 90 } | 95 } |
| 91 | 96 |
| 92 std::set<std::string> ReadDismissedIDsFromPrefs() { | 97 std::set<std::string> ReadDismissedIDsFromPrefs() { |
| 93 return provider_->ReadDismissedIDsFromPrefs(); | 98 return provider_->ReadDismissedIDsFromPrefs(); |
| 94 } | 99 } |
| 95 | 100 |
| 96 RecentTabSuggestionsProvider* provider() { return provider_.get(); } | 101 RecentTabSuggestionsProvider* provider() { return provider_.get(); } |
| 97 FakeOfflinePageModel* model() { return &model_; } | 102 FakeOfflinePageModel* model() { return &model_; } |
| 98 MockContentSuggestionsProviderObserver* observer() { return &observer_; } | 103 MockContentSuggestionsProviderObserver* observer() { return &observer_; } |
| 99 TestingPrefServiceSimple* pref_service() { return pref_service_.get(); } | 104 TestingPrefServiceSimple* pref_service() { return pref_service_.get(); } |
| 100 | 105 |
| 101 private: | 106 private: |
| 102 FakeOfflinePageModel model_; | 107 FakeOfflinePageModel model_; |
| 103 MockContentSuggestionsProviderObserver observer_; | 108 MockContentSuggestionsProviderObserver observer_; |
| 104 CategoryFactory category_factory_; | 109 CategoryFactory category_factory_; |
| 105 std::unique_ptr<TestingPrefServiceSimple> pref_service_; | 110 std::unique_ptr<TestingPrefServiceSimple> pref_service_; |
| 106 // Last so that the dependencies are deleted after the provider. | 111 // Last so that the dependencies are deleted after the provider. |
| 107 std::unique_ptr<RecentTabSuggestionsProvider> provider_; | 112 std::unique_ptr<RecentTabSuggestionsProvider> provider_; |
| 108 | 113 |
| 109 DISALLOW_COPY_AND_ASSIGN(RecentTabSuggestionsProviderTest); | 114 DISALLOW_COPY_AND_ASSIGN(RecentTabSuggestionsProviderTest); |
| 110 }; | 115 }; |
| 111 | 116 |
| 112 TEST_F(RecentTabSuggestionsProviderTest, ShouldConvertToSuggestions) { | 117 TEST_F(RecentTabSuggestionsProviderTest, ShouldConvertToSuggestions) { |
| 118 auto recent_tabs_list = CreateDummyRecentTabs({1, 2}); |
| 119 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(2); |
| 120 for (OfflinePageItem& recent_tab : recent_tabs_list) |
| 121 AddOfflinePageToModel(recent_tab); |
| 122 |
| 113 EXPECT_CALL( | 123 EXPECT_CALL( |
| 114 *observer(), | 124 *observer(), |
| 115 OnNewSuggestions(_, recent_tabs_category(), | 125 OnNewSuggestions(_, recent_tabs_category(), |
| 116 UnorderedElementsAre( | 126 UnorderedElementsAre( |
| 117 Property(&ContentSuggestion::url, | 127 Property(&ContentSuggestion::url, |
| 118 GURL("http://dummy.com/1")), | 128 GURL("http://dummy.com/1")), |
| 119 Property(&ContentSuggestion::url, | 129 Property(&ContentSuggestion::url, |
| 120 GURL("http://dummy.com/2")), | 130 GURL("http://dummy.com/2")), |
| 121 Property(&ContentSuggestion::url, | 131 Property(&ContentSuggestion::url, |
| 122 GURL("http://dummy.com/3"))))); | 132 GURL("http://dummy.com/3"))))); |
| 123 FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3})); | 133 AddOfflinePageToModel(CreateDummyRecentTab(3)); |
| 124 } | 134 } |
| 125 | 135 |
| 126 TEST_F(RecentTabSuggestionsProviderTest, ShouldSortByCreationTime) { | 136 TEST_F(RecentTabSuggestionsProviderTest, ShouldSortByCreationTime) { |
| 127 base::Time now = base::Time::Now(); | 137 base::Time now = base::Time::Now(); |
| 128 base::Time yesterday = now - base::TimeDelta::FromDays(1); | 138 base::Time yesterday = now - base::TimeDelta::FromDays(1); |
| 129 base::Time tomorrow = now + base::TimeDelta::FromDays(1); | 139 base::Time tomorrow = now + base::TimeDelta::FromDays(1); |
| 140 |
| 130 std::vector<OfflinePageItem> offline_pages = { | 141 std::vector<OfflinePageItem> offline_pages = { |
| 131 CreateDummyRecentTab(1, now), CreateDummyRecentTab(2, yesterday), | 142 CreateDummyRecentTab(1, now), CreateDummyRecentTab(2, yesterday), |
| 132 CreateDummyRecentTab(3, tomorrow)}; | 143 CreateDummyRecentTab(3, tomorrow)}; |
| 133 | 144 |
| 145 EXPECT_CALL( |
| 146 *observer(), |
| 147 OnNewSuggestions(_, recent_tabs_category(), |
| 148 ElementsAre(Property(&ContentSuggestion::url, |
| 149 GURL("http://dummy.com/1"))))); |
| 150 AddOfflinePageToModel(CreateDummyRecentTab(1, now)); |
| 151 |
| 152 EXPECT_CALL( |
| 153 *observer(), |
| 154 OnNewSuggestions( |
| 155 _, recent_tabs_category(), |
| 156 ElementsAre( |
| 157 Property(&ContentSuggestion::url, GURL("http://dummy.com/1")), |
| 158 Property(&ContentSuggestion::url, GURL("http://dummy.com/2"))))); |
| 159 AddOfflinePageToModel(CreateDummyRecentTab(2, yesterday)); |
| 160 |
| 134 offline_pages[1].last_access_time = | 161 offline_pages[1].last_access_time = |
| 135 offline_pages[0].last_access_time + base::TimeDelta::FromHours(1); | 162 offline_pages[0].last_access_time + base::TimeDelta::FromHours(1); |
| 136 | 163 |
| 137 EXPECT_CALL( | 164 EXPECT_CALL( |
| 138 *observer(), | 165 *observer(), |
| 139 OnNewSuggestions( | 166 OnNewSuggestions( |
| 140 _, recent_tabs_category(), | 167 _, recent_tabs_category(), |
| 141 ElementsAre(Property(&ContentSuggestion::url, | 168 ElementsAre(Property(&ContentSuggestion::url, |
| 142 GURL("http://dummy.com/3")), | 169 GURL("http://dummy.com/3")), |
| 143 Property(&ContentSuggestion::url, | 170 Property(&ContentSuggestion::url, |
| 144 GURL("http://dummy.com/1")), | 171 GURL("http://dummy.com/1")), |
| 145 Property(&ContentSuggestion::url, | 172 Property(&ContentSuggestion::url, |
| 146 GURL("http://dummy.com/2"))))); | 173 GURL("http://dummy.com/2"))))); |
| 147 FireOfflinePageModelChanged(offline_pages); | 174 AddOfflinePageToModel(CreateDummyRecentTab(3, tomorrow)); |
| 148 } | 175 } |
| 149 | 176 |
| 150 TEST_F(RecentTabSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) { | 177 TEST_F(RecentTabSuggestionsProviderTest, ShouldDeliverCorrectCategoryInfo) { |
| 151 EXPECT_FALSE( | 178 EXPECT_FALSE( |
| 152 provider()->GetCategoryInfo(recent_tabs_category()).has_more_action()); | 179 provider()->GetCategoryInfo(recent_tabs_category()).has_more_action()); |
| 153 EXPECT_FALSE( | 180 EXPECT_FALSE( |
| 154 provider()->GetCategoryInfo(recent_tabs_category()).has_reload_action()); | 181 provider()->GetCategoryInfo(recent_tabs_category()).has_reload_action()); |
| 155 EXPECT_FALSE(provider() | 182 EXPECT_FALSE(provider() |
| 156 ->GetCategoryInfo(recent_tabs_category()) | 183 ->GetCategoryInfo(recent_tabs_category()) |
| 157 .has_view_all_action()); | 184 .has_view_all_action()); |
| 158 } | 185 } |
| 159 | 186 |
| 160 TEST_F(RecentTabSuggestionsProviderTest, ShouldDismiss) { | 187 TEST_F(RecentTabSuggestionsProviderTest, ShouldDismiss) { |
| 161 FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3, 4})); | 188 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(4); |
| 189 auto recent_tabs_list = CreateDummyRecentTabs({1, 2, 3, 4}); |
| 190 for (OfflinePageItem& recent_tab : recent_tabs_list) |
| 191 AddOfflinePageToModel(recent_tab); |
| 162 | 192 |
| 163 // Dismiss 2 and 3. | 193 // Dismiss 2 and 3. |
| 164 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); | 194 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(0); |
| 165 provider()->DismissSuggestion(GetDummySuggestionId(2)); | 195 provider()->DismissSuggestion(GetDummySuggestionId(2)); |
| 166 provider()->DismissSuggestion(GetDummySuggestionId(3)); | 196 provider()->DismissSuggestion(GetDummySuggestionId(3)); |
| 167 Mock::VerifyAndClearExpectations(observer()); | 197 Mock::VerifyAndClearExpectations(observer()); |
| 168 | 198 |
| 169 // They should disappear from the reported suggestions. | 199 // They should disappear from the reported suggestions. |
| 170 EXPECT_CALL( | 200 EXPECT_CALL( |
| 171 *observer(), | 201 *observer(), |
| 172 OnNewSuggestions(_, recent_tabs_category(), | 202 OnNewSuggestions(_, recent_tabs_category(), |
| 173 UnorderedElementsAre( | 203 UnorderedElementsAre( |
| 174 Property(&ContentSuggestion::url, | 204 Property(&ContentSuggestion::url, |
| 175 GURL("http://dummy.com/1")), | 205 GURL("http://dummy.com/1")), |
| 176 Property(&ContentSuggestion::url, | 206 Property(&ContentSuggestion::url, |
| 177 GURL("http://dummy.com/4"))))); | 207 GURL("http://dummy.com/4"))))); |
| 178 | 208 |
| 179 FireOfflinePageModelChanged(model()->items()); | 209 AddOfflinePageToModel(ntp_snippets::test::CreateDummyOfflinePageItem( |
| 210 4, offline_pages::kDefaultNamespace)); |
| 180 Mock::VerifyAndClearExpectations(observer()); | 211 Mock::VerifyAndClearExpectations(observer()); |
| 181 | 212 |
| 182 // And appear in the dismissed suggestions. | 213 // And appear in the dismissed suggestions. |
| 183 std::vector<ContentSuggestion> dismissed_suggestions; | 214 std::vector<ContentSuggestion> dismissed_suggestions; |
| 184 provider()->GetDismissedSuggestionsForDebugging( | 215 provider()->GetDismissedSuggestionsForDebugging( |
| 185 recent_tabs_category(), | 216 recent_tabs_category(), |
| 186 base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions)); | 217 base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions)); |
| 187 EXPECT_THAT( | 218 EXPECT_THAT( |
| 188 dismissed_suggestions, | 219 dismissed_suggestions, |
| 189 UnorderedElementsAre(Property(&ContentSuggestion::url, | 220 UnorderedElementsAre(Property(&ContentSuggestion::url, |
| 190 GURL("http://dummy.com/2")), | 221 GURL("http://dummy.com/2")), |
| 191 Property(&ContentSuggestion::url, | 222 Property(&ContentSuggestion::url, |
| 192 GURL("http://dummy.com/3")))); | 223 GURL("http://dummy.com/3")))); |
| 193 | 224 |
| 194 // Clear dismissed suggestions. | 225 // Clear dismissed suggestions. |
| 195 provider()->ClearDismissedSuggestionsForDebugging(recent_tabs_category()); | 226 provider()->ClearDismissedSuggestionsForDebugging(recent_tabs_category()); |
| 196 | 227 |
| 197 // They should be gone from the dismissed suggestions. | 228 // They should be gone from the dismissed suggestions. |
| 198 dismissed_suggestions.clear(); | 229 dismissed_suggestions.clear(); |
| 199 provider()->GetDismissedSuggestionsForDebugging( | 230 provider()->GetDismissedSuggestionsForDebugging( |
| 200 recent_tabs_category(), | 231 recent_tabs_category(), |
| 201 base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions)); | 232 base::Bind(&CaptureDismissedSuggestions, &dismissed_suggestions)); |
| 202 EXPECT_THAT(dismissed_suggestions, IsEmpty()); | 233 EXPECT_THAT(dismissed_suggestions, IsEmpty()); |
| 203 | 234 |
| 204 // And appear in the reported suggestions for the category again. | 235 // And appear in the reported suggestions for the category again. |
| 205 EXPECT_CALL(*observer(), | 236 EXPECT_CALL(*observer(), |
| 206 OnNewSuggestions(_, recent_tabs_category(), SizeIs(4))); | 237 OnNewSuggestions(_, recent_tabs_category(), SizeIs(4))); |
| 207 FireOfflinePageModelChanged(model()->items()); | 238 AddOfflinePageToModel(ntp_snippets::test::CreateDummyOfflinePageItem( |
| 239 5, offline_pages::kDefaultNamespace)); |
| 208 Mock::VerifyAndClearExpectations(observer()); | 240 Mock::VerifyAndClearExpectations(observer()); |
| 209 } | 241 } |
| 210 | 242 |
| 211 TEST_F(RecentTabSuggestionsProviderTest, | 243 TEST_F(RecentTabSuggestionsProviderTest, |
| 212 ShouldInvalidateWhenOfflinePageDeleted) { | 244 ShouldInvalidateWhenOfflinePageDeleted) { |
| 245 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3); |
| 213 std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); | 246 std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); |
| 214 FireOfflinePageModelChanged(offline_pages); | 247 for (OfflinePageItem& recent_tab : offline_pages) |
| 248 AddOfflinePageToModel(recent_tab); |
| 215 | 249 |
| 216 // Invalidation of suggestion 2 should be forwarded. | 250 // Invalidation of suggestion 2 should be forwarded. |
| 217 EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(2))); | 251 EXPECT_CALL(*observer(), OnSuggestionInvalidated(_, GetDummySuggestionId(2))); |
| 218 FireOfflinePageDeleted(offline_pages[1]); | 252 FireOfflinePageDeleted(offline_pages[1]); |
| 219 } | 253 } |
| 220 | 254 |
| 221 TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnInvalidate) { | 255 TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnInvalidate) { |
| 256 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3); |
| 222 std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); | 257 std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); |
| 223 FireOfflinePageModelChanged(offline_pages); | 258 for (OfflinePageItem& recent_tab : offline_pages) |
| 259 AddOfflinePageToModel(recent_tab); |
| 224 EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty()); | 260 EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty()); |
| 225 | 261 |
| 226 provider()->DismissSuggestion(GetDummySuggestionId(2)); | 262 provider()->DismissSuggestion(GetDummySuggestionId(2)); |
| 227 EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(1)); | 263 EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(1)); |
| 228 | 264 |
| 229 FireOfflinePageDeleted(offline_pages[1]); | 265 FireOfflinePageDeleted(offline_pages[1]); |
| 230 EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty()); | 266 EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty()); |
| 231 } | 267 } |
| 232 | 268 |
| 233 TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnFetch) { | 269 TEST_F(RecentTabSuggestionsProviderTest, ShouldClearDismissedOnFetch) { |
| 234 FireOfflinePageModelChanged(CreateDummyRecentTabs({1, 2, 3})); | 270 EXPECT_CALL(*observer(), OnNewSuggestions(_, _, _)).Times(3); |
| 271 std::vector<OfflinePageItem> offline_pages = CreateDummyRecentTabs({1, 2, 3}); |
| 272 for (OfflinePageItem& recent_tab : offline_pages) |
| 273 AddOfflinePageToModel(recent_tab); |
| 235 | 274 |
| 236 provider()->DismissSuggestion(GetDummySuggestionId(2)); | 275 provider()->DismissSuggestion(GetDummySuggestionId(2)); |
| 237 provider()->DismissSuggestion(GetDummySuggestionId(3)); | 276 provider()->DismissSuggestion(GetDummySuggestionId(3)); |
| 238 EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(2)); | 277 EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(2)); |
| 239 | 278 |
| 240 FireOfflinePageModelChanged(CreateDummyRecentTabs({2})); | 279 FireOfflinePageDeleted(offline_pages[0]); |
| 280 FireOfflinePageDeleted(offline_pages[2]); |
| 241 EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(1)); | 281 EXPECT_THAT(ReadDismissedIDsFromPrefs(), SizeIs(1)); |
| 242 | 282 |
| 243 FireOfflinePageModelChanged(std::vector<OfflinePageItem>()); | 283 FireOfflinePageDeleted(offline_pages[1]); |
| 244 EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty()); | 284 EXPECT_THAT(ReadDismissedIDsFromPrefs(), IsEmpty()); |
| 245 } | 285 } |
| 246 | 286 |
| 247 TEST_F(RecentTabSuggestionsProviderTest, ShouldNotShowSameUrlMutlipleTimes) { | 287 TEST_F(RecentTabSuggestionsProviderTest, ShouldNotShowSameUrlMutlipleTimes) { |
| 248 base::Time now = base::Time::Now(); | 288 base::Time now = base::Time::Now(); |
| 249 base::Time yesterday = now - base::TimeDelta::FromDays(1); | 289 base::Time yesterday = now - base::TimeDelta::FromDays(1); |
| 250 base::Time tomorrow = now + base::TimeDelta::FromDays(1); | 290 base::Time tomorrow = now + base::TimeDelta::FromDays(1); |
| 251 std::vector<OfflinePageItem> offline_pages = { | 291 std::vector<OfflinePageItem> offline_pages = { |
| 252 CreateDummyRecentTab(1, yesterday), CreateDummyRecentTab(2, now), | 292 CreateDummyRecentTab(1, yesterday), CreateDummyRecentTab(2, now), |
| 253 CreateDummyRecentTab(3, tomorrow)}; | 293 CreateDummyRecentTab(3, tomorrow)}; |
| 254 | 294 |
| 255 // We leave IDs different, but make the URLs the same. | 295 // We leave IDs different, but make the URLs the same. |
| 256 offline_pages[2].url = offline_pages[0].url; | 296 offline_pages[2].url = offline_pages[0].url; |
| 257 | 297 |
| 298 AddOfflinePageToModel(offline_pages[0]); |
| 299 AddOfflinePageToModel(offline_pages[1]); |
| 300 Mock::VerifyAndClearExpectations(observer()); |
| 258 EXPECT_CALL(*observer(), | 301 EXPECT_CALL(*observer(), |
| 259 OnNewSuggestions( | 302 OnNewSuggestions( |
| 260 _, recent_tabs_category(), | 303 _, recent_tabs_category(), |
| 261 UnorderedElementsAre( | 304 UnorderedElementsAre( |
| 262 Property(&ContentSuggestion::publish_date, now), | 305 Property(&ContentSuggestion::publish_date, now), |
| 263 Property(&ContentSuggestion::publish_date, tomorrow)))); | 306 Property(&ContentSuggestion::publish_date, tomorrow)))); |
| 264 FireOfflinePageModelChanged(offline_pages); | 307 |
| 308 AddOfflinePageToModel(offline_pages[2]); |
| 265 } | 309 } |
| 266 | 310 |
| 267 } // namespace ntp_snippets | 311 } // namespace ntp_snippets |
| OLD | NEW |