| Index: components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc
|
| diff --git a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0015c8356ad8bb8724768dd2f3cf2b0b9b2df446
|
| --- /dev/null
|
| +++ b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider_unittest.cc
|
| @@ -0,0 +1,296 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h"
|
| +
|
| +#include <memory>
|
| +#include <set>
|
| +#include <string>
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include "base/command_line.h"
|
| +#include "base/macros.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "base/time/time.h"
|
| +#include "components/ntp_snippets/features.h"
|
| +#include "components/ntp_snippets/ntp_snippets_constants.h"
|
| +#include "components/ntp_snippets/pref_names.h"
|
| +#include "components/ntp_snippets/remote/persistent_scheduler.h"
|
| +#include "components/ntp_snippets/remote/remote_suggestions_provider.h"
|
| +#include "components/ntp_snippets/remote/test_utils.h"
|
| +#include "components/ntp_snippets/status.h"
|
| +#include "components/ntp_snippets/user_classifier.h"
|
| +#include "components/prefs/testing_pref_service.h"
|
| +#include "components/variations/variations_params_manager.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using testing::ElementsAre;
|
| +using testing::Eq;
|
| +using testing::InSequence;
|
| +using testing::Invoke;
|
| +using testing::IsEmpty;
|
| +using testing::Mock;
|
| +using testing::MockFunction;
|
| +using testing::Not;
|
| +using testing::SaveArg;
|
| +using testing::SaveArgPointee;
|
| +using testing::SizeIs;
|
| +using testing::StartsWith;
|
| +using testing::StrictMock;
|
| +using testing::WithArgs;
|
| +using testing::_;
|
| +
|
| +namespace ntp_snippets {
|
| +
|
| +class NTPSnippetsFetcher;
|
| +
|
| +namespace {
|
| +
|
| +class MockPersistentScheduler : public PersistentScheduler {
|
| + public:
|
| + MOCK_METHOD2(Schedule,
|
| + bool(base::TimeDelta period_wifi,
|
| + base::TimeDelta period_fallback));
|
| + MOCK_METHOD0(Unschedule, bool());
|
| +};
|
| +
|
| +// TODO(jkrcal): Move into its own library to reuse in other unit-tests?
|
| +class MockRemoteSuggestionsProvider : public RemoteSuggestionsProvider {
|
| + public:
|
| + MockRemoteSuggestionsProvider(Observer* observer)
|
| + : RemoteSuggestionsProvider(observer) {}
|
| +
|
| + // Move-only params are not supported by GMock. We want to mock out
|
| + // RefetchInTheBackground() which takes a unique_ptr<>. Instead, we add a new
|
| + // mock function which takes a copy of the callback and override the
|
| + // RemoteSuggestionsProvider's method to forward the call into the new mock
|
| + // function.
|
| + void SetProviderStatusCallback(
|
| + std::unique_ptr<RemoteSuggestionsProvider::ProviderStatusCallback>
|
| + callback) override {
|
| + SetProviderStatusCallback(*callback);
|
| + }
|
| + MOCK_METHOD1(SetProviderStatusCallback,
|
| + void(RemoteSuggestionsProvider::ProviderStatusCallback));
|
| +
|
| + // Move-only params are not supported by GMock (same work-around as above).
|
| + void RefetchInTheBackground(
|
| + std::unique_ptr<RemoteSuggestionsProvider::FetchStatusCallback> callback)
|
| + override {
|
| + RefetchInTheBackground(*callback);
|
| + }
|
| + MOCK_METHOD1(RefetchInTheBackground,
|
| + void(RemoteSuggestionsProvider::FetchStatusCallback));
|
| +
|
| + MOCK_CONST_METHOD0(snippets_fetcher_for_testing_and_debugging,
|
| + const NTPSnippetsFetcher*());
|
| +
|
| + MOCK_METHOD1(GetCategoryStatus, CategoryStatus(Category));
|
| + MOCK_METHOD1(GetCategoryInfo, CategoryInfo(Category));
|
| + MOCK_METHOD3(ClearHistory,
|
| + void(base::Time begin,
|
| + base::Time end,
|
| + const base::Callback<bool(const GURL& url)>& filter));
|
| + MOCK_METHOD3(Fetch,
|
| + void(const Category&,
|
| + const std::set<std::string>&,
|
| + const FetchDoneCallback&));
|
| + MOCK_METHOD1(ClearCachedSuggestions, void(Category));
|
| + MOCK_METHOD1(ClearDismissedSuggestionsForDebugging, void(Category));
|
| + MOCK_METHOD1(DismissSuggestion, void(const ContentSuggestion::ID&));
|
| + MOCK_METHOD2(FetchSuggestionImage,
|
| + void(const ContentSuggestion::ID&, const ImageFetchedCallback&));
|
| + MOCK_METHOD2(GetDismissedSuggestionsForDebugging,
|
| + void(Category, const DismissedSuggestionsCallback&));
|
| + MOCK_METHOD0(OnSignInStateChanged, void());
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class SchedulingRemoteSuggestionsProviderTest
|
| + : public ::testing::Test {
|
| + public:
|
| + SchedulingRemoteSuggestionsProviderTest()
|
| + : underlying_provider_(nullptr),
|
| + scheduling_provider_(nullptr),
|
| + user_classifier_(/*pref_service=*/nullptr) {
|
| + SchedulingRemoteSuggestionsProvider::RegisterProfilePrefs(
|
| + utils_.pref_service()->registry());
|
| +
|
| + auto underlying_provider =
|
| + base::MakeUnique<StrictMock<MockRemoteSuggestionsProvider>>(
|
| + /*observer=*/nullptr);
|
| + underlying_provider_ = underlying_provider.get();
|
| +
|
| + // SchedulingRemoteSuggestionsProvider calls SetProviderStatusCallback(_) to
|
| + // stay in the loop of status changes.
|
| + EXPECT_CALL(*underlying_provider_, SetProviderStatusCallback(_))
|
| + .WillOnce(SaveArg<0>(&provider_status_callback_));
|
| +
|
| + scheduling_provider_ =
|
| + base::MakeUnique<SchedulingRemoteSuggestionsProvider>(
|
| + /*observer=*/nullptr, std::move(underlying_provider),
|
| + &persistent_scheduler_, &user_classifier_, utils_.pref_service());
|
| + }
|
| +
|
| + protected:
|
| + StrictMock<MockPersistentScheduler> persistent_scheduler_;
|
| + StrictMock<MockRemoteSuggestionsProvider>* underlying_provider_;
|
| + std::unique_ptr<SchedulingRemoteSuggestionsProvider> scheduling_provider_;
|
| + RemoteSuggestionsProvider::ProviderStatusCallback provider_status_callback_;
|
| +
|
| + void ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus new_status) {
|
| + provider_status_callback_.Run(new_status);
|
| + }
|
| +
|
| + private:
|
| + test::RemoteSuggestionsTestUtils utils_;
|
| + UserClassifier user_classifier_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SchedulingRemoteSuggestionsProviderTest);
|
| +};
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ShouldFetchOnPersistentSchedulerWakeUp) {
|
| + EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_));
|
| + scheduling_provider_->OnPersistentSchedulerWakeUp();
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ShouldRescheduleOnRescheduleFetching) {
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
|
| + scheduling_provider_->RescheduleFetching();
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest, ShouldScheduleOnActivation) {
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ShouldUnscheduleOnLaterInactivation) {
|
| + {
|
| + InSequence s;
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
|
| + EXPECT_CALL(persistent_scheduler_, Unschedule());
|
| + }
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ShouldScheduleOnLaterActivation) {
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
|
| + // There is no schedule yet, so inactivation does not trigger unschedule.
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ShouldRescheduleAfterSuccessfulFetch) {
|
| + // First reschedule on becoming active.
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +
|
| + RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
|
| + EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
|
| + .WillOnce(SaveArg<0>(&signal_fetch_done));
|
| +
|
| + // Trigger a fetch.
|
| + scheduling_provider_->OnPersistentSchedulerWakeUp();
|
| + // Second reschedule after a successful fetch.
|
| + signal_fetch_done.Run(Status::Success());
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ShouldNotRescheduleAfterFailedFetch) {
|
| + // Only reschedule on becoming active.
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +
|
| + RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
|
| + EXPECT_CALL(*underlying_provider_, RefetchInTheBackground(_))
|
| + .WillOnce(SaveArg<0>(&signal_fetch_done));
|
| +
|
| + // Trigger a fetch.
|
| + scheduling_provider_->OnPersistentSchedulerWakeUp();
|
| + // No furter reschedule after a failure.
|
| + signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest, ShouldScheduleOnlyOnce) {
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| + // No further call to Schedule on a second status callback.
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest, ShouldUnscheduleOnlyOnce) {
|
| + {
|
| + InSequence s;
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _));
|
| + EXPECT_CALL(persistent_scheduler_, Unschedule());
|
| + }
|
| + // First schedule so that later we really unschedule.
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
|
| + // No further call to Unschedule on second status callback.
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::INACTIVE);
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ReschedulesWhenWifiParamChanges) {
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +
|
| + // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
|
| + // null. Change the wifi interval for this class.
|
| + variations::testing::VariationParamsManager params_manager(
|
| + ntp_snippets::kStudyName,
|
| + {{"fetching_interval_hours-wifi-active_ntp_user", "2"}},
|
| + {kArticleSuggestionsFeature.name});
|
| +
|
| + // Schedule() should get called for the second time after params have changed.
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +}
|
| +
|
| +TEST_F(SchedulingRemoteSuggestionsProviderTest,
|
| + ReschedulesWhenFallbackParamChanges) {
|
| + EXPECT_CALL(persistent_scheduler_, Schedule(_, _)).Times(2);
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +
|
| + // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
|
| + // null. Change the wifi interval for this class.
|
| + variations::testing::VariationParamsManager params_manager(
|
| + ntp_snippets::kStudyName,
|
| + {{"fetching_interval_hours-fallback-active_ntp_user", "2"}},
|
| + {kArticleSuggestionsFeature.name});
|
| +
|
| + // Schedule() should get called for the second time after params have changed.
|
| + ChangeStatusOfUnderlyingProvider(
|
| + RemoteSuggestionsProvider::ProviderStatus::ACTIVE);
|
| +}
|
| +
|
| +} // namespace ntp_snippets
|
|
|