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/content_suggestions_service.h" | 5 #include "components/ntp_snippets/content_suggestions_service.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 #include <utility> | 8 #include <utility> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/macros.h" | 12 #include "base/macros.h" |
13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
14 #include "base/message_loop/message_loop.h" | 14 #include "base/message_loop/message_loop.h" |
15 #include "base/run_loop.h" | 15 #include "base/run_loop.h" |
16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
17 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
18 #include "components/ntp_snippets/category_info.h" | 18 #include "components/ntp_snippets/category_info.h" |
19 #include "components/ntp_snippets/category_rankers/constant_category_ranker.h" | 19 #include "components/ntp_snippets/category_rankers/constant_category_ranker.h" |
| 20 #include "components/ntp_snippets/category_rankers/fake_category_ranker.h" |
20 #include "components/ntp_snippets/category_status.h" | 21 #include "components/ntp_snippets/category_status.h" |
21 #include "components/ntp_snippets/content_suggestion.h" | 22 #include "components/ntp_snippets/content_suggestion.h" |
22 #include "components/ntp_snippets/content_suggestions_provider.h" | 23 #include "components/ntp_snippets/content_suggestions_provider.h" |
23 #include "components/ntp_snippets/user_classifier.h" | 24 #include "components/ntp_snippets/user_classifier.h" |
24 #include "components/prefs/testing_pref_service.h" | 25 #include "components/prefs/testing_pref_service.h" |
25 #include "testing/gmock/include/gmock/gmock.h" | 26 #include "testing/gmock/include/gmock/gmock.h" |
26 #include "testing/gtest/include/gtest/gtest.h" | 27 #include "testing/gtest/include/gtest/gtest.h" |
27 #include "ui/gfx/image/image.h" | 28 #include "ui/gfx/image/image.h" |
28 | 29 |
| 30 using testing::_; |
29 using testing::ElementsAre; | 31 using testing::ElementsAre; |
30 using testing::Eq; | 32 using testing::Eq; |
31 using testing::InvokeWithoutArgs; | 33 using testing::InvokeWithoutArgs; |
32 using testing::IsEmpty; | 34 using testing::IsEmpty; |
33 using testing::Mock; | 35 using testing::Mock; |
34 using testing::Property; | 36 using testing::Property; |
35 using testing::_; | 37 using testing::Return; |
| 38 using testing::UnorderedElementsAre; |
36 | 39 |
37 namespace ntp_snippets { | 40 namespace ntp_snippets { |
38 | 41 |
39 namespace { | 42 namespace { |
40 | 43 |
| 44 // TODO(treib): This is a weird combination of a mock and a fake. Fix this. |
41 class MockProvider : public ContentSuggestionsProvider { | 45 class MockProvider : public ContentSuggestionsProvider { |
42 public: | 46 public: |
43 MockProvider(Observer* observer, | 47 MockProvider(Observer* observer, |
44 const std::vector<Category>& provided_categories) | 48 const std::vector<Category>& provided_categories) |
45 : ContentSuggestionsProvider(observer) { | 49 : ContentSuggestionsProvider(observer) { |
46 SetProvidedCategories(provided_categories); | 50 SetProvidedCategories(provided_categories); |
47 } | 51 } |
48 | 52 |
49 void SetProvidedCategories(const std::vector<Category>& provided_categories) { | 53 void SetProvidedCategories(const std::vector<Category>& provided_categories) { |
50 statuses_.clear(); | 54 statuses_.clear(); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 | 128 |
125 private: | 129 private: |
126 DISALLOW_COPY_AND_ASSIGN(MockServiceObserver); | 130 DISALLOW_COPY_AND_ASSIGN(MockServiceObserver); |
127 }; | 131 }; |
128 | 132 |
129 } // namespace | 133 } // namespace |
130 | 134 |
131 class ContentSuggestionsServiceTest : public testing::Test { | 135 class ContentSuggestionsServiceTest : public testing::Test { |
132 public: | 136 public: |
133 ContentSuggestionsServiceTest() | 137 ContentSuggestionsServiceTest() |
134 : pref_service_(new TestingPrefServiceSimple()) {} | 138 : pref_service_(base::MakeUnique<TestingPrefServiceSimple>()), |
| 139 category_ranker_(base::MakeUnique<ConstantCategoryRanker>()) {} |
135 | 140 |
136 void SetUp() override { | 141 void SetUp() override { |
137 RegisterPrefs(); | 142 RegisterPrefs(); |
138 CreateContentSuggestionsService(ContentSuggestionsService::State::ENABLED); | 143 CreateContentSuggestionsService(ContentSuggestionsService::State::ENABLED); |
139 } | 144 } |
140 | 145 |
141 void TearDown() override { | 146 void TearDown() override { |
142 service_->Shutdown(); | 147 service_->Shutdown(); |
143 service_.reset(); | 148 service_.reset(); |
144 } | 149 } |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
186 | 191 |
187 MockProvider* RegisterProvider( | 192 MockProvider* RegisterProvider( |
188 const std::vector<Category>& provided_categories) { | 193 const std::vector<Category>& provided_categories) { |
189 std::unique_ptr<MockProvider> provider = | 194 std::unique_ptr<MockProvider> provider = |
190 base::MakeUnique<MockProvider>(service(), provided_categories); | 195 base::MakeUnique<MockProvider>(service(), provided_categories); |
191 MockProvider* result = provider.get(); | 196 MockProvider* result = provider.get(); |
192 service()->RegisterProvider(std::move(provider)); | 197 service()->RegisterProvider(std::move(provider)); |
193 return result; | 198 return result; |
194 } | 199 } |
195 | 200 |
| 201 void SetCategoryRanker(std::unique_ptr<CategoryRanker> category_ranker) { |
| 202 category_ranker_ = std::move(category_ranker); |
| 203 } |
| 204 |
196 MOCK_METHOD1(OnImageFetched, void(const gfx::Image&)); | 205 MOCK_METHOD1(OnImageFetched, void(const gfx::Image&)); |
197 | 206 |
198 protected: | 207 protected: |
199 void RegisterPrefs() { | 208 void RegisterPrefs() { |
200 ContentSuggestionsService::RegisterProfilePrefs(pref_service_->registry()); | 209 ContentSuggestionsService::RegisterProfilePrefs(pref_service_->registry()); |
201 UserClassifier::RegisterProfilePrefs(pref_service_->registry()); | 210 UserClassifier::RegisterProfilePrefs(pref_service_->registry()); |
202 } | 211 } |
203 | 212 |
204 void CreateContentSuggestionsService( | 213 void CreateContentSuggestionsService( |
205 ContentSuggestionsService::State enabled) { | 214 ContentSuggestionsService::State enabled) { |
206 ASSERT_FALSE(service_); | 215 ASSERT_FALSE(service_); |
207 service_.reset(new ContentSuggestionsService( | 216 service_ = base::MakeUnique<ContentSuggestionsService>( |
208 enabled, /*signin_manager=*/nullptr, /*history_service=*/nullptr, | 217 enabled, /*signin_manager=*/nullptr, /*history_service=*/nullptr, |
209 pref_service_.get(), | 218 pref_service_.get(), std::move(category_ranker_)); |
210 base::MakeUnique<ntp_snippets::ConstantCategoryRanker>())); | |
211 } | 219 } |
212 | 220 |
213 void ResetService() { | 221 void ResetService() { |
214 service_->Shutdown(); | 222 service_->Shutdown(); |
215 service_.reset(); | 223 service_.reset(); |
216 CreateContentSuggestionsService(ContentSuggestionsService::State::ENABLED); | 224 CreateContentSuggestionsService(ContentSuggestionsService::State::ENABLED); |
217 } | 225 } |
218 | 226 |
219 ContentSuggestionsService* service() { return service_.get(); } | 227 ContentSuggestionsService* service() { return service_.get(); } |
220 | 228 |
(...skipping 10 matching lines...) Expand all Loading... |
231 std::vector<ContentSuggestion> result; | 239 std::vector<ContentSuggestion> result; |
232 for (int number : numbers) { | 240 for (int number : numbers) { |
233 result.push_back(CreateSuggestion(category, number)); | 241 result.push_back(CreateSuggestion(category, number)); |
234 } | 242 } |
235 return result; | 243 return result; |
236 } | 244 } |
237 | 245 |
238 private: | 246 private: |
239 std::unique_ptr<ContentSuggestionsService> service_; | 247 std::unique_ptr<ContentSuggestionsService> service_; |
240 std::unique_ptr<TestingPrefServiceSimple> pref_service_; | 248 std::unique_ptr<TestingPrefServiceSimple> pref_service_; |
| 249 std::unique_ptr<CategoryRanker> category_ranker_; |
241 | 250 |
242 DISALLOW_COPY_AND_ASSIGN(ContentSuggestionsServiceTest); | 251 DISALLOW_COPY_AND_ASSIGN(ContentSuggestionsServiceTest); |
243 }; | 252 }; |
244 | 253 |
245 class ContentSuggestionsServiceDisabledTest | 254 class ContentSuggestionsServiceDisabledTest |
246 : public ContentSuggestionsServiceTest { | 255 : public ContentSuggestionsServiceTest { |
247 public: | 256 public: |
248 void SetUp() override { | 257 void SetUp() override { |
249 RegisterPrefs(); | 258 RegisterPrefs(); |
250 CreateContentSuggestionsService(ContentSuggestionsService::State::DISABLED); | 259 CreateContentSuggestionsService(ContentSuggestionsService::State::DISABLED); |
(...skipping 13 matching lines...) Expand all Loading... |
264 Eq(CategoryStatus::NOT_PROVIDED)); | 273 Eq(CategoryStatus::NOT_PROVIDED)); |
265 EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category), | 274 EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category), |
266 Eq(CategoryStatus::NOT_PROVIDED)); | 275 Eq(CategoryStatus::NOT_PROVIDED)); |
267 | 276 |
268 MockProvider* provider1 = RegisterProvider(articles_category); | 277 MockProvider* provider1 = RegisterProvider(articles_category); |
269 provider1->FireCategoryStatusChangedWithCurrentStatus(articles_category); | 278 provider1->FireCategoryStatusChangedWithCurrentStatus(articles_category); |
270 EXPECT_THAT(providers().count(offline_pages_category), Eq(0ul)); | 279 EXPECT_THAT(providers().count(offline_pages_category), Eq(0ul)); |
271 ASSERT_THAT(providers().count(articles_category), Eq(1ul)); | 280 ASSERT_THAT(providers().count(articles_category), Eq(1ul)); |
272 EXPECT_THAT(providers().at(articles_category), Eq(provider1)); | 281 EXPECT_THAT(providers().at(articles_category), Eq(provider1)); |
273 EXPECT_THAT(providers().size(), Eq(1ul)); | 282 EXPECT_THAT(providers().size(), Eq(1ul)); |
274 EXPECT_THAT(service()->GetCategories(), ElementsAre(articles_category)); | 283 EXPECT_THAT(service()->GetCategories(), |
| 284 UnorderedElementsAre(articles_category)); |
275 EXPECT_THAT(service()->GetCategoryStatus(articles_category), | 285 EXPECT_THAT(service()->GetCategoryStatus(articles_category), |
276 Eq(CategoryStatus::AVAILABLE)); | 286 Eq(CategoryStatus::AVAILABLE)); |
277 EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category), | 287 EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category), |
278 Eq(CategoryStatus::NOT_PROVIDED)); | 288 Eq(CategoryStatus::NOT_PROVIDED)); |
279 | 289 |
280 MockProvider* provider2 = RegisterProvider(offline_pages_category); | 290 MockProvider* provider2 = RegisterProvider(offline_pages_category); |
281 provider2->FireCategoryStatusChangedWithCurrentStatus(offline_pages_category); | 291 provider2->FireCategoryStatusChangedWithCurrentStatus(offline_pages_category); |
282 ASSERT_THAT(providers().count(offline_pages_category), Eq(1ul)); | 292 ASSERT_THAT(providers().count(offline_pages_category), Eq(1ul)); |
283 EXPECT_THAT(providers().at(articles_category), Eq(provider1)); | 293 EXPECT_THAT(providers().at(articles_category), Eq(provider1)); |
284 ASSERT_THAT(providers().count(articles_category), Eq(1ul)); | 294 ASSERT_THAT(providers().count(articles_category), Eq(1ul)); |
285 EXPECT_THAT(providers().at(offline_pages_category), Eq(provider2)); | 295 EXPECT_THAT(providers().at(offline_pages_category), Eq(provider2)); |
286 EXPECT_THAT(providers().size(), Eq(2ul)); | 296 EXPECT_THAT(providers().size(), Eq(2ul)); |
287 EXPECT_THAT(service()->GetCategories(), | 297 EXPECT_THAT(service()->GetCategories(), |
288 ElementsAre(offline_pages_category, articles_category)); | 298 UnorderedElementsAre(offline_pages_category, articles_category)); |
289 EXPECT_THAT(service()->GetCategoryStatus(articles_category), | 299 EXPECT_THAT(service()->GetCategoryStatus(articles_category), |
290 Eq(CategoryStatus::AVAILABLE)); | 300 Eq(CategoryStatus::AVAILABLE)); |
291 EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category), | 301 EXPECT_THAT(service()->GetCategoryStatus(offline_pages_category), |
292 Eq(CategoryStatus::AVAILABLE)); | 302 Eq(CategoryStatus::AVAILABLE)); |
293 } | 303 } |
294 | 304 |
295 TEST_F(ContentSuggestionsServiceDisabledTest, ShouldDoNothingWhenDisabled) { | 305 TEST_F(ContentSuggestionsServiceDisabledTest, ShouldDoNothingWhenDisabled) { |
296 Category articles_category = | 306 Category articles_category = |
297 Category::FromKnownCategory(KnownCategories::ARTICLES); | 307 Category::FromKnownCategory(KnownCategories::ARTICLES); |
298 Category offline_pages_category = | 308 Category offline_pages_category = |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
538 CategoryStatus::INITIALIZING)); | 548 CategoryStatus::INITIALIZING)); |
539 provider->FireCategoryStatusChanged(new_category, | 549 provider->FireCategoryStatusChanged(new_category, |
540 CategoryStatus::INITIALIZING); | 550 CategoryStatus::INITIALIZING); |
541 | 551 |
542 ASSERT_THAT(providers().count(new_category), Eq(1ul)); | 552 ASSERT_THAT(providers().count(new_category), Eq(1ul)); |
543 EXPECT_THAT(providers().at(new_category), Eq(provider)); | 553 EXPECT_THAT(providers().at(new_category), Eq(provider)); |
544 ExpectThatSuggestionsAre(new_category, std::vector<int>()); | 554 ExpectThatSuggestionsAre(new_category, std::vector<int>()); |
545 EXPECT_THAT(service()->GetCategoryStatus(new_category), | 555 EXPECT_THAT(service()->GetCategoryStatus(new_category), |
546 Eq(CategoryStatus::INITIALIZING)); | 556 Eq(CategoryStatus::INITIALIZING)); |
547 EXPECT_THAT(service()->GetCategories(), | 557 EXPECT_THAT(service()->GetCategories(), |
548 Eq(std::vector<Category>({category, new_category}))); | 558 UnorderedElementsAre(category, new_category)); |
549 | 559 |
550 service()->RemoveObserver(&observer); | 560 service()->RemoveObserver(&observer); |
551 } | 561 } |
552 | 562 |
553 TEST_F(ContentSuggestionsServiceTest, ShouldRemoveCategoryWhenNotProvided) { | 563 TEST_F(ContentSuggestionsServiceTest, ShouldRemoveCategoryWhenNotProvided) { |
554 Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS); | 564 Category category = Category::FromKnownCategory(KnownCategories::DOWNLOADS); |
555 MockProvider* provider = RegisterProvider(category); | 565 MockProvider* provider = RegisterProvider(category); |
556 MockServiceObserver observer; | 566 MockServiceObserver observer; |
557 service()->AddObserver(&observer); | 567 service()->AddObserver(&observer); |
558 | 568 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
591 service()->Fetch(category, known_suggestions, FetchDoneCallback()); | 601 service()->Fetch(category, known_suggestions, FetchDoneCallback()); |
592 } | 602 } |
593 | 603 |
594 TEST_F(ContentSuggestionsServiceTest, DismissAndRestoreCategory) { | 604 TEST_F(ContentSuggestionsServiceTest, DismissAndRestoreCategory) { |
595 // Register a category with one suggestion. | 605 // Register a category with one suggestion. |
596 Category category = Category::FromKnownCategory(KnownCategories::ARTICLES); | 606 Category category = Category::FromKnownCategory(KnownCategories::ARTICLES); |
597 MockProvider* provider = RegisterProvider(category); | 607 MockProvider* provider = RegisterProvider(category); |
598 provider->FireCategoryStatusChangedWithCurrentStatus(category); | 608 provider->FireCategoryStatusChangedWithCurrentStatus(category); |
599 provider->FireSuggestionsChanged(category, CreateSuggestions(category, {42})); | 609 provider->FireSuggestionsChanged(category, CreateSuggestions(category, {42})); |
600 | 610 |
601 EXPECT_THAT(service()->GetCategories(), ElementsAre(category)); | 611 EXPECT_THAT(service()->GetCategories(), UnorderedElementsAre(category)); |
602 EXPECT_THAT(service()->GetCategoryStatus(category), | 612 EXPECT_THAT(service()->GetCategoryStatus(category), |
603 Eq(CategoryStatus::AVAILABLE)); | 613 Eq(CategoryStatus::AVAILABLE)); |
604 ExpectThatSuggestionsAre(category, {42}); | 614 ExpectThatSuggestionsAre(category, {42}); |
605 EXPECT_THAT(providers().count(category), Eq(1ul)); | 615 EXPECT_THAT(providers().count(category), Eq(1ul)); |
606 EXPECT_THAT(dismissed_providers(), IsEmpty()); | 616 EXPECT_THAT(dismissed_providers(), IsEmpty()); |
607 | 617 |
608 // Dismissing the category clears the suggestions for it. | 618 // Dismissing the category clears the suggestions for it. |
609 service()->DismissCategory(category); | 619 service()->DismissCategory(category); |
610 | 620 |
611 EXPECT_THAT(service()->GetCategories(), IsEmpty()); | 621 EXPECT_THAT(service()->GetCategories(), IsEmpty()); |
612 EXPECT_THAT(service()->GetCategoryStatus(category), | 622 EXPECT_THAT(service()->GetCategoryStatus(category), |
613 Eq(CategoryStatus::NOT_PROVIDED)); | 623 Eq(CategoryStatus::NOT_PROVIDED)); |
614 EXPECT_THAT(service()->GetSuggestionsForCategory(category), IsEmpty()); | 624 EXPECT_THAT(service()->GetSuggestionsForCategory(category), IsEmpty()); |
615 EXPECT_THAT(providers(), IsEmpty()); | 625 EXPECT_THAT(providers(), IsEmpty()); |
616 EXPECT_THAT(dismissed_providers().count(category), Eq(1ul)); | 626 EXPECT_THAT(dismissed_providers().count(category), Eq(1ul)); |
617 | 627 |
618 // Restoring the dismissed category makes it available again but it is still | 628 // Restoring the dismissed category makes it available again but it is still |
619 // empty. | 629 // empty. |
620 service()->RestoreDismissedCategories(); | 630 service()->RestoreDismissedCategories(); |
621 | 631 |
622 EXPECT_THAT(service()->GetCategories(), ElementsAre(category)); | 632 EXPECT_THAT(service()->GetCategories(), UnorderedElementsAre(category)); |
623 EXPECT_THAT(service()->GetCategoryStatus(category), | 633 EXPECT_THAT(service()->GetCategoryStatus(category), |
624 Eq(CategoryStatus::AVAILABLE)); | 634 Eq(CategoryStatus::AVAILABLE)); |
625 EXPECT_THAT(service()->GetSuggestionsForCategory(category), IsEmpty()); | 635 EXPECT_THAT(service()->GetSuggestionsForCategory(category), IsEmpty()); |
626 EXPECT_THAT(providers().count(category), Eq(1ul)); | 636 EXPECT_THAT(providers().count(category), Eq(1ul)); |
627 EXPECT_THAT(dismissed_providers(), IsEmpty()); | 637 EXPECT_THAT(dismissed_providers(), IsEmpty()); |
628 } | 638 } |
629 | 639 |
630 TEST_F(ContentSuggestionsServiceTest, ShouldRestoreDismissedCategories) { | 640 TEST_F(ContentSuggestionsServiceTest, ShouldRestoreDismissedCategories) { |
631 // Create and register provider. | 641 // Create and register provider. |
632 Category category1 = Category::FromIDValue(1); | 642 Category category1 = Category::FromIDValue(1); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
698 // restoration. | 708 // restoration. |
699 provider = RegisterProvider(category); | 709 provider = RegisterProvider(category); |
700 provider->FireCategoryStatusChangedWithCurrentStatus(category); | 710 provider->FireCategoryStatusChangedWithCurrentStatus(category); |
701 EXPECT_TRUE(service()->IsCategoryDismissed(category)); | 711 EXPECT_TRUE(service()->IsCategoryDismissed(category)); |
702 | 712 |
703 service()->RestoreDismissedCategories(); | 713 service()->RestoreDismissedCategories(); |
704 EXPECT_FALSE(service()->IsCategoryDismissed(category)); | 714 EXPECT_FALSE(service()->IsCategoryDismissed(category)); |
705 EXPECT_THAT(providers().find(category)->second, Eq(provider)); | 715 EXPECT_THAT(providers().find(category)->second, Eq(provider)); |
706 } | 716 } |
707 | 717 |
| 718 TEST_F(ContentSuggestionsServiceTest, ShouldReturnCategoriesInOrderToDisplay) { |
| 719 const Category first_category = Category::FromRemoteCategory(1); |
| 720 const Category second_category = Category::FromRemoteCategory(2); |
| 721 |
| 722 auto fake_ranker = base::MakeUnique<FakeCategoryRanker>(); |
| 723 FakeCategoryRanker* raw_fake_ranker = fake_ranker.get(); |
| 724 SetCategoryRanker(std::move(fake_ranker)); |
| 725 |
| 726 raw_fake_ranker->SetOrder({first_category, second_category}); |
| 727 |
| 728 // The service is recreated to pick up the new ranker. |
| 729 ResetService(); |
| 730 |
| 731 MockProvider* provider = RegisterProvider({first_category, second_category}); |
| 732 provider->FireCategoryStatusChangedWithCurrentStatus(first_category); |
| 733 provider->FireCategoryStatusChangedWithCurrentStatus(second_category); |
| 734 |
| 735 EXPECT_THAT(service()->GetCategories(), |
| 736 ElementsAre(first_category, second_category)); |
| 737 |
| 738 // The order to display (in the ranker) changes. |
| 739 raw_fake_ranker->SetOrder({second_category, first_category}); |
| 740 |
| 741 // Categories order should reflect the new order. |
| 742 EXPECT_THAT(service()->GetCategories(), |
| 743 ElementsAre(second_category, first_category)); |
| 744 } |
| 745 |
708 } // namespace ntp_snippets | 746 } // namespace ntp_snippets |
OLD | NEW |