blob: ea142787bcde0c63ad8553bd033f31262805d5d8 [file] [log] [blame]
// 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/remote_suggestions_scheduler_impl.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/test/simple_test_clock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/clock.h"
#include "base/time/default_clock.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/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/variations/variations_params_manager.h"
#include "components/web_resource/web_resource_pref_names.h"
#include "net/base/network_change_notifier.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::ElementsAre;
using testing::Eq;
using testing::Field;
using testing::InSequence;
using testing::Invoke;
using testing::IsEmpty;
using testing::Mock;
using testing::MockFunction;
using testing::Not;
using testing::Return;
using testing::SaveArg;
using testing::SaveArgPointee;
using testing::SizeIs;
using testing::StartsWith;
using testing::StrictMock;
using testing::WithArgs;
using testing::_;
namespace ntp_snippets {
class RemoteSuggestionsFetcher;
namespace {
class MockPersistentScheduler : public PersistentScheduler {
public:
MOCK_METHOD2(Schedule,
bool(base::TimeDelta period_wifi,
base::TimeDelta period_fallback));
MOCK_METHOD0(Unschedule, bool());
MOCK_METHOD0(IsOnUnmeteredConnection, bool());
};
// TODO(jkrcal): Move into its own library to reuse in other unit-tests?
class MockRemoteSuggestionsProvider : public RemoteSuggestionsProvider {
public:
MockRemoteSuggestionsProvider(Observer* observer)
: RemoteSuggestionsProvider(observer) {}
MOCK_METHOD1(RefetchInTheBackground,
void(const RemoteSuggestionsProvider::FetchStatusCallback&));
MOCK_CONST_METHOD0(suggestions_fetcher_for_debugging,
const RemoteSuggestionsFetcher*());
MOCK_CONST_METHOD1(GetUrlWithFavicon,
GURL(const ContentSuggestion::ID& suggestion_id));
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_METHOD0(ReloadSuggestions, void());
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 RemoteSuggestionsSchedulerImplTest : public ::testing::Test {
public:
RemoteSuggestionsSchedulerImplTest()
: // For the test we enabled all trigger types.
default_variation_params_{{"scheduler_trigger_types",
"persistent_scheduler_wake_up,ntp_opened,"
"browser_foregrounded,browser_cold_start"}},
params_manager_(ntp_snippets::kArticleSuggestionsFeature.name,
default_variation_params_,
{kArticleSuggestionsFeature.name}),
user_classifier_(/*pref_service=*/nullptr,
base::MakeUnique<base::DefaultClock>()) {
RemoteSuggestionsSchedulerImpl::RegisterProfilePrefs(
utils_.pref_service()->registry());
RequestThrottler::RegisterProfilePrefs(utils_.pref_service()->registry());
// TODO(jkrcal) Create a static function in EulaAcceptedNotifier that
// registers this pref and replace the call in browser_process_impl.cc & in
// eula_accepted_notifier_unittest.cc with the new static function.
local_state_.registry()->RegisterBooleanPref(::prefs::kEulaAccepted, false);
// By default pretend we are on WiFi.
EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
.WillRepeatedly(Return(true));
ResetProvider();
}
void ResetProvider() {
provider_ = base::MakeUnique<StrictMock<MockRemoteSuggestionsProvider>>(
/*observer=*/nullptr);
auto test_clock = base::MakeUnique<base::SimpleTestClock>();
test_clock_ = test_clock.get();
test_clock_->SetNow(base::Time::Now());
scheduler_ = base::MakeUnique<RemoteSuggestionsSchedulerImpl>(
&persistent_scheduler_, &user_classifier_, utils_.pref_service(),
&local_state_, std::move(test_clock));
scheduler_->SetProvider(provider_.get());
}
void SetVariationParameter(const std::string& param_name,
const std::string& param_value) {
std::map<std::string, std::string> params = default_variation_params_;
params[param_name] = param_value;
params_manager_.ClearAllVariationParams();
params_manager_.SetVariationParamsWithFeatureAssociations(
ntp_snippets::kArticleSuggestionsFeature.name, params,
{ntp_snippets::kArticleSuggestionsFeature.name});
}
bool IsEulaNotifierAvailable() {
// Create() returns a unique_ptr, so this is no leak.
return web_resource::EulaAcceptedNotifier::Create(&local_state_) != nullptr;
}
void SetEulaAcceptedPref() {
local_state_.SetBoolean(::prefs::kEulaAccepted, true);
}
// GMock cannot deal with move-only types. We need to pass the vector to the
// mock function as const ref using this wrapper callback.
void FetchDoneWrapper(
MockFunction<void(Status status_code,
const std::vector<ContentSuggestion>& suggestions)>*
fetch_done,
Status status_code,
std::vector<ContentSuggestion> suggestions) {
fetch_done->Call(status_code, suggestions);
}
protected:
std::map<std::string, std::string> default_variation_params_;
variations::testing::VariationParamsManager params_manager_;
void ActivateProvider() {
SetEulaAcceptedPref();
scheduler_->OnProviderActivated();
}
void DeactivateProvider() { scheduler_->OnProviderDeactivated(); }
MockPersistentScheduler* persistent_scheduler() {
return &persistent_scheduler_;
}
base::SimpleTestClock* test_clock() { return test_clock_; }
MockRemoteSuggestionsProvider* provider() { return provider_.get(); }
RemoteSuggestionsSchedulerImpl* scheduler() { return scheduler_.get(); }
private:
test::RemoteSuggestionsTestUtils utils_;
UserClassifier user_classifier_;
TestingPrefServiceSimple local_state_;
StrictMock<MockPersistentScheduler> persistent_scheduler_;
base::SimpleTestClock* test_clock_;
std::unique_ptr<MockRemoteSuggestionsProvider> provider_;
std::unique_ptr<RemoteSuggestionsSchedulerImpl> scheduler_;
DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsSchedulerImplTest);
};
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldIgnoreSignalsWhenNotEnabled) {
scheduler()->OnPersistentSchedulerWakeUp();
scheduler()->OnNTPOpened();
scheduler()->OnBrowserForegrounded();
scheduler()->OnBrowserColdStart();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldIgnoreEulaStateOnPlatformsWhereNotAvaiable) {
// Only run this tests on platforms that don't support Eula.
if (IsEulaNotifierAvailable()) {
return;
}
// Activating the provider should schedule the persistent background fetches.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
scheduler()->OnProviderActivated();
// Verify fetches get triggered.
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnPersistentSchedulerWakeUp();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldIgnoreSignalsWhenEulaNotAccepted) {
// Only run this tests on platforms supporting Eula.
if (!IsEulaNotifierAvailable()) {
return;
}
// Activating the provider should schedule the persistent background fetches.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
scheduler()->OnProviderActivated();
// All signals are ignored because of Eula not being accepted.
scheduler()->OnPersistentSchedulerWakeUp();
scheduler()->OnNTPOpened();
scheduler()->OnBrowserForegrounded();
scheduler()->OnBrowserColdStart();
}
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldFetchWhenEulaGetsAccepted) {
// Only run this tests on platforms supporting Eula.
if (!IsEulaNotifierAvailable()) {
return;
}
// Activating the provider should schedule the persistent background fetches.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
scheduler()->OnProviderActivated();
// Make one (ignored) call to make sure we are interested in eula state.
scheduler()->OnPersistentSchedulerWakeUp();
// Accepting Eula afterwards results in a background fetch.
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
SetEulaAcceptedPref();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldIgnoreSignalsWhenDisabledByParam) {
// First set an empty list of allowed trigger types.
SetVariationParameter("scheduler_trigger_types", "-");
ResetProvider();
// Then enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
scheduler()->OnPersistentSchedulerWakeUp();
scheduler()->OnNTPOpened();
scheduler()->OnBrowserForegrounded();
scheduler()->OnBrowserColdStart();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldHandleEmptyParamForTriggerTypes) {
// First set an empty param for allowed trigger types -> should result in the
// default list.
SetVariationParameter("scheduler_trigger_types", "");
ResetProvider();
// Then enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// For instance, persistent scheduler wake up should be enabled by default.
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnPersistentSchedulerWakeUp();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldHandleIncorrentParamForTriggerTypes) {
// First set an invalid list of allowed trigger types.
SetVariationParameter("scheduler_trigger_types", "ntp_opened,foo;");
ResetProvider();
// Then enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// For instance, persistent scheduler wake up should be enabled by default.
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnPersistentSchedulerWakeUp();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldFetchOnPersistentSchedulerWakeUp) {
// First set only this type to be allowed.
SetVariationParameter("scheduler_trigger_types",
"persistent_scheduler_wake_up");
ResetProvider();
// Then enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnPersistentSchedulerWakeUp();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldFetchOnPersistentSchedulerWakeUpRepeated) {
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
{
InSequence s;
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
}
// First enable the scheduler -- calling Schedule() for the first time.
ActivateProvider();
// Make the first persistent fetch successful -- calling Schedule() again.
scheduler()->OnPersistentSchedulerWakeUp();
signal_fetch_done.Run(Status::Success());
// Make the second fetch.
scheduler()->OnPersistentSchedulerWakeUp();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldNotTriggerBackgroundFetchIfAlreadyInProgess) {
{
InSequence s;
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
// RefetchInTheBackground is not called after the second trigger.
}
// First enable the scheduler -- calling Schedule() for the first time.
ActivateProvider();
// Make the first persistent fetch never finish.
scheduler()->OnPersistentSchedulerWakeUp();
// Make the second fetch.
scheduler()->OnPersistentSchedulerWakeUp();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldFetchOnNTPOpenedForTheFirstTime) {
// First set only this type to be allowed.
SetVariationParameter("scheduler_trigger_types", "ntp_opened");
ResetProvider();
// Then enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldFetchOnBrowserForegroundedForTheFirstTime) {
// First set only this type to be allowed.
SetVariationParameter("scheduler_trigger_types", "browser_foregrounded");
ResetProvider();
// Then enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnBrowserForegrounded();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldFetchOnBrowserColdStartForTheFirstTime) {
// First set only this type to be allowed.
SetVariationParameter("scheduler_trigger_types", "browser_cold_start");
ResetProvider();
// Then enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnBrowserColdStart();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldNotFetchOnNTPOpenedAfterSuccessfulSoftFetch) {
// First enable the scheduler; the second Schedule is called after the
// successful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
ActivateProvider();
// Make the first soft fetch successful.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnNTPOpened();
signal_fetch_done.Run(Status::Success());
// The second call is ignored if it happens right after the first one.
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldNotFetchOnNTPOpenedAfterSuccessfulPersistentFetch) {
// First enable the scheduler; the second Schedule is called after the
// successful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
ActivateProvider();
// Make the first persistent fetch successful.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnPersistentSchedulerWakeUp();
signal_fetch_done.Run(Status::Success());
// The second call is ignored if it happens right after the first one.
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldNotFetchOnNTPOpenedAfterFailedSoftFetch) {
// First enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// Make the first soft fetch failed.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnNTPOpened();
signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
// The second call is ignored if it happens right after the first one.
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldNotFetchOnNTPOpenedAfterFailedPersistentFetch) {
// First enable the scheduler.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// Make the first persistent fetch failed.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnPersistentSchedulerWakeUp();
signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
// The second call is ignored if it happens right after the first one.
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldFetchAgainOnBrowserForgroundLaterAgain) {
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
{
InSequence s;
// Initial scheduling after being enabled.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
// The first call to NTPOpened results in a fetch.
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
// The second call to NTPOpened 4hrs later again results in a fetch.
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
}
// First enable the scheduler.
ActivateProvider();
// Make the first soft fetch successful.
scheduler()->OnBrowserForegrounded();
signal_fetch_done.Run(Status::Success());
// Open NTP again after 4hrs.
test_clock()->Advance(base::TimeDelta::FromHours(4));
scheduler()->OnBrowserForegrounded();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldRescheduleOnRescheduleFetching) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
scheduler()->RescheduleFetching();
}
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldScheduleOnActivation) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldUnscheduleOnLaterInactivation) {
{
InSequence s;
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
EXPECT_CALL(*persistent_scheduler(), Unschedule());
}
ActivateProvider();
DeactivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldScheduleOnLaterActivation) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
// There is no schedule yet, so inactivation does not trigger unschedule.
DeactivateProvider();
ActivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldRescheduleAfterSuccessfulFetch) {
// First reschedule on becoming active.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
ActivateProvider();
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
// Trigger a fetch.
scheduler()->OnPersistentSchedulerWakeUp();
// Second reschedule after a successful fetch.
signal_fetch_done.Run(Status::Success());
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldNotRescheduleAfterFailedFetch) {
// Only reschedule on becoming active.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
// Trigger a fetch.
scheduler()->OnPersistentSchedulerWakeUp();
// No furter reschedule after a failure.
signal_fetch_done.Run(Status(StatusCode::PERMANENT_ERROR, ""));
}
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldScheduleOnlyOnce) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// No further call to Schedule on a second status callback.
ActivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldUnscheduleOnlyOnce) {
{
InSequence s;
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
EXPECT_CALL(*persistent_scheduler(), Unschedule());
}
// First schedule so that later we really unschedule.
ActivateProvider();
DeactivateProvider();
// No further call to Unschedule on second status callback.
DeactivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ReschedulesWhenPersistentWifiParamChanges) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
ActivateProvider();
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
// null. Change the wifi interval for this class.
SetVariationParameter("fetching_interval_hours-wifi-active_ntp_user", "1.5");
// Schedule() should get called for the second time after params have changed.
ActivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ReschedulesWhenPersistentFallbackParamChanges) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
ActivateProvider();
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
// null. Change the fallback interval for this class.
SetVariationParameter("fetching_interval_hours-fallback-active_ntp_user",
"1.5");
// Schedule() should get called for the second time after params have changed.
ActivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ReschedulesWhenSoftWifiParamChanges) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
ActivateProvider();
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
// null. Change the on usage interval for this class.
SetVariationParameter("soft_fetching_interval_hours-wifi-active_ntp_user",
"1.5");
// Schedule() should get called for the second time after params have changed.
ActivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ReschedulesWhenSoftFallbackParamChanges) {
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
ActivateProvider();
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
// null. Change the fallback interval for this class.
SetVariationParameter("soft_fetching_interval_hours-fallback-active_ntp_user",
"1.5");
// Schedule() should get called for the second time after params have changed.
ActivateProvider();
}
TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForSoftTriggerOnWifi) {
// Pretend we are on WiFi (already done in ctor, we make it explicit here).
EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
.WillRepeatedly(Return(true));
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 3h time
// interval by default for soft background fetches on WiFi.
// Initial scheduling after being enabled.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// The first call to NTPOpened results in a fetch.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnNTPOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Open NTP again after too short delay. This time no fetch is executed.
test_clock()->Advance(base::TimeDelta::FromMinutes(30));
scheduler()->OnNTPOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
test_clock()->Advance(base::TimeDelta::FromMinutes(150));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
OverrideFetchIntervalForSoftTriggerOnWifi) {
// Pretend we are on WiFi (already done in ctor, we make it explicit here).
EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
.WillRepeatedly(Return(true));
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
// null. Change the on usage interval for this class from 2h to 30min.
SetVariationParameter("soft_fetching_interval_hours-wifi-active_ntp_user",
"0.5");
// Initial scheduling after being enabled.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// The first call to NTPOpened results in a fetch.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnNTPOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Open NTP again after too short delay. This time no fetch is executed.
test_clock()->Advance(base::TimeDelta::FromMinutes(20));
scheduler()->OnNTPOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
test_clock()->Advance(base::TimeDelta::FromMinutes(10));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
FetchIntervalForSoftTriggerOnFallback) {
// Pretend we are not on wifi -> fallback connection.
EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
.WillRepeatedly(Return(false));
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 6h time
// interval by default for soft background fetches not on WiFi.
// Initial scheduling after being enabled.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// The first call to NTPOpened results in a fetch.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnNTPOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Open NTP again after too short delay. This time no fetch is executed.
test_clock()->Advance(base::TimeDelta::FromMinutes(300));
scheduler()->OnNTPOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
test_clock()->Advance(base::TimeDelta::FromMinutes(60));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
OverrideFetchIntervalForSoftTriggerOnFallback) {
// Pretend we are not on wifi -> fallback connection.
EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
.WillRepeatedly(Return(false));
// UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
// null. Change the on usage interval for this class from 4h to 30min.
SetVariationParameter("soft_fetching_interval_hours-fallback-active_ntp_user",
"0.5");
// Initial scheduling after being enabled.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// The first call to NTPOpened results in a fetch.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnNTPOpened();
// Rescheduling after a succesful fetch.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Open NTP again after too short delay. This time no fetch is executed.
test_clock()->Advance(base::TimeDelta::FromMinutes(20));
scheduler()->OnNTPOpened();
// Open NTP after another delay, now together long enough to issue a fetch.
test_clock()->Advance(base::TimeDelta::FromMinutes(10));
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
scheduler()->OnNTPOpened();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldBlockFetchingForSomeTimeAfterHistoryCleared) {
// First enable the scheduler -- this will trigger the persistent scheduling.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// Clear the history.
scheduler()->OnHistoryCleared();
// A trigger after 15 minutes is ignored.
test_clock()->Advance(base::TimeDelta::FromMinutes(15));
scheduler()->OnBrowserForegrounded();
// A trigger after another 16 minutes is performed (more than 30m after
// clearing the history).
EXPECT_CALL(*provider(), RefetchInTheBackground(_));
test_clock()->Advance(base::TimeDelta::FromMinutes(16));
scheduler()->OnBrowserForegrounded();
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldImmediatelyFetchAfterSuggestionsCleared) {
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
// First enable the scheduler -- this will trigger the persistent scheduling.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
ActivateProvider();
// The first trigger results in a fetch.
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.WillOnce(SaveArg<0>(&signal_fetch_done));
scheduler()->OnBrowserForegrounded();
// Make the fetch successful -- this results in rescheduling.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
signal_fetch_done.Run(Status::Success());
// Clear the suggestions - results in an immediate fetch.
EXPECT_CALL(*provider(), ReloadSuggestions());
scheduler()->OnSuggestionsCleared();
}
TEST_F(RemoteSuggestionsSchedulerImplTest, ShouldThrottleInteractiveRequests) {
// Change the quota for interactive requests ("active NTP user" is the default
// class in tests).
SetVariationParameter("interactive_quota_SuggestionFetcherActiveNTPUser",
"10");
ResetProvider();
for (int x = 0; x < 10; ++x) {
EXPECT_THAT(scheduler()->AcquireQuotaForInteractiveFetch(), Eq(true));
}
// Now the quota is over.
EXPECT_THAT(scheduler()->AcquireQuotaForInteractiveFetch(), Eq(false));
}
TEST_F(RemoteSuggestionsSchedulerImplTest,
ShouldThrottleNonInteractiveRequests) {
// Change the quota for interactive requests ("active NTP user" is the default
// class in tests).
SetVariationParameter("quota_SuggestionFetcherActiveNTPUser", "5");
ResetProvider();
// One scheduling on start, 5 times after successful fetches.
EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(6);
// First enable the scheduler -- this will trigger the persistent scheduling.
ActivateProvider();
// As long as the quota suffices, the call gets through.
RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
EXPECT_CALL(*provider(), RefetchInTheBackground(_))
.Times(5)
.WillRepeatedly(SaveArg<0>(&signal_fetch_done));
for (int x = 0; x < 5; ++x) {
scheduler()->OnPersistentSchedulerWakeUp();
signal_fetch_done.Run(Status::Success());
}
// For the 6th time, it is blocked by the scheduling provider.
scheduler()->OnPersistentSchedulerWakeUp();
}
} // namespace ntp_snippets