| Index: components/sync/engine_impl/sync_scheduler_unittest.cc
|
| diff --git a/components/sync/engine_impl/sync_scheduler_unittest.cc b/components/sync/engine_impl/sync_scheduler_unittest.cc
|
| deleted file mode 100644
|
| index f996bf145ad5dafd195e1bf3f832254e943dc9d5..0000000000000000000000000000000000000000
|
| --- a/components/sync/engine_impl/sync_scheduler_unittest.cc
|
| +++ /dev/null
|
| @@ -1,1438 +0,0 @@
|
| -// Copyright (c) 2012 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 <stddef.h>
|
| -#include <stdint.h>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/callback.h"
|
| -#include "base/compiler_specific.h"
|
| -#include "base/location.h"
|
| -#include "base/memory/weak_ptr.h"
|
| -#include "base/run_loop.h"
|
| -#include "base/single_thread_task_runner.h"
|
| -#include "base/test/test_timeouts.h"
|
| -#include "base/threading/thread_task_runner_handle.h"
|
| -#include "components/sync/base/cancelation_signal.h"
|
| -#include "components/sync/base/extensions_activity.h"
|
| -#include "components/sync/base/model_type_test_util.h"
|
| -#include "components/sync/engine_impl/backoff_delay_provider.h"
|
| -#include "components/sync/engine_impl/cycle/test_util.h"
|
| -#include "components/sync/engine_impl/sync_scheduler_impl.h"
|
| -#include "components/sync/engine_impl/syncer.h"
|
| -#include "components/sync/test/callback_counter.h"
|
| -#include "components/sync/test/engine/fake_model_worker.h"
|
| -#include "components/sync/test/engine/mock_connection_manager.h"
|
| -#include "components/sync/test/engine/mock_nudge_handler.h"
|
| -#include "components/sync/test/engine/test_directory_setter_upper.h"
|
| -#include "components/sync/test/mock_invalidation.h"
|
| -#include "testing/gmock/include/gmock/gmock.h"
|
| -#include "testing/gtest/include/gtest/gtest.h"
|
| -
|
| -using base::TimeDelta;
|
| -using base::TimeTicks;
|
| -using testing::_;
|
| -using testing::AtLeast;
|
| -using testing::DoAll;
|
| -using testing::Invoke;
|
| -using testing::Mock;
|
| -using testing::Return;
|
| -using testing::WithArg;
|
| -using testing::WithArgs;
|
| -using testing::WithoutArgs;
|
| -
|
| -namespace syncer {
|
| -
|
| -using sync_pb::GetUpdatesCallerInfo;
|
| -
|
| -class MockSyncer : public Syncer {
|
| - public:
|
| - MockSyncer();
|
| - MOCK_METHOD3(NormalSyncShare, bool(ModelTypeSet, NudgeTracker*, SyncCycle*));
|
| - MOCK_METHOD3(ConfigureSyncShare,
|
| - bool(ModelTypeSet,
|
| - sync_pb::GetUpdatesCallerInfo::GetUpdatesSource,
|
| - SyncCycle*));
|
| - MOCK_METHOD2(PollSyncShare, bool(ModelTypeSet, SyncCycle*));
|
| -};
|
| -
|
| -MockSyncer::MockSyncer() : Syncer(NULL) {}
|
| -
|
| -typedef std::vector<TimeTicks> SyncShareTimes;
|
| -
|
| -void QuitLoopNow() {
|
| - // We use QuitNow() instead of Quit() as the latter may get stalled
|
| - // indefinitely in the presence of repeated timers with low delays
|
| - // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll
|
| - // delay of 5ms] run under TSAN on the trybots).
|
| - base::MessageLoop::current()->QuitNow();
|
| -}
|
| -
|
| -void RunLoop() {
|
| - base::RunLoop().Run();
|
| -}
|
| -
|
| -void PumpLoop() {
|
| - // Do it this way instead of RunAllPending to pump loop exactly once
|
| - // (necessary in the presence of timers; see comment in
|
| - // QuitLoopNow).
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
|
| - base::Bind(&QuitLoopNow));
|
| - RunLoop();
|
| -}
|
| -
|
| -void PumpLoopFor(base::TimeDelta time) {
|
| - // Allow the loop to run for the specified amount of time.
|
| - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| - FROM_HERE, base::Bind(&QuitLoopNow), time);
|
| - RunLoop();
|
| -}
|
| -
|
| -ModelSafeRoutingInfo TypesToRoutingInfo(ModelTypeSet types) {
|
| - ModelSafeRoutingInfo routes;
|
| - for (ModelTypeSet::Iterator iter = types.First(); iter.Good(); iter.Inc()) {
|
| - routes[iter.Get()] = GROUP_PASSIVE;
|
| - }
|
| - return routes;
|
| -}
|
| -
|
| -static const size_t kMinNumSamples = 5;
|
| -
|
| -// Test harness for the SyncScheduler. Test the delays and backoff timers used
|
| -// in response to various events.
|
| -//
|
| -// These tests execute in real time with real timers. We try to keep the
|
| -// delays short, but there is a limit to how short we can make them. The
|
| -// timers on some platforms (ie. Windows) have a timer resolution greater than
|
| -// 1ms. Using 1ms delays may result in test flakiness.
|
| -//
|
| -// See crbug.com/402212 for more info.
|
| -class SyncSchedulerTest : public testing::Test {
|
| - public:
|
| - SyncSchedulerTest() : syncer_(NULL), delay_(NULL), weak_ptr_factory_(this) {}
|
| -
|
| - class MockDelayProvider : public BackoffDelayProvider {
|
| - public:
|
| - MockDelayProvider()
|
| - : BackoffDelayProvider(
|
| - TimeDelta::FromSeconds(kInitialBackoffRetrySeconds),
|
| - TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds)) {}
|
| -
|
| - MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&));
|
| - };
|
| -
|
| - void SetUp() override {
|
| - dir_maker_.SetUp();
|
| - syncer_ = new testing::StrictMock<MockSyncer>();
|
| - delay_ = NULL;
|
| - extensions_activity_ = new ExtensionsActivity();
|
| -
|
| - routing_info_[THEMES] = GROUP_UI;
|
| - routing_info_[TYPED_URLS] = GROUP_DB;
|
| - routing_info_[THEMES] = GROUP_UI;
|
| - routing_info_[NIGORI] = GROUP_PASSIVE;
|
| -
|
| - workers_.clear();
|
| - workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_UI)));
|
| - workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_DB)));
|
| - workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_PASSIVE)));
|
| -
|
| - connection_.reset(
|
| - new MockConnectionManager(directory(), &cancelation_signal_));
|
| - connection_->SetServerReachable();
|
| -
|
| - model_type_registry_.reset(
|
| - new ModelTypeRegistry(workers_, directory(), &mock_nudge_handler_));
|
| -
|
| - context_.reset(new SyncCycleContext(
|
| - connection_.get(), directory(), extensions_activity_.get(),
|
| - std::vector<SyncEngineEventListener*>(), NULL,
|
| - model_type_registry_.get(),
|
| - true, // enable keystore encryption
|
| - false, // force enable pre-commit GU avoidance
|
| - "fake_invalidator_client_id"));
|
| - context_->SetRoutingInfo(routing_info_);
|
| - context_->set_notifications_enabled(true);
|
| - context_->set_account_name("Test");
|
| - scheduler_.reset(new SyncSchedulerImpl("TestSyncScheduler",
|
| - BackoffDelayProvider::FromDefaults(),
|
| - context(), syncer_));
|
| - scheduler_->SetDefaultNudgeDelay(default_delay());
|
| - }
|
| -
|
| - SyncSchedulerImpl* scheduler() { return scheduler_.get(); }
|
| - const ModelSafeRoutingInfo& routing_info() { return routing_info_; }
|
| - MockSyncer* syncer() { return syncer_; }
|
| - MockDelayProvider* delay() { return delay_; }
|
| - MockConnectionManager* connection() { return connection_.get(); }
|
| - TimeDelta default_delay() { return TimeDelta::FromSeconds(0); }
|
| - TimeDelta timeout() { return TestTimeouts::action_timeout(); }
|
| -
|
| - void TearDown() override {
|
| - PumpLoop();
|
| - scheduler_.reset();
|
| - PumpLoop();
|
| - dir_maker_.TearDown();
|
| - }
|
| -
|
| - void AnalyzePollRun(const SyncShareTimes& times,
|
| - size_t min_num_samples,
|
| - const TimeTicks& optimal_start,
|
| - const TimeDelta& poll_interval) {
|
| - EXPECT_GE(times.size(), min_num_samples);
|
| - for (size_t i = 0; i < times.size(); i++) {
|
| - SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
|
| - TimeTicks optimal_next_sync = optimal_start + poll_interval * i;
|
| - EXPECT_GE(times[i], optimal_next_sync);
|
| - }
|
| - }
|
| -
|
| - void DoQuitLoopNow() { QuitLoopNow(); }
|
| -
|
| - void StartSyncConfiguration() {
|
| - scheduler()->Start(SyncScheduler::CONFIGURATION_MODE, base::Time());
|
| - }
|
| -
|
| - void StartSyncScheduler(base::Time last_poll_time) {
|
| - scheduler()->Start(SyncScheduler::NORMAL_MODE, last_poll_time);
|
| - }
|
| -
|
| - // This stops the scheduler synchronously.
|
| - void StopSyncScheduler() {
|
| - base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| - FROM_HERE, base::Bind(&SyncSchedulerTest::DoQuitLoopNow,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - RunLoop();
|
| - }
|
| -
|
| - bool RunAndGetBackoff() {
|
| - ModelTypeSet nudge_types(THEMES);
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
|
| - RunLoop();
|
| -
|
| - return scheduler()->IsBackingOff();
|
| - }
|
| -
|
| - void UseMockDelayProvider() {
|
| - delay_ = new MockDelayProvider();
|
| - scheduler_->delay_provider_.reset(delay_);
|
| - }
|
| -
|
| - SyncCycleContext* context() { return context_.get(); }
|
| -
|
| - ModelTypeSet GetThrottledTypes() {
|
| - return scheduler_->nudge_tracker_.GetThrottledTypes();
|
| - }
|
| -
|
| - base::TimeDelta GetRetryTimerDelay() {
|
| - EXPECT_TRUE(scheduler_->retry_timer_.IsRunning());
|
| - return scheduler_->retry_timer_.GetCurrentDelay();
|
| - }
|
| -
|
| - static std::unique_ptr<InvalidationInterface> BuildInvalidation(
|
| - int64_t version,
|
| - const std::string& payload) {
|
| - return MockInvalidation::Build(version, payload);
|
| - }
|
| -
|
| - private:
|
| - syncable::Directory* directory() { return dir_maker_.directory(); }
|
| -
|
| - base::MessageLoop loop_;
|
| - TestDirectorySetterUpper dir_maker_;
|
| - CancelationSignal cancelation_signal_;
|
| - std::unique_ptr<MockConnectionManager> connection_;
|
| - std::unique_ptr<ModelTypeRegistry> model_type_registry_;
|
| - std::unique_ptr<SyncCycleContext> context_;
|
| - std::unique_ptr<SyncSchedulerImpl> scheduler_;
|
| - MockNudgeHandler mock_nudge_handler_;
|
| - MockSyncer* syncer_;
|
| - MockDelayProvider* delay_;
|
| - std::vector<scoped_refptr<ModelSafeWorker>> workers_;
|
| - scoped_refptr<ExtensionsActivity> extensions_activity_;
|
| - ModelSafeRoutingInfo routing_info_;
|
| - base::WeakPtrFactory<SyncSchedulerTest> weak_ptr_factory_;
|
| -};
|
| -
|
| -void RecordSyncShareImpl(SyncShareTimes* times) {
|
| - times->push_back(TimeTicks::Now());
|
| -}
|
| -
|
| -ACTION_P2(RecordSyncShare, times, success) {
|
| - RecordSyncShareImpl(times);
|
| - if (base::MessageLoop::current()->is_running())
|
| - QuitLoopNow();
|
| - return success;
|
| -}
|
| -
|
| -ACTION_P3(RecordSyncShareMultiple, times, quit_after, success) {
|
| - RecordSyncShareImpl(times);
|
| - EXPECT_LE(times->size(), quit_after);
|
| - if (times->size() >= quit_after &&
|
| - base::MessageLoop::current()->is_running()) {
|
| - QuitLoopNow();
|
| - }
|
| - return success;
|
| -}
|
| -
|
| -ACTION_P(StopScheduler, scheduler) {
|
| - scheduler->Stop();
|
| -}
|
| -
|
| -ACTION(AddFailureAndQuitLoopNow) {
|
| - ADD_FAILURE();
|
| - QuitLoopNow();
|
| - return true;
|
| -}
|
| -
|
| -ACTION_P(QuitLoopNowAction, success) {
|
| - QuitLoopNow();
|
| - return success;
|
| -}
|
| -
|
| -// Test nudge scheduling.
|
| -TEST_F(SyncSchedulerTest, Nudge) {
|
| - SyncShareTimes times;
|
| - ModelTypeSet model_types(THEMES);
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)))
|
| - .RetiresOnSaturation();
|
| -
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
|
| - RunLoop();
|
| -
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - // Make sure a second, later, nudge is unaffected by first (no coalescing).
|
| - SyncShareTimes times2;
|
| - model_types.Remove(THEMES);
|
| - model_types.Put(TYPED_URLS);
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×2, true)));
|
| - scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
|
| - RunLoop();
|
| -}
|
| -
|
| -// Make sure a regular config command is scheduled fine in the absence of any
|
| -// errors.
|
| -TEST_F(SyncSchedulerTest, Config) {
|
| - SyncShareTimes times;
|
| - const ModelTypeSet model_types(THEMES);
|
| -
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureSuccess),
|
| - RecordSyncShare(×, true)));
|
| -
|
| - StartSyncConfiguration();
|
| -
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, model_types,
|
| - TypesToRoutingInfo(model_types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - PumpLoop();
|
| - ASSERT_EQ(1, ready_counter.times_called());
|
| - ASSERT_EQ(0, retry_counter.times_called());
|
| -}
|
| -
|
| -// Simulate a failure and make sure the config request is retried.
|
| -TEST_F(SyncSchedulerTest, ConfigWithBackingOff) {
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(20)));
|
| - SyncShareTimes times;
|
| - const ModelTypeSet model_types(THEMES);
|
| -
|
| - StartSyncConfiguration();
|
| -
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureFailed),
|
| - RecordSyncShare(×, false)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureFailed),
|
| - RecordSyncShare(×, false)));
|
| -
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, model_types,
|
| - TypesToRoutingInfo(model_types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - RunLoop();
|
| - ASSERT_EQ(0, ready_counter.times_called());
|
| - ASSERT_EQ(1, retry_counter.times_called());
|
| -
|
| - // RunLoop() will trigger TryCanaryJob which will retry configuration.
|
| - // Since retry_task was already called it shouldn't be called again.
|
| - RunLoop();
|
| - ASSERT_EQ(0, ready_counter.times_called());
|
| - ASSERT_EQ(1, retry_counter.times_called());
|
| -
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureSuccess),
|
| - RecordSyncShare(×, true)));
|
| - RunLoop();
|
| -
|
| - ASSERT_EQ(1, ready_counter.times_called());
|
| -}
|
| -
|
| -// Simuilate SyncSchedulerImpl::Stop being called in the middle of Configure.
|
| -// This can happen if server returns NOT_MY_BIRTHDAY.
|
| -TEST_F(SyncSchedulerTest, ConfigWithStop) {
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(20)));
|
| - SyncShareTimes times;
|
| - const ModelTypeSet model_types(THEMES);
|
| -
|
| - StartSyncConfiguration();
|
| -
|
| - // Make ConfigureSyncShare call scheduler->Stop(). It is not supposed to call
|
| - // retry_task or dereference configuration params.
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureFailed),
|
| - StopScheduler(scheduler()),
|
| - RecordSyncShare(×, false)));
|
| -
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, model_types,
|
| - TypesToRoutingInfo(model_types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - PumpLoop();
|
| - ASSERT_EQ(0, ready_counter.times_called());
|
| - ASSERT_EQ(0, retry_counter.times_called());
|
| -}
|
| -
|
| -// Issue a nudge when the config has failed. Make sure both the config and
|
| -// nudge are executed.
|
| -TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) {
|
| - const ModelTypeSet model_types(THEMES);
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50)));
|
| - SyncShareTimes times;
|
| -
|
| - StartSyncConfiguration();
|
| -
|
| - // Request a configure and make sure it fails.
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureFailed),
|
| - RecordSyncShare(×, false)));
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, model_types,
|
| - TypesToRoutingInfo(model_types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - RunLoop();
|
| - ASSERT_EQ(0, ready_counter.times_called());
|
| - ASSERT_EQ(1, retry_counter.times_called());
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - // Ask for a nudge while dealing with repeated configure failure.
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureFailed),
|
| - RecordSyncShare(×, false)));
|
| - scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
|
| - RunLoop();
|
| - // Note that we're not RunLoop()ing for the NUDGE we just scheduled, but
|
| - // for the first retry attempt from the config job (after
|
| - // waiting ~+/- 50ms).
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| - ASSERT_EQ(0, ready_counter.times_called());
|
| -
|
| - // Let the next configure retry succeed.
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureSuccess),
|
| - RecordSyncShare(×, true)));
|
| - RunLoop();
|
| -
|
| - // Now change the mode so nudge can execute.
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| - StartSyncScheduler(base::Time());
|
| - PumpLoop();
|
| -}
|
| -
|
| -// Test that nudges are coalesced.
|
| -TEST_F(SyncSchedulerTest, NudgeCoalescing) {
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - SyncShareTimes times;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| - const ModelTypeSet types1(THEMES), types2(TYPED_URLS), types3(THEMES);
|
| - TimeTicks optimal_time = TimeTicks::Now() + default_delay();
|
| - scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
|
| - scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
|
| - RunLoop();
|
| -
|
| - ASSERT_EQ(1U, times.size());
|
| - EXPECT_GE(times[0], optimal_time);
|
| -
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - SyncShareTimes times2;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×2, true)));
|
| - scheduler()->ScheduleLocalNudge(types3, FROM_HERE);
|
| - RunLoop();
|
| -}
|
| -
|
| -// Test that nudges are coalesced.
|
| -TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) {
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - SyncShareTimes times;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| - ModelTypeSet types1(THEMES), types2(TYPED_URLS), types3;
|
| -
|
| - // Create a huge time delay.
|
| - TimeDelta delay = TimeDelta::FromDays(1);
|
| -
|
| - std::map<ModelType, TimeDelta> delay_map;
|
| - delay_map[types1.First().Get()] = delay;
|
| - scheduler()->OnReceivedCustomNudgeDelays(delay_map);
|
| - scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
|
| - scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
|
| -
|
| - TimeTicks min_time = TimeTicks::Now();
|
| - TimeTicks max_time = TimeTicks::Now() + delay;
|
| -
|
| - RunLoop();
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - // Make sure the sync happened at the right time.
|
| - ASSERT_EQ(1U, times.size());
|
| - EXPECT_GE(times[0], min_time);
|
| - EXPECT_LE(times[0], max_time);
|
| -}
|
| -
|
| -// Test nudge scheduling.
|
| -TEST_F(SyncSchedulerTest, NudgeWithStates) {
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - SyncShareTimes times1;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×1, true)))
|
| - .RetiresOnSaturation();
|
| - scheduler()->ScheduleInvalidationNudge(THEMES, BuildInvalidation(10, "test"),
|
| - FROM_HERE);
|
| - RunLoop();
|
| -
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - // Make sure a second, later, nudge is unaffected by first (no coalescing).
|
| - SyncShareTimes times2;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×2, true)));
|
| - scheduler()->ScheduleInvalidationNudge(
|
| - TYPED_URLS, BuildInvalidation(10, "test2"), FROM_HERE);
|
| - RunLoop();
|
| -}
|
| -
|
| -// Test that polling works as expected.
|
| -TEST_F(SyncSchedulerTest, Polling) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .Times(AtLeast(kMinNumSamples))
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, true)));
|
| -
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
|
| -
|
| - TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Run again to wait for polling.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| - AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
|
| -}
|
| -
|
| -// Test that we reuse the previous poll time on startup, triggering the first
|
| -// poll based on when the last one happened. Subsequent polls should have the
|
| -// normal delay.
|
| -TEST_F(SyncSchedulerTest, PollingPersistence) {
|
| - SyncShareTimes times;
|
| - // Use a large poll interval that wouldn't normally get hit on its own for
|
| - // some time yet.
|
| - TimeDelta poll_interval(TimeDelta::FromMilliseconds(500));
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .Times(AtLeast(kMinNumSamples))
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, true)));
|
| -
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
|
| -
|
| - // Set the start time to now, as the poll was overdue.
|
| - TimeTicks optimal_start = TimeTicks::Now();
|
| - StartSyncScheduler(base::Time::Now() - poll_interval);
|
| -
|
| - // Run again to wait for polling.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| - AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
|
| -}
|
| -
|
| -// Test that if the persisted poll is in the future, it's ignored (the case
|
| -// where the local time has been modified).
|
| -TEST_F(SyncSchedulerTest, PollingPersistenceBadClock) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .Times(AtLeast(kMinNumSamples))
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, true)));
|
| -
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
|
| -
|
| - // Set the start time to |poll_interval| in the future.
|
| - TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
|
| - StartSyncScheduler(base::Time::Now() + base::TimeDelta::FromMinutes(10));
|
| -
|
| - // Run again to wait for polling.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| - AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
|
| -}
|
| -
|
| -// Test that the short poll interval is used.
|
| -TEST_F(SyncSchedulerTest, PollNotificationsDisabled) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .Times(AtLeast(kMinNumSamples))
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, true)));
|
| -
|
| - scheduler()->OnReceivedShortPollIntervalUpdate(poll_interval);
|
| - scheduler()->SetNotificationsEnabled(false);
|
| -
|
| - TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Run again to wait for polling.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| - AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
|
| -}
|
| -
|
| -// Test that polling intervals are updated when needed.
|
| -TEST_F(SyncSchedulerTest, PollIntervalUpdate) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll1(TimeDelta::FromMilliseconds(120));
|
| - TimeDelta poll2(TimeDelta::FromMilliseconds(30));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll1);
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .Times(AtLeast(kMinNumSamples))
|
| - .WillOnce(
|
| - DoAll(WithArgs<0, 1>(test_util::SimulatePollIntervalUpdate(poll2)),
|
| - Return(true)))
|
| - .WillRepeatedly(DoAll(
|
| - Invoke(test_util::SimulatePollSuccess),
|
| - WithArg<1>(RecordSyncShareMultiple(×, kMinNumSamples, true))));
|
| -
|
| - TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2;
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Run again to wait for polling.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| - AnalyzePollRun(times, kMinNumSamples, optimal_start, poll2);
|
| -}
|
| -
|
| -// Test that no syncing occurs when throttled.
|
| -TEST_F(SyncSchedulerTest, ThrottlingDoesThrottle) {
|
| - const ModelTypeSet types(THEMES);
|
| - TimeDelta poll(TimeDelta::FromMilliseconds(20));
|
| - TimeDelta throttle(TimeDelta::FromMinutes(10));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(WithArg<2>(test_util::SimulateThrottled(throttle)),
|
| - Return(false)))
|
| - .WillRepeatedly(AddFailureAndQuitLoopNow());
|
| -
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - scheduler()->ScheduleLocalNudge(types, FROM_HERE);
|
| - PumpLoop();
|
| -
|
| - StartSyncConfiguration();
|
| -
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, types, TypesToRoutingInfo(types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - PumpLoop();
|
| - ASSERT_EQ(0, ready_counter.times_called());
|
| - ASSERT_EQ(1, retry_counter.times_called());
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, ThrottlingExpiresFromPoll) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll(TimeDelta::FromMilliseconds(15));
|
| - TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - ::testing::InSequence seq;
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .WillOnce(DoAll(WithArg<1>(test_util::SimulateThrottled(throttle1)),
|
| - Return(false)))
|
| - .RetiresOnSaturation();
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, true)));
|
| -
|
| - TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1;
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Run again to wait for polling.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| - AnalyzePollRun(times, kMinNumSamples, optimal_start, poll);
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, ThrottlingExpiresFromNudge) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll(TimeDelta::FromDays(1));
|
| - TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - ::testing::InSequence seq;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(WithArg<2>(test_util::SimulateThrottled(throttle1)),
|
| - Return(false)))
|
| - .RetiresOnSaturation();
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - QuitLoopNowAction(true)));
|
| -
|
| - const ModelTypeSet types(THEMES);
|
| - StartSyncScheduler(base::Time());
|
| - scheduler()->ScheduleLocalNudge(types, FROM_HERE);
|
| -
|
| - PumpLoop(); // To get PerformDelayedNudge called.
|
| - PumpLoop(); // To get TrySyncCycleJob called
|
| - EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
|
| - RunLoop();
|
| - EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, ThrottlingExpiresFromConfigure) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll(TimeDelta::FromDays(1));
|
| - TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - ::testing::InSequence seq;
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(WithArg<2>(test_util::SimulateThrottled(throttle1)),
|
| - Return(false)))
|
| - .RetiresOnSaturation();
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureSuccess),
|
| - QuitLoopNowAction(true)));
|
| -
|
| - const ModelTypeSet types(THEMES);
|
| - StartSyncConfiguration();
|
| -
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, types, TypesToRoutingInfo(types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - PumpLoop();
|
| - EXPECT_EQ(0, ready_counter.times_called());
|
| - EXPECT_EQ(1, retry_counter.times_called());
|
| - EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
|
| -
|
| - RunLoop();
|
| - EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, TypeThrottlingBlocksNudge) {
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_)).WillRepeatedly(Return(default_delay()));
|
| -
|
| - TimeDelta poll(TimeDelta::FromDays(1));
|
| - TimeDelta throttle1(TimeDelta::FromSeconds(60));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - const ModelTypeSet types(THEMES);
|
| -
|
| - ::testing::InSequence seq;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(
|
| - DoAll(WithArg<2>(test_util::SimulateTypesThrottled(types, throttle1)),
|
| - Return(false)))
|
| - .RetiresOnSaturation();
|
| -
|
| - StartSyncScheduler(base::Time());
|
| - scheduler()->ScheduleLocalNudge(types, FROM_HERE);
|
| - PumpLoop(); // To get PerformDelayedNudge called.
|
| - PumpLoop(); // To get TrySyncCycleJob called
|
| - EXPECT_TRUE(GetThrottledTypes().HasAll(types));
|
| -
|
| - // This won't cause a sync cycle because the types are throttled.
|
| - scheduler()->ScheduleLocalNudge(types, FROM_HERE);
|
| - PumpLoop();
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, TypeThrottlingDoesBlockOtherSources) {
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_)).WillRepeatedly(Return(default_delay()));
|
| -
|
| - SyncShareTimes times;
|
| - TimeDelta poll(TimeDelta::FromDays(1));
|
| - TimeDelta throttle1(TimeDelta::FromSeconds(60));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - const ModelTypeSet throttled_types(THEMES);
|
| - const ModelTypeSet unthrottled_types(PREFERENCES);
|
| -
|
| - ::testing::InSequence seq;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(WithArg<2>(test_util::SimulateTypesThrottled(
|
| - throttled_types, throttle1)),
|
| - Return(false)))
|
| - .RetiresOnSaturation();
|
| -
|
| - StartSyncScheduler(base::Time());
|
| - scheduler()->ScheduleLocalNudge(throttled_types, FROM_HERE);
|
| - PumpLoop(); // To get PerformDelayedNudge called.
|
| - PumpLoop(); // To get TrySyncCycleJob called
|
| - EXPECT_TRUE(GetThrottledTypes().HasAll(throttled_types));
|
| -
|
| - // Ignore invalidations for throttled types.
|
| - scheduler()->ScheduleInvalidationNudge(THEMES, BuildInvalidation(10, "test"),
|
| - FROM_HERE);
|
| - PumpLoop();
|
| -
|
| - // Ignore refresh requests for throttled types.
|
| - scheduler()->ScheduleLocalRefreshRequest(throttled_types, FROM_HERE);
|
| - PumpLoop();
|
| -
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - // Local nudges for non-throttled types will trigger a sync.
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillRepeatedly(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| - scheduler()->ScheduleLocalNudge(unthrottled_types, FROM_HERE);
|
| - RunLoop();
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -// Test nudges / polls don't run in config mode and config tasks do.
|
| -TEST_F(SyncSchedulerTest, ConfigurationMode) {
|
| - TimeDelta poll(TimeDelta::FromMilliseconds(15));
|
| - SyncShareTimes times;
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - StartSyncConfiguration();
|
| -
|
| - const ModelTypeSet nudge_types(TYPED_URLS);
|
| - scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
|
| - scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
|
| -
|
| - const ModelTypeSet config_types(THEMES);
|
| -
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateConfigureSuccess),
|
| - RecordSyncShare(×, true)))
|
| - .RetiresOnSaturation();
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, config_types,
|
| - TypesToRoutingInfo(config_types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - RunLoop();
|
| - ASSERT_EQ(1, ready_counter.times_called());
|
| - ASSERT_EQ(0, retry_counter.times_called());
|
| -
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - // Switch to NORMAL_MODE to ensure NUDGES were properly saved and run.
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(TimeDelta::FromDays(1));
|
| - SyncShareTimes times2;
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×2, true)));
|
| -
|
| - // TODO(tim): Figure out how to remove this dangerous need to reset
|
| - // routing info between mode switches.
|
| - context()->SetRoutingInfo(routing_info());
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - RunLoop();
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -}
|
| -
|
| -class BackoffTriggersSyncSchedulerTest : public SyncSchedulerTest {
|
| - void SetUp() override {
|
| - SyncSchedulerTest::SetUp();
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(10)));
|
| - }
|
| -
|
| - void TearDown() override {
|
| - StopSyncScheduler();
|
| - SyncSchedulerTest::TearDown();
|
| - }
|
| -};
|
| -
|
| -// Have the syncer fail during commit. Expect that the scheduler enters
|
| -// backoff.
|
| -TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnce) {
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateCommitFailed),
|
| - QuitLoopNowAction(false)));
|
| - EXPECT_TRUE(RunAndGetBackoff());
|
| -}
|
| -
|
| -// Have the syncer fail during download updates and succeed on the first
|
| -// retry. Expect that this clears the backoff state.
|
| -TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadOnceThenSucceed) {
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateDownloadUpdatesFailed),
|
| - Return(false)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - QuitLoopNowAction(true)));
|
| - EXPECT_FALSE(RunAndGetBackoff());
|
| -}
|
| -
|
| -// Have the syncer fail during commit and succeed on the first retry. Expect
|
| -// that this clears the backoff state.
|
| -TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnceThenSucceed) {
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateCommitFailed), Return(false)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - QuitLoopNowAction(true)));
|
| - EXPECT_FALSE(RunAndGetBackoff());
|
| -}
|
| -
|
| -// Have the syncer fail to download updates and fail again on the retry.
|
| -// Expect this will leave the scheduler in backoff.
|
| -TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadTwice) {
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateDownloadUpdatesFailed),
|
| - Return(false)))
|
| - .WillRepeatedly(DoAll(Invoke(test_util::SimulateDownloadUpdatesFailed),
|
| - QuitLoopNowAction(false)));
|
| - EXPECT_TRUE(RunAndGetBackoff());
|
| -}
|
| -
|
| -// Have the syncer fail to get the encryption key yet succeed in downloading
|
| -// updates. Expect this will leave the scheduler in backoff.
|
| -TEST_F(BackoffTriggersSyncSchedulerTest, FailGetEncryptionKey) {
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateGetEncryptionKeyFailed),
|
| - Return(false)))
|
| - .WillRepeatedly(DoAll(Invoke(test_util::SimulateGetEncryptionKeyFailed),
|
| - QuitLoopNowAction(false)));
|
| - StartSyncConfiguration();
|
| -
|
| - ModelTypeSet types(THEMES);
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, types, TypesToRoutingInfo(types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - RunLoop();
|
| -
|
| - EXPECT_TRUE(scheduler()->IsBackingOff());
|
| -}
|
| -
|
| -// Test that no polls or extraneous nudges occur when in backoff.
|
| -TEST_F(SyncSchedulerTest, BackoffDropsJobs) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll(TimeDelta::FromMilliseconds(10));
|
| - const ModelTypeSet types(THEMES);
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| - UseMockDelayProvider();
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateCommitFailed),
|
| - RecordSyncShareMultiple(×, 1U, false)));
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromDays(1)));
|
| -
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // This nudge should fail and put us into backoff. Thanks to our mock
|
| - // GetDelay() setup above, this will be a long backoff.
|
| - scheduler()->ScheduleLocalNudge(types, FROM_HERE);
|
| - RunLoop();
|
| -
|
| - // From this point forward, no SyncShare functions should be invoked.
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| -
|
| - // Wait a while (10x poll interval) so a few poll jobs will be attempted.
|
| - PumpLoopFor(poll * 10);
|
| -
|
| - // Try (and fail) to schedule a nudge.
|
| - scheduler()->ScheduleLocalNudge(types, FROM_HERE);
|
| -
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| - Mock::VerifyAndClearExpectations(delay());
|
| -
|
| - EXPECT_CALL(*delay(), GetDelay(_)).Times(0);
|
| -
|
| - StartSyncConfiguration();
|
| -
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, types, TypesToRoutingInfo(types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| - PumpLoop();
|
| - ASSERT_EQ(0, ready_counter.times_called());
|
| - ASSERT_EQ(1, retry_counter.times_called());
|
| -}
|
| -
|
| -// Test that backoff is shaping traffic properly with consecutive errors.
|
| -TEST_F(SyncSchedulerTest, BackoffElevation) {
|
| - SyncShareTimes times;
|
| - UseMockDelayProvider();
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .Times(kMinNumSamples)
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulateCommitFailed),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, false)));
|
| -
|
| - const TimeDelta first = TimeDelta::FromSeconds(kInitialBackoffRetrySeconds);
|
| - const TimeDelta second = TimeDelta::FromMilliseconds(20);
|
| - const TimeDelta third = TimeDelta::FromMilliseconds(30);
|
| - const TimeDelta fourth = TimeDelta::FromMilliseconds(40);
|
| - const TimeDelta fifth = TimeDelta::FromMilliseconds(50);
|
| - const TimeDelta sixth = TimeDelta::FromDays(1);
|
| -
|
| - EXPECT_CALL(*delay(), GetDelay(first))
|
| - .WillOnce(Return(second))
|
| - .RetiresOnSaturation();
|
| - EXPECT_CALL(*delay(), GetDelay(second))
|
| - .WillOnce(Return(third))
|
| - .RetiresOnSaturation();
|
| - EXPECT_CALL(*delay(), GetDelay(third))
|
| - .WillOnce(Return(fourth))
|
| - .RetiresOnSaturation();
|
| - EXPECT_CALL(*delay(), GetDelay(fourth))
|
| - .WillOnce(Return(fifth))
|
| - .RetiresOnSaturation();
|
| - EXPECT_CALL(*delay(), GetDelay(fifth)).WillOnce(Return(sixth));
|
| -
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Run again with a nudge.
|
| - scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
|
| - RunLoop();
|
| -
|
| - ASSERT_EQ(kMinNumSamples, times.size());
|
| - EXPECT_GE(times[1] - times[0], second);
|
| - EXPECT_GE(times[2] - times[1], third);
|
| - EXPECT_GE(times[3] - times[2], fourth);
|
| - EXPECT_GE(times[4] - times[3], fifth);
|
| -}
|
| -
|
| -// Test that things go back to normal once a retry makes forward progress.
|
| -TEST_F(SyncSchedulerTest, BackoffRelief) {
|
| - SyncShareTimes times;
|
| - UseMockDelayProvider();
|
| -
|
| - const TimeDelta backoff = TimeDelta::FromMilliseconds(10);
|
| - EXPECT_CALL(*delay(), GetDelay(_)).WillOnce(Return(backoff));
|
| -
|
| - // Optimal start for the post-backoff poll party.
|
| - TimeTicks optimal_start = TimeTicks::Now();
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Kick off the test with a failed nudge.
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateCommitFailed),
|
| - RecordSyncShare(×, false)));
|
| - scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
|
| - RunLoop();
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| - TimeTicks optimal_job_time = optimal_start;
|
| - ASSERT_EQ(1U, times.size());
|
| - EXPECT_GE(times[0], optimal_job_time);
|
| -
|
| - // The retry succeeds.
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| - RunLoop();
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| - optimal_job_time = optimal_job_time + backoff;
|
| - ASSERT_EQ(2U, times.size());
|
| - EXPECT_GE(times[1], optimal_job_time);
|
| -
|
| - // Now let the Poll timer do its thing.
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, true)));
|
| - const TimeDelta poll(TimeDelta::FromMilliseconds(10));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - // The new optimal time is now, since the desired poll should have happened
|
| - // in the past.
|
| - optimal_job_time = base::TimeTicks::Now();
|
| - RunLoop();
|
| - Mock::VerifyAndClearExpectations(syncer());
|
| - ASSERT_EQ(kMinNumSamples, times.size());
|
| - for (size_t i = 2; i < times.size(); i++) {
|
| - SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
|
| - EXPECT_GE(times[i], optimal_job_time);
|
| - optimal_job_time = optimal_job_time + poll;
|
| - }
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -// Test that poll failures are treated like any other failure. They should
|
| -// result in retry with backoff.
|
| -TEST_F(SyncSchedulerTest, TransientPollFailure) {
|
| - SyncShareTimes times;
|
| - const TimeDelta poll_interval(TimeDelta::FromMilliseconds(10));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
|
| - UseMockDelayProvider(); // Will cause test failure if backoff is initiated.
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
|
| -
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulatePollFailed),
|
| - RecordSyncShare(×, false)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShare(×, true)));
|
| -
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Run the unsuccessful poll. The failed poll should not trigger backoff.
|
| - RunLoop();
|
| - EXPECT_TRUE(scheduler()->IsBackingOff());
|
| -
|
| - // Run the successful poll.
|
| - RunLoop();
|
| - EXPECT_FALSE(scheduler()->IsBackingOff());
|
| -}
|
| -
|
| -// Test that starting the syncer thread without a valid connection doesn't
|
| -// break things when a connection is detected.
|
| -TEST_F(SyncSchedulerTest, StartWhenNotConnected) {
|
| - connection()->SetServerNotReachable();
|
| - connection()->UpdateConnectionStatus();
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(
|
| - DoAll(Invoke(test_util::SimulateConnectionFailure), Return(false)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess), Return(true)));
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
|
| - // Should save the nudge for until after the server is reachable.
|
| - base::RunLoop().RunUntilIdle();
|
| -
|
| - scheduler()->OnConnectionStatusChange();
|
| - connection()->SetServerReachable();
|
| - connection()->UpdateConnectionStatus();
|
| - base::RunLoop().RunUntilIdle();
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, ServerConnectionChangeDuringBackoff) {
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
|
| -
|
| - StartSyncScheduler(base::Time());
|
| - connection()->SetServerNotReachable();
|
| - connection()->UpdateConnectionStatus();
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(
|
| - DoAll(Invoke(test_util::SimulateConnectionFailure), Return(false)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess), Return(true)));
|
| -
|
| - scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
|
| - PumpLoop(); // To get PerformDelayedNudge called.
|
| - PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
|
| - ASSERT_TRUE(scheduler()->IsBackingOff());
|
| -
|
| - // Before we run the scheduled canary, trigger a server connection change.
|
| - scheduler()->OnConnectionStatusChange();
|
| - connection()->SetServerReachable();
|
| - connection()->UpdateConnectionStatus();
|
| - base::RunLoop().RunUntilIdle();
|
| -}
|
| -
|
| -// This was supposed to test the scenario where we receive a nudge while a
|
| -// connection change canary is scheduled, but has not run yet. Since we've made
|
| -// the connection change canary synchronous, this is no longer possible.
|
| -TEST_F(SyncSchedulerTest, ConnectionChangeCanaryPreemptedByNudge) {
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
|
| -
|
| - StartSyncScheduler(base::Time());
|
| - connection()->SetServerNotReachable();
|
| - connection()->UpdateConnectionStatus();
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(
|
| - DoAll(Invoke(test_util::SimulateConnectionFailure), Return(false)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess), Return(true)))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - QuitLoopNowAction(true)));
|
| -
|
| - scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
|
| -
|
| - PumpLoop(); // To get PerformDelayedNudge called.
|
| - PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
|
| - ASSERT_TRUE(scheduler()->IsBackingOff());
|
| -
|
| - // Before we run the scheduled canary, trigger a server connection change.
|
| - scheduler()->OnConnectionStatusChange();
|
| - PumpLoop();
|
| - connection()->SetServerReachable();
|
| - connection()->UpdateConnectionStatus();
|
| - scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
|
| - base::RunLoop().RunUntilIdle();
|
| -}
|
| -
|
| -// Tests that we don't crash trying to run two canaries at once if we receive
|
| -// extra connection status change notifications. See crbug.com/190085.
|
| -TEST_F(SyncSchedulerTest, DoubleCanaryInConfigure) {
|
| - EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
|
| - .WillRepeatedly(DoAll(
|
| - Invoke(test_util::SimulateConfigureConnectionFailure), Return(true)));
|
| - StartSyncConfiguration();
|
| - connection()->SetServerNotReachable();
|
| - connection()->UpdateConnectionStatus();
|
| -
|
| - ModelTypeSet model_types(THEMES);
|
| - CallbackCounter ready_counter;
|
| - CallbackCounter retry_counter;
|
| - ConfigurationParams params(
|
| - GetUpdatesCallerInfo::RECONFIGURATION, model_types,
|
| - TypesToRoutingInfo(model_types),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
|
| - base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
|
| - scheduler()->ScheduleConfiguration(params);
|
| -
|
| - scheduler()->OnConnectionStatusChange();
|
| - scheduler()->OnConnectionStatusChange();
|
| -
|
| - PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, PollFromCanaryAfterAuthError) {
|
| - SyncShareTimes times;
|
| - TimeDelta poll(TimeDelta::FromMilliseconds(15));
|
| - scheduler()->OnReceivedLongPollIntervalUpdate(poll);
|
| -
|
| - ::testing::InSequence seq;
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .WillRepeatedly(
|
| - DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShareMultiple(×, kMinNumSamples, true)));
|
| -
|
| - connection()->SetServerStatus(HttpResponse::SYNC_AUTH_ERROR);
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - // Run to wait for polling.
|
| - RunLoop();
|
| -
|
| - // Normally OnCredentialsUpdated calls TryCanaryJob that doesn't run Poll,
|
| - // but after poll finished with auth error from poll timer it should retry
|
| - // poll once more
|
| - EXPECT_CALL(*syncer(), PollSyncShare(_, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulatePollSuccess),
|
| - RecordSyncShare(×, true)));
|
| - scheduler()->OnCredentialsUpdated();
|
| - connection()->SetServerStatus(HttpResponse::SERVER_CONNECTION_OK);
|
| - RunLoop();
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, SuccessfulRetry) {
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - SyncShareTimes times;
|
| - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
|
| - scheduler()->OnReceivedGuRetryDelay(delay);
|
| - EXPECT_EQ(delay, GetRetryTimerDelay());
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| -
|
| - // Run to wait for retrying.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, FailedRetry) {
|
| - SyncShareTimes times;
|
| -
|
| - UseMockDelayProvider();
|
| - EXPECT_CALL(*delay(), GetDelay(_))
|
| - .WillRepeatedly(Return(TimeDelta::FromMilliseconds(10)));
|
| -
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
|
| - scheduler()->OnReceivedGuRetryDelay(delay);
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateDownloadUpdatesFailed),
|
| - RecordSyncShare(×, false)));
|
| -
|
| - // Run to wait for retrying.
|
| - RunLoop();
|
| -
|
| - EXPECT_TRUE(scheduler()->IsBackingOff());
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| -
|
| - // Run to wait for second retrying.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -ACTION_P2(VerifyRetryTimerDelay, scheduler_test, expected_delay) {
|
| - EXPECT_EQ(expected_delay, scheduler_test->GetRetryTimerDelay());
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, ReceiveNewRetryDelay) {
|
| - StartSyncScheduler(base::Time());
|
| -
|
| - SyncShareTimes times;
|
| - base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(100);
|
| - base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(200);
|
| -
|
| - scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
|
| - scheduler()->OnReceivedGuRetryDelay(delay1);
|
| - EXPECT_EQ(delay1, GetRetryTimerDelay());
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(
|
| - DoAll(WithoutArgs(VerifyRetryTimerDelay(this, delay1)),
|
| - WithArg<2>(test_util::SimulateGuRetryDelayCommand(delay2)),
|
| - RecordSyncShare(×, true)));
|
| -
|
| - // Run nudge GU.
|
| - RunLoop();
|
| - EXPECT_EQ(delay2, GetRetryTimerDelay());
|
| -
|
| - EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
|
| - .WillOnce(DoAll(Invoke(test_util::SimulateNormalSuccess),
|
| - RecordSyncShare(×, true)));
|
| -
|
| - // Run to wait for retrying.
|
| - RunLoop();
|
| -
|
| - StopSyncScheduler();
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, ScheduleClearServerData_Succeeds) {
|
| - StartSyncConfiguration();
|
| - scheduler()->Start(SyncScheduler::CLEAR_SERVER_DATA_MODE, base::Time());
|
| - CallbackCounter success_counter;
|
| - ClearParams params(base::Bind(&CallbackCounter::Callback,
|
| - base::Unretained(&success_counter)));
|
| - scheduler()->ScheduleClearServerData(params);
|
| - PumpLoop();
|
| - ASSERT_EQ(1, success_counter.times_called());
|
| -}
|
| -
|
| -TEST_F(SyncSchedulerTest, ScheduleClearServerData_FailsRetriesSucceeds) {
|
| - UseMockDelayProvider();
|
| - TimeDelta delta(TimeDelta::FromMilliseconds(20));
|
| - EXPECT_CALL(*delay(), GetDelay(_)).WillRepeatedly(Return(delta));
|
| -
|
| - StartSyncConfiguration();
|
| - scheduler()->Start(SyncScheduler::CLEAR_SERVER_DATA_MODE, base::Time());
|
| - CallbackCounter success_counter;
|
| - ClearParams params(base::Bind(&CallbackCounter::Callback,
|
| - base::Unretained(&success_counter)));
|
| -
|
| - // Next request will fail.
|
| - connection()->SetServerNotReachable();
|
| - scheduler()->ScheduleClearServerData(params);
|
| - PumpLoop();
|
| - ASSERT_EQ(0, success_counter.times_called());
|
| - ASSERT_TRUE(scheduler()->IsBackingOff());
|
| -
|
| - // Now succeed.
|
| - connection()->SetServerReachable();
|
| - PumpLoopFor(2 * delta);
|
| - ASSERT_EQ(1, success_counter.times_called());
|
| - ASSERT_FALSE(scheduler()->IsBackingOff());
|
| -}
|
| -
|
| -} // namespace syncer
|
|
|