Index: sync/engine/sync_scheduler_unittest.cc |
diff --git a/sync/engine/sync_scheduler_unittest.cc b/sync/engine/sync_scheduler_unittest.cc |
deleted file mode 100644 |
index 56c44f67e022f75662fdc7509a3d531259e28d46..0000000000000000000000000000000000000000 |
--- a/sync/engine/sync_scheduler_unittest.cc |
+++ /dev/null |
@@ -1,1479 +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 "sync/engine/backoff_delay_provider.h" |
-#include "sync/engine/sync_scheduler_impl.h" |
-#include "sync/engine/syncer.h" |
-#include "sync/internal_api/public/base/cancelation_signal.h" |
-#include "sync/internal_api/public/base/model_type_test_util.h" |
-#include "sync/sessions/test_util.h" |
-#include "sync/test/callback_counter.h" |
-#include "sync/test/engine/fake_model_worker.h" |
-#include "sync/test/engine/mock_connection_manager.h" |
-#include "sync/test/engine/mock_nudge_handler.h" |
-#include "sync/test/engine/test_directory_setter_upper.h" |
-#include "sync/test/mock_invalidation.h" |
-#include "sync/util/extensions_activity.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 sessions::SyncSession; |
-using sessions::SyncSessionContext; |
-using sync_pb::GetUpdatesCallerInfo; |
- |
-class MockSyncer : public Syncer { |
- public: |
- MockSyncer(); |
- MOCK_METHOD3(NormalSyncShare, |
- bool(ModelTypeSet, |
- sessions::NudgeTracker*, |
- sessions::SyncSession*)); |
- MOCK_METHOD3(ConfigureSyncShare, |
- bool(ModelTypeSet, |
- sync_pb::GetUpdatesCallerInfo::GetUpdatesSource, |
- SyncSession*)); |
- MOCK_METHOD2(PollSyncShare, bool(ModelTypeSet, sessions::SyncSession*)); |
-}; |
- |
-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 SyncSessionContext( |
- 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_); |
- } |
- |
- SyncSessionContext* 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<SyncSessionContext> 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(sessions::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(sessions::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(sessions::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(sessions::test_util::SimulateConfigureFailed), |
- RecordSyncShare(×, false))) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::test_util::SimulateConfigureSuccess), |
- RecordSyncShare(×, true))); |
- RunLoop(); |
- |
- // Now change the mode so nudge can execute. |
- EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _)) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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>( |
- sessions::test_util::SimulatePollIntervalUpdate(poll2)), |
- Return(true))) |
- .WillRepeatedly(DoAll( |
- Invoke(sessions::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>(sessions::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>(sessions::test_util::SimulateThrottled(throttle1)), |
- Return(false))) |
- .RetiresOnSaturation(); |
- EXPECT_CALL(*syncer(), PollSyncShare(_, _)) |
- .WillRepeatedly( |
- DoAll(Invoke(sessions::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>(sessions::test_util::SimulateThrottled(throttle1)), |
- Return(false))) |
- .RetiresOnSaturation(); |
- EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _)) |
- .WillOnce(DoAll(Invoke(sessions::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 TrySyncSessionJob 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>(sessions::test_util::SimulateThrottled(throttle1)), |
- Return(false))) |
- .RetiresOnSaturation(); |
- EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _)) |
- .WillOnce(DoAll(Invoke(sessions::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>(sessions::test_util::SimulateTypesThrottled( |
- types, throttle1)), |
- Return(false))) |
- .RetiresOnSaturation(); |
- |
- StartSyncScheduler(base::Time()); |
- scheduler()->ScheduleLocalNudge(types, FROM_HERE); |
- PumpLoop(); // To get PerformDelayedNudge called. |
- PumpLoop(); // To get TrySyncSessionJob 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>(sessions::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 TrySyncSessionJob 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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::test_util::SimulateDownloadUpdatesFailed), |
- Return(false))) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::test_util::SimulateCommitFailed), |
- Return(false))) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::test_util::SimulateDownloadUpdatesFailed), |
- Return(false))) |
- .WillRepeatedly( |
- DoAll(Invoke(sessions::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(sessions::test_util::SimulateGetEncryptionKeyFailed), |
- Return(false))) |
- .WillRepeatedly( |
- DoAll(Invoke(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::test_util::SimulatePollFailed), |
- RecordSyncShare(×, false))) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::test_util::SimulateConnectionFailure), |
- Return(false))) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::test_util::SimulateConnectionFailure), |
- Return(false))) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::test_util::SimulateConnectionFailure), |
- Return(false))) |
- .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess), |
- Return(true))) |
- .WillOnce(DoAll(Invoke(sessions::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(sessions::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(sessions::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(sessions::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(sessions::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(sessions::test_util::SimulateDownloadUpdatesFailed), |
- RecordSyncShare(×, false))); |
- |
- // Run to wait for retrying. |
- RunLoop(); |
- |
- EXPECT_TRUE(scheduler()->IsBackingOff()); |
- EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _)) |
- .WillOnce(DoAll(Invoke(sessions::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>(sessions::test_util::SimulateGuRetryDelayCommand(delay2)), |
- RecordSyncShare(×, true))); |
- |
- // Run nudge GU. |
- RunLoop(); |
- EXPECT_EQ(delay2, GetRetryTimerDelay()); |
- |
- EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _)) |
- .WillOnce(DoAll(Invoke(sessions::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 |