| 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_status.h" | 19 #include "components/ntp_snippets/category_status.h" |
| 20 #include "components/ntp_snippets/content_suggestion.h" | 20 #include "components/ntp_snippets/content_suggestion.h" |
| 21 #include "components/ntp_snippets/content_suggestions_provider.h" | 21 #include "components/ntp_snippets/content_suggestions_provider.h" |
| 22 #include "components/prefs/testing_pref_service.h" |
| 22 #include "testing/gmock/include/gmock/gmock.h" | 23 #include "testing/gmock/include/gmock/gmock.h" |
| 23 #include "testing/gtest/include/gtest/gtest.h" | 24 #include "testing/gtest/include/gtest/gtest.h" |
| 24 #include "ui/gfx/image/image.h" | 25 #include "ui/gfx/image/image.h" |
| 25 | 26 |
| 26 using testing::ElementsAre; | 27 using testing::ElementsAre; |
| 27 using testing::Eq; | 28 using testing::Eq; |
| 28 using testing::InvokeWithoutArgs; | 29 using testing::InvokeWithoutArgs; |
| 29 using testing::IsEmpty; | 30 using testing::IsEmpty; |
| 30 using testing::Mock; | 31 using testing::Mock; |
| 31 using testing::Property; | 32 using testing::Property; |
| (...skipping 28 matching lines...) Expand all Loading... |
| 60 return CategoryInfo(base::ASCIIToUTF16("Section title"), | 61 return CategoryInfo(base::ASCIIToUTF16("Section title"), |
| 61 ContentSuggestionsCardLayout::FULL_CARD, true, true); | 62 ContentSuggestionsCardLayout::FULL_CARD, true, true); |
| 62 } | 63 } |
| 63 | 64 |
| 64 void FireSuggestionsChanged( | 65 void FireSuggestionsChanged( |
| 65 Category category, | 66 Category category, |
| 66 std::vector<ContentSuggestion> suggestions) { | 67 std::vector<ContentSuggestion> suggestions) { |
| 67 observer()->OnNewSuggestions(this, category, std::move(suggestions)); | 68 observer()->OnNewSuggestions(this, category, std::move(suggestions)); |
| 68 } | 69 } |
| 69 | 70 |
| 71 void FireSuggestionBatchChanged( |
| 72 ContentSuggestionsService::SuggestionBatch suggestion_batch) { |
| 73 observer()->OnNewSuggestionBatch(this, std::move(suggestion_batch)); |
| 74 } |
| 75 |
| 70 void FireCategoryStatusChanged(Category category, CategoryStatus new_status) { | 76 void FireCategoryStatusChanged(Category category, CategoryStatus new_status) { |
| 71 statuses_[category.id()] = new_status; | 77 statuses_[category.id()] = new_status; |
| 72 observer()->OnCategoryStatusChanged(this, category, new_status); | 78 observer()->OnCategoryStatusChanged(this, category, new_status); |
| 73 } | 79 } |
| 74 | 80 |
| 75 void FireCategoryStatusChangedWithCurrentStatus(Category category) { | 81 void FireCategoryStatusChangedWithCurrentStatus(Category category) { |
| 76 observer()->OnCategoryStatusChanged(this, category, | 82 observer()->OnCategoryStatusChanged(this, category, |
| 77 statuses_[category.id()]); | 83 statuses_[category.id()]); |
| 78 } | 84 } |
| 79 | 85 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 MOCK_METHOD0(ContentSuggestionsServiceShutdown, void()); | 120 MOCK_METHOD0(ContentSuggestionsServiceShutdown, void()); |
| 115 | 121 |
| 116 private: | 122 private: |
| 117 DISALLOW_COPY_AND_ASSIGN(MockServiceObserver); | 123 DISALLOW_COPY_AND_ASSIGN(MockServiceObserver); |
| 118 }; | 124 }; |
| 119 | 125 |
| 120 } // namespace | 126 } // namespace |
| 121 | 127 |
| 122 class ContentSuggestionsServiceTest : public testing::Test { | 128 class ContentSuggestionsServiceTest : public testing::Test { |
| 123 public: | 129 public: |
| 124 ContentSuggestionsServiceTest() {} | 130 ContentSuggestionsServiceTest() |
| 131 : pref_service_(new TestingPrefServiceSimple()) {} |
| 125 | 132 |
| 126 void SetUp() override { | 133 void SetUp() override { |
| 134 RegisterPrefs(); |
| 127 CreateContentSuggestionsService(ContentSuggestionsService::State::ENABLED); | 135 CreateContentSuggestionsService(ContentSuggestionsService::State::ENABLED); |
| 128 } | 136 } |
| 129 | 137 |
| 130 void TearDown() override { | 138 void TearDown() override { |
| 131 service_->Shutdown(); | 139 service_->Shutdown(); |
| 132 service_.reset(); | 140 service_.reset(); |
| 133 } | 141 } |
| 134 | 142 |
| 135 // Verifies that exactly the suggestions with the given |numbers| are | 143 // Verifies that exactly the suggestions with the given |numbers| are |
| 136 // returned by the service for the given |category|. | 144 // returned by the service for the given |category|. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 std::unique_ptr<MockProvider> provider = base::MakeUnique<MockProvider>( | 191 std::unique_ptr<MockProvider> provider = base::MakeUnique<MockProvider>( |
| 184 service(), category_factory(), provided_categories); | 192 service(), category_factory(), provided_categories); |
| 185 MockProvider* result = provider.get(); | 193 MockProvider* result = provider.get(); |
| 186 service()->RegisterProvider(std::move(provider)); | 194 service()->RegisterProvider(std::move(provider)); |
| 187 return result; | 195 return result; |
| 188 } | 196 } |
| 189 | 197 |
| 190 MOCK_METHOD1(OnImageFetched, void(const gfx::Image&)); | 198 MOCK_METHOD1(OnImageFetched, void(const gfx::Image&)); |
| 191 | 199 |
| 192 protected: | 200 protected: |
| 201 void RegisterPrefs() { |
| 202 ContentSuggestionsService::RegisterProfilePrefs(pref_service_->registry()); |
| 203 UserClassifier::RegisterProfilePrefs(pref_service_->registry()); |
| 204 } |
| 205 |
| 193 void CreateContentSuggestionsService( | 206 void CreateContentSuggestionsService( |
| 194 ContentSuggestionsService::State enabled) { | 207 ContentSuggestionsService::State enabled) { |
| 195 ASSERT_FALSE(service_); | 208 ASSERT_FALSE(service_); |
| 196 service_.reset(new ContentSuggestionsService(enabled, | 209 service_.reset(new ContentSuggestionsService( |
| 197 nullptr /* history_service */, | 210 enabled, nullptr /* history_service */, pref_service_.get())); |
| 198 nullptr /* pref_service */)); | |
| 199 } | 211 } |
| 200 | 212 |
| 201 ContentSuggestionsService* service() { return service_.get(); } | 213 ContentSuggestionsService* service() { return service_.get(); } |
| 202 | 214 |
| 203 // Returns a suggestion instance for testing. | 215 // Returns a suggestion instance for testing. |
| 204 ContentSuggestion CreateSuggestion(Category category, int number) { | 216 ContentSuggestion CreateSuggestion(Category category, int number) { |
| 205 return ContentSuggestion( | 217 return ContentSuggestion( |
| 206 category, base::IntToString(number), | 218 category, base::IntToString(number), |
| 207 GURL("http://testsuggestion/" + base::IntToString(number))); | 219 GURL("http://testsuggestion/" + base::IntToString(number))); |
| 208 } | 220 } |
| 209 | 221 |
| 210 std::vector<ContentSuggestion> CreateSuggestions( | 222 std::vector<ContentSuggestion> CreateSuggestions( |
| 211 Category category, | 223 Category category, |
| 212 const std::vector<int>& numbers) { | 224 const std::vector<int>& numbers) { |
| 213 std::vector<ContentSuggestion> result; | 225 std::vector<ContentSuggestion> result; |
| 214 for (int number : numbers) { | 226 for (int number : numbers) { |
| 215 result.push_back(CreateSuggestion(category, number)); | 227 result.push_back(CreateSuggestion(category, number)); |
| 216 } | 228 } |
| 217 return result; | 229 return result; |
| 218 } | 230 } |
| 219 | 231 |
| 220 private: | 232 private: |
| 221 std::unique_ptr<ContentSuggestionsService> service_; | 233 std::unique_ptr<ContentSuggestionsService> service_; |
| 234 std::unique_ptr<TestingPrefServiceSimple> pref_service_; |
| 222 | 235 |
| 223 DISALLOW_COPY_AND_ASSIGN(ContentSuggestionsServiceTest); | 236 DISALLOW_COPY_AND_ASSIGN(ContentSuggestionsServiceTest); |
| 224 }; | 237 }; |
| 225 | 238 |
| 226 class ContentSuggestionsServiceDisabledTest | 239 class ContentSuggestionsServiceDisabledTest |
| 227 : public ContentSuggestionsServiceTest { | 240 : public ContentSuggestionsServiceTest { |
| 228 public: | 241 public: |
| 229 void SetUp() override { | 242 void SetUp() override { |
| 243 RegisterPrefs(); |
| 230 CreateContentSuggestionsService(ContentSuggestionsService::State::DISABLED); | 244 CreateContentSuggestionsService(ContentSuggestionsService::State::DISABLED); |
| 231 } | 245 } |
| 232 }; | 246 }; |
| 233 | 247 |
| 234 TEST_F(ContentSuggestionsServiceTest, ShouldRegisterProviders) { | 248 TEST_F(ContentSuggestionsServiceTest, ShouldRegisterProviders) { |
| 235 EXPECT_THAT(service()->state(), | 249 EXPECT_THAT(service()->state(), |
| 236 Eq(ContentSuggestionsService::State::ENABLED)); | 250 Eq(ContentSuggestionsService::State::ENABLED)); |
| 237 Category articles_category = FromKnownCategory(KnownCategories::ARTICLES); | 251 Category articles_category = FromKnownCategory(KnownCategories::ARTICLES); |
| 238 Category offline_pages_category = | 252 Category offline_pages_category = |
| 239 FromKnownCategory(KnownCategories::DOWNLOADS); | 253 FromKnownCategory(KnownCategories::DOWNLOADS); |
| (...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 594 TEST_F(ContentSuggestionsServiceTest, ShouldForwardClearHistory) { | 608 TEST_F(ContentSuggestionsServiceTest, ShouldForwardClearHistory) { |
| 595 Category category = FromKnownCategory(KnownCategories::DOWNLOADS); | 609 Category category = FromKnownCategory(KnownCategories::DOWNLOADS); |
| 596 MockProvider* provider = RegisterProvider(category); | 610 MockProvider* provider = RegisterProvider(category); |
| 597 base::Time begin = base::Time::FromTimeT(123), | 611 base::Time begin = base::Time::FromTimeT(123), |
| 598 end = base::Time::FromTimeT(456); | 612 end = base::Time::FromTimeT(456); |
| 599 EXPECT_CALL(*provider, ClearHistory(begin, end, _)); | 613 EXPECT_CALL(*provider, ClearHistory(begin, end, _)); |
| 600 base::Callback<bool(const GURL& url)> filter; | 614 base::Callback<bool(const GURL& url)> filter; |
| 601 service()->ClearHistory(begin, end, filter); | 615 service()->ClearHistory(begin, end, filter); |
| 602 } | 616 } |
| 603 | 617 |
| 618 TEST_F(ContentSuggestionsServiceTest, ShouldClearCategoryDismissal) { |
| 619 // Create and register provider |
| 620 Category category1 = service()->category_factory()->FromIDValue(1); |
| 621 Category category10042 = service()->category_factory()->FromIDValue(10042); |
| 622 Category category10043 = service()->category_factory()->FromIDValue(10043); |
| 623 |
| 624 // Setup and verify initial state |
| 625 MockProvider* provider = |
| 626 RegisterProvider({category1, category10042, category10043}); |
| 627 provider->FireCategoryStatusChangedWithCurrentStatus(category1); |
| 628 provider->FireCategoryStatusChangedWithCurrentStatus(category10042); |
| 629 provider->FireCategoryStatusChangedWithCurrentStatus(category10043); |
| 630 |
| 631 ASSERT_THAT(service()->GetCategoryStatus(category1), |
| 632 Eq(CategoryStatus::AVAILABLE)); |
| 633 ASSERT_THAT(service()->GetCategoryStatus(category10042), |
| 634 Eq(CategoryStatus::AVAILABLE)); |
| 635 ASSERT_THAT(service()->GetCategoryStatus(category10043), |
| 636 Eq(CategoryStatus::AVAILABLE)); |
| 637 |
| 638 // Dismiss all the categories. None should be provided now. |
| 639 service()->DismissCategory(category1); |
| 640 service()->DismissCategory(category10042); |
| 641 service()->DismissCategory(category10043); |
| 642 |
| 643 ASSERT_THAT(service()->GetCategoryStatus(category1), |
| 644 Eq(CategoryStatus::NOT_PROVIDED)); |
| 645 ASSERT_THAT(service()->GetCategoryStatus(category10042), |
| 646 Eq(CategoryStatus::NOT_PROVIDED)); |
| 647 ASSERT_THAT(service()->GetCategoryStatus(category10043), |
| 648 Eq(CategoryStatus::NOT_PROVIDED)); |
| 649 |
| 650 // Receiving a notification without suggestions should not change anything. |
| 651 provider->FireSuggestionsChanged(category1, std::vector<ContentSuggestion>()); |
| 652 |
| 653 ASSERT_THAT(service()->GetCategoryStatus(category1), |
| 654 Eq(CategoryStatus::NOT_PROVIDED)); |
| 655 ASSERT_THAT(service()->GetCategoryStatus(category10042), |
| 656 Eq(CategoryStatus::NOT_PROVIDED)); |
| 657 ASSERT_THAT(service()->GetCategoryStatus(category10043), |
| 658 Eq(CategoryStatus::NOT_PROVIDED)); |
| 659 |
| 660 // A notification with suggestions should make the category available. |
| 661 provider->FireSuggestionsChanged(category1, |
| 662 CreateSuggestions(category1, {1, 2})); |
| 663 |
| 664 ASSERT_THAT(service()->GetCategoryStatus(category1), |
| 665 Eq(CategoryStatus::AVAILABLE)); |
| 666 ASSERT_THAT(service()->GetCategoryStatus(category10042), |
| 667 Eq(CategoryStatus::NOT_PROVIDED)); |
| 668 ASSERT_THAT(service()->GetCategoryStatus(category10043), |
| 669 Eq(CategoryStatus::NOT_PROVIDED)); |
| 670 |
| 671 // A batch notification with suggestions should make the category available. |
| 672 ContentSuggestionsService::SuggestionBatch suggestion_batch; |
| 673 suggestion_batch[category10042] = CreateSuggestions(category10042, {3, 4}); |
| 674 suggestion_batch[category10043] = std::vector<ContentSuggestion>(); |
| 675 provider->FireSuggestionBatchChanged(std::move(suggestion_batch)); |
| 676 |
| 677 ASSERT_THAT(service()->GetCategoryStatus(category1), |
| 678 Eq(CategoryStatus::AVAILABLE)); |
| 679 ASSERT_THAT(service()->GetCategoryStatus(category10042), |
| 680 Eq(CategoryStatus::AVAILABLE)); |
| 681 ASSERT_THAT(service()->GetCategoryStatus(category10043), |
| 682 Eq(CategoryStatus::NOT_PROVIDED)); |
| 683 |
| 684 // A batch notification with a missing category should clear its dismissal. |
| 685 suggestion_batch = ContentSuggestionsService::SuggestionBatch(); |
| 686 suggestion_batch[category10042] = CreateSuggestions(category10042, {5, 6}); |
| 687 provider->FireSuggestionBatchChanged(std::move(suggestion_batch)); |
| 688 |
| 689 ASSERT_THAT(service()->GetCategoryStatus(category1), |
| 690 Eq(CategoryStatus::AVAILABLE)); |
| 691 ASSERT_THAT(service()->GetCategoryStatus(category10042), |
| 692 Eq(CategoryStatus::AVAILABLE)); |
| 693 ASSERT_THAT(service()->GetCategoryStatus(category10043), |
| 694 Eq(CategoryStatus::AVAILABLE)); |
| 695 } |
| 696 |
| 697 TEST_F(ContentSuggestionsServiceTest, ShouldNotReturnDismissedCategories) { |
| 698 Category category = FromKnownCategory(KnownCategories::ARTICLES); |
| 699 |
| 700 // Create and register provider |
| 701 MockProvider* provider = RegisterProvider(category); |
| 702 provider->FireCategoryStatusChangedWithCurrentStatus(category); |
| 703 ASSERT_THAT(providers().count(category), Eq(1ul)); |
| 704 EXPECT_THAT(providers().at(category), Eq(provider)); |
| 705 ASSERT_THAT(service()->GetCategories(), ElementsAre(category)); |
| 706 |
| 707 // Create and register observer |
| 708 MockServiceObserver observer; |
| 709 service()->AddObserver(&observer); |
| 710 |
| 711 EXPECT_THAT(service()->GetCategoryStatus(category), |
| 712 Eq(CategoryStatus::AVAILABLE)); |
| 713 EXPECT_TRUE(service()->GetCategoryInfo(category)); |
| 714 |
| 715 service()->DismissCategory(category); |
| 716 EXPECT_THAT(service()->GetCategories(), ElementsAre(category)); |
| 717 EXPECT_THAT(service()->GetCategoryStatus(category), |
| 718 Eq(CategoryStatus::NOT_PROVIDED)); |
| 719 EXPECT_FALSE(service()->GetCategoryInfo(category)); |
| 720 EXPECT_TRUE(service()->GetSuggestionsForCategory(category).empty()); |
| 721 |
| 722 // OnCategoryStatusChanged |
| 723 EXPECT_CALL(observer, OnCategoryStatusChanged(category, _)).Times(0); |
| 724 provider->FireCategoryStatusChanged(category, CategoryStatus::SIGNED_OUT); |
| 725 provider->FireCategoryStatusChanged(category, CategoryStatus::AVAILABLE); |
| 726 Mock::VerifyAndClearExpectations(&observer); |
| 727 |
| 728 // OnNewSuggestions (with suggestions) |
| 729 EXPECT_CALL(observer, OnNewSuggestions(category)); |
| 730 provider->FireSuggestionsChanged(category, |
| 731 CreateSuggestions(category, {1, 2})); |
| 732 ExpectThatSuggestionsAre(category, {1, 2}); |
| 733 Mock::VerifyAndClearExpectations(&observer); |
| 734 |
| 735 EXPECT_THAT(service()->GetCategoryStatus(category), |
| 736 Eq(CategoryStatus::AVAILABLE)); |
| 737 EXPECT_TRUE(service()->GetCategoryInfo(category)); |
| 738 EXPECT_FALSE(service()->GetSuggestionsForCategory(category).empty()); |
| 739 |
| 740 EXPECT_CALL(observer, |
| 741 OnCategoryStatusChanged(category, CategoryStatus::SIGNED_OUT)); |
| 742 provider->FireCategoryStatusChanged(category, CategoryStatus::SIGNED_OUT); |
| 743 Mock::VerifyAndClearExpectations(&observer); |
| 744 |
| 745 service()->RemoveObserver(&observer); |
| 746 } |
| 747 |
| 604 } // namespace ntp_snippets | 748 } // namespace ntp_snippets |
| OLD | NEW |