Add an exponential backoff to rechecking the app list doodle.
[chromium-blink-merge.git] / sync / engine / sync_scheduler_unittest.cc
blob1d43844cddb4fa3a804321d69bc2352807950165
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/bind.h"
6 #include "base/callback.h"
7 #include "base/compiler_specific.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/run_loop.h"
10 #include "base/test/test_timeouts.h"
11 #include "sync/engine/backoff_delay_provider.h"
12 #include "sync/engine/sync_scheduler_impl.h"
13 #include "sync/engine/syncer.h"
14 #include "sync/internal_api/public/base/cancelation_signal.h"
15 #include "sync/internal_api/public/base/model_type_test_util.h"
16 #include "sync/sessions/test_util.h"
17 #include "sync/test/callback_counter.h"
18 #include "sync/test/engine/fake_model_worker.h"
19 #include "sync/test/engine/mock_connection_manager.h"
20 #include "sync/test/engine/mock_nudge_handler.h"
21 #include "sync/test/engine/test_directory_setter_upper.h"
22 #include "sync/test/mock_invalidation.h"
23 #include "sync/util/extensions_activity.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 using base::TimeDelta;
28 using base::TimeTicks;
29 using testing::_;
30 using testing::AtLeast;
31 using testing::DoAll;
32 using testing::Invoke;
33 using testing::Mock;
34 using testing::Return;
35 using testing::WithArg;
36 using testing::WithArgs;
37 using testing::WithoutArgs;
39 namespace syncer {
40 using sessions::SyncSession;
41 using sessions::SyncSessionContext;
42 using sync_pb::GetUpdatesCallerInfo;
44 class MockSyncer : public Syncer {
45 public:
46 MockSyncer();
47 MOCK_METHOD3(NormalSyncShare,
48 bool(ModelTypeSet,
49 sessions::NudgeTracker*,
50 sessions::SyncSession*));
51 MOCK_METHOD3(ConfigureSyncShare,
52 bool(ModelTypeSet,
53 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource,
54 SyncSession*));
55 MOCK_METHOD2(PollSyncShare, bool(ModelTypeSet, sessions::SyncSession*));
58 MockSyncer::MockSyncer()
59 : Syncer(NULL) {}
61 typedef std::vector<TimeTicks> SyncShareTimes;
63 void QuitLoopNow() {
64 // We use QuitNow() instead of Quit() as the latter may get stalled
65 // indefinitely in the presence of repeated timers with low delays
66 // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll
67 // delay of 5ms] run under TSAN on the trybots).
68 base::MessageLoop::current()->QuitNow();
71 void RunLoop() {
72 base::MessageLoop::current()->Run();
75 void PumpLoop() {
76 // Do it this way instead of RunAllPending to pump loop exactly once
77 // (necessary in the presence of timers; see comment in
78 // QuitLoopNow).
79 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&QuitLoopNow));
80 RunLoop();
83 void PumpLoopFor(base::TimeDelta time) {
84 // Allow the loop to run for the specified amount of time.
85 base::MessageLoop::current()->PostDelayedTask(
86 FROM_HERE, base::Bind(&QuitLoopNow), time);
87 RunLoop();
90 ModelSafeRoutingInfo TypesToRoutingInfo(ModelTypeSet types) {
91 ModelSafeRoutingInfo routes;
92 for (ModelTypeSet::Iterator iter = types.First(); iter.Good(); iter.Inc()) {
93 routes[iter.Get()] = GROUP_PASSIVE;
95 return routes;
99 static const size_t kMinNumSamples = 5;
101 // Test harness for the SyncScheduler. Test the delays and backoff timers used
102 // in response to various events.
104 // These tests execute in real time with real timers. We try to keep the
105 // delays short, but there is a limit to how short we can make them. The
106 // timers on some platforms (ie. Windows) have a timer resolution greater than
107 // 1ms. Using 1ms delays may result in test flakiness.
109 // See crbug.com/402212 for more info.
110 class SyncSchedulerTest : public testing::Test {
111 public:
112 SyncSchedulerTest() : syncer_(NULL), delay_(NULL), weak_ptr_factory_(this) {}
114 class MockDelayProvider : public BackoffDelayProvider {
115 public:
116 MockDelayProvider() : BackoffDelayProvider(
117 TimeDelta::FromSeconds(kInitialBackoffRetrySeconds),
118 TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds)) {
121 MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&));
124 void SetUp() override {
125 dir_maker_.SetUp();
126 syncer_ = new testing::StrictMock<MockSyncer>();
127 delay_ = NULL;
128 extensions_activity_ = new ExtensionsActivity();
130 routing_info_[THEMES] = GROUP_UI;
131 routing_info_[TYPED_URLS] = GROUP_DB;
132 routing_info_[THEMES] = GROUP_UI;
133 routing_info_[NIGORI] = GROUP_PASSIVE;
135 workers_.clear();
136 workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_UI)));
137 workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_DB)));
138 workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_PASSIVE)));
140 connection_.reset(new MockConnectionManager(directory(),
141 &cancelation_signal_));
142 connection_->SetServerReachable();
144 model_type_registry_.reset(
145 new ModelTypeRegistry(workers_, directory(), &mock_nudge_handler_));
147 context_.reset(new SyncSessionContext(
148 connection_.get(), directory(),
149 extensions_activity_.get(),
150 std::vector<SyncEngineEventListener*>(), NULL,
151 model_type_registry_.get(),
152 true, // enable keystore encryption
153 false, // force enable pre-commit GU avoidance
154 "fake_invalidator_client_id"));
155 context_->SetRoutingInfo(routing_info_);
156 context_->set_notifications_enabled(true);
157 context_->set_account_name("Test");
158 scheduler_.reset(
159 new SyncSchedulerImpl("TestSyncScheduler",
160 BackoffDelayProvider::FromDefaults(),
161 context(),
162 syncer_));
163 scheduler_->SetDefaultNudgeDelay(default_delay());
166 SyncSchedulerImpl* scheduler() { return scheduler_.get(); }
167 const ModelSafeRoutingInfo& routing_info() { return routing_info_; }
168 MockSyncer* syncer() { return syncer_; }
169 MockDelayProvider* delay() { return delay_; }
170 MockConnectionManager* connection() { return connection_.get(); }
171 TimeDelta default_delay() { return TimeDelta::FromSeconds(0); }
172 TimeDelta timeout() {
173 return TestTimeouts::action_timeout();
176 void TearDown() override {
177 PumpLoop();
178 scheduler_.reset();
179 PumpLoop();
180 dir_maker_.TearDown();
183 void AnalyzePollRun(const SyncShareTimes& times, size_t min_num_samples,
184 const TimeTicks& optimal_start, const TimeDelta& poll_interval) {
185 EXPECT_GE(times.size(), min_num_samples);
186 for (size_t i = 0; i < times.size(); i++) {
187 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
188 TimeTicks optimal_next_sync = optimal_start + poll_interval * i;
189 EXPECT_GE(times[i], optimal_next_sync);
193 void DoQuitLoopNow() {
194 QuitLoopNow();
197 void StartSyncScheduler(SyncScheduler::Mode mode) {
198 scheduler()->Start(mode);
201 // This stops the scheduler synchronously.
202 void StopSyncScheduler() {
203 base::MessageLoop::current()->PostTask(
204 FROM_HERE,
205 base::Bind(&SyncSchedulerTest::DoQuitLoopNow,
206 weak_ptr_factory_.GetWeakPtr()));
207 RunLoop();
210 bool RunAndGetBackoff() {
211 ModelTypeSet nudge_types(THEMES);
212 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
214 scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
215 RunLoop();
217 return scheduler()->IsBackingOff();
220 void UseMockDelayProvider() {
221 delay_ = new MockDelayProvider();
222 scheduler_->delay_provider_.reset(delay_);
225 SyncSessionContext* context() { return context_.get(); }
227 ModelTypeSet GetThrottledTypes() {
228 return scheduler_->nudge_tracker_.GetThrottledTypes();
231 base::TimeDelta GetRetryTimerDelay() {
232 EXPECT_TRUE(scheduler_->retry_timer_.IsRunning());
233 return scheduler_->retry_timer_.GetCurrentDelay();
236 static scoped_ptr<InvalidationInterface> BuildInvalidation(
237 int64 version,
238 const std::string& payload) {
239 return MockInvalidation::Build(version, payload);
242 private:
243 syncable::Directory* directory() {
244 return dir_maker_.directory();
247 base::MessageLoop loop_;
248 TestDirectorySetterUpper dir_maker_;
249 CancelationSignal cancelation_signal_;
250 scoped_ptr<MockConnectionManager> connection_;
251 scoped_ptr<ModelTypeRegistry> model_type_registry_;
252 scoped_ptr<SyncSessionContext> context_;
253 scoped_ptr<SyncSchedulerImpl> scheduler_;
254 MockNudgeHandler mock_nudge_handler_;
255 MockSyncer* syncer_;
256 MockDelayProvider* delay_;
257 std::vector<scoped_refptr<ModelSafeWorker> > workers_;
258 scoped_refptr<ExtensionsActivity> extensions_activity_;
259 ModelSafeRoutingInfo routing_info_;
260 base::WeakPtrFactory<SyncSchedulerTest> weak_ptr_factory_;
263 void RecordSyncShareImpl(SyncShareTimes* times) {
264 times->push_back(TimeTicks::Now());
267 ACTION_P(RecordSyncShare, times) {
268 RecordSyncShareImpl(times);
269 if (base::MessageLoop::current()->is_running())
270 QuitLoopNow();
271 return true;
274 ACTION_P2(RecordSyncShareMultiple, times, quit_after) {
275 RecordSyncShareImpl(times);
276 EXPECT_LE(times->size(), quit_after);
277 if (times->size() >= quit_after &&
278 base::MessageLoop::current()->is_running()) {
279 QuitLoopNow();
281 return true;
284 ACTION_P(StopScheduler, scheduler) {
285 scheduler->Stop();
288 ACTION(AddFailureAndQuitLoopNow) {
289 ADD_FAILURE();
290 QuitLoopNow();
291 return true;
294 ACTION(QuitLoopNowAction) {
295 QuitLoopNow();
296 return true;
299 // Test nudge scheduling.
300 TEST_F(SyncSchedulerTest, Nudge) {
301 SyncShareTimes times;
302 ModelTypeSet model_types(THEMES);
304 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
305 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
306 RecordSyncShare(&times)))
307 .RetiresOnSaturation();
309 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
311 scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
312 RunLoop();
314 Mock::VerifyAndClearExpectations(syncer());
316 // Make sure a second, later, nudge is unaffected by first (no coalescing).
317 SyncShareTimes times2;
318 model_types.Remove(THEMES);
319 model_types.Put(TYPED_URLS);
320 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
321 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
322 RecordSyncShare(&times2)));
323 scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
324 RunLoop();
327 // Make sure a regular config command is scheduled fine in the absence of any
328 // errors.
329 TEST_F(SyncSchedulerTest, Config) {
330 SyncShareTimes times;
331 const ModelTypeSet model_types(THEMES);
333 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
334 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
335 RecordSyncShare(&times)));
337 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
339 CallbackCounter ready_counter;
340 CallbackCounter retry_counter;
341 ConfigurationParams params(
342 GetUpdatesCallerInfo::RECONFIGURATION,
343 model_types,
344 TypesToRoutingInfo(model_types),
345 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
346 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
347 scheduler()->ScheduleConfiguration(params);
348 PumpLoop();
349 ASSERT_EQ(1, ready_counter.times_called());
350 ASSERT_EQ(0, retry_counter.times_called());
353 // Simulate a failure and make sure the config request is retried.
354 TEST_F(SyncSchedulerTest, ConfigWithBackingOff) {
355 UseMockDelayProvider();
356 EXPECT_CALL(*delay(), GetDelay(_))
357 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(20)));
358 SyncShareTimes times;
359 const ModelTypeSet model_types(THEMES);
361 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
363 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
364 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
365 RecordSyncShare(&times)))
366 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
367 RecordSyncShare(&times)));
369 CallbackCounter ready_counter;
370 CallbackCounter retry_counter;
371 ConfigurationParams params(
372 GetUpdatesCallerInfo::RECONFIGURATION,
373 model_types,
374 TypesToRoutingInfo(model_types),
375 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
376 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
377 scheduler()->ScheduleConfiguration(params);
378 RunLoop();
379 ASSERT_EQ(0, ready_counter.times_called());
380 ASSERT_EQ(1, retry_counter.times_called());
382 // RunLoop() will trigger TryCanaryJob which will retry configuration.
383 // Since retry_task was already called it shouldn't be called again.
384 RunLoop();
385 ASSERT_EQ(0, ready_counter.times_called());
386 ASSERT_EQ(1, retry_counter.times_called());
388 Mock::VerifyAndClearExpectations(syncer());
390 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
391 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
392 RecordSyncShare(&times)));
393 RunLoop();
395 ASSERT_EQ(1, ready_counter.times_called());
398 // Simuilate SyncSchedulerImpl::Stop being called in the middle of Configure.
399 // This can happen if server returns NOT_MY_BIRTHDAY.
400 TEST_F(SyncSchedulerTest, ConfigWithStop) {
401 UseMockDelayProvider();
402 EXPECT_CALL(*delay(), GetDelay(_))
403 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(20)));
404 SyncShareTimes times;
405 const ModelTypeSet model_types(THEMES);
407 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
409 // Make ConfigureSyncShare call scheduler->Stop(). It is not supposed to call
410 // retry_task or dereference configuration params.
411 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
412 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
413 StopScheduler(scheduler()),
414 RecordSyncShare(&times)));
416 CallbackCounter ready_counter;
417 CallbackCounter retry_counter;
418 ConfigurationParams params(
419 GetUpdatesCallerInfo::RECONFIGURATION,
420 model_types,
421 TypesToRoutingInfo(model_types),
422 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
423 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
424 scheduler()->ScheduleConfiguration(params);
425 PumpLoop();
426 ASSERT_EQ(0, ready_counter.times_called());
427 ASSERT_EQ(0, retry_counter.times_called());
430 // Issue a nudge when the config has failed. Make sure both the config and
431 // nudge are executed.
432 TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) {
433 const ModelTypeSet model_types(THEMES);
434 UseMockDelayProvider();
435 EXPECT_CALL(*delay(), GetDelay(_))
436 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50)));
437 SyncShareTimes times;
439 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
441 // Request a configure and make sure it fails.
442 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
443 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
444 RecordSyncShare(&times)));
445 CallbackCounter ready_counter;
446 CallbackCounter retry_counter;
447 ConfigurationParams params(
448 GetUpdatesCallerInfo::RECONFIGURATION,
449 model_types,
450 TypesToRoutingInfo(model_types),
451 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
452 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
453 scheduler()->ScheduleConfiguration(params);
454 RunLoop();
455 ASSERT_EQ(0, ready_counter.times_called());
456 ASSERT_EQ(1, retry_counter.times_called());
457 Mock::VerifyAndClearExpectations(syncer());
459 // Ask for a nudge while dealing with repeated configure failure.
460 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
461 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
462 RecordSyncShare(&times)));
463 scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
464 RunLoop();
465 // Note that we're not RunLoop()ing for the NUDGE we just scheduled, but
466 // for the first retry attempt from the config job (after
467 // waiting ~+/- 50ms).
468 Mock::VerifyAndClearExpectations(syncer());
469 ASSERT_EQ(0, ready_counter.times_called());
471 // Let the next configure retry succeed.
472 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
473 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
474 RecordSyncShare(&times)));
475 RunLoop();
477 // Now change the mode so nudge can execute.
478 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
479 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
480 RecordSyncShare(&times)));
481 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
482 PumpLoop();
485 // Test that nudges are coalesced.
486 TEST_F(SyncSchedulerTest, NudgeCoalescing) {
487 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
489 SyncShareTimes times;
490 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
491 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
492 RecordSyncShare(&times)));
493 const ModelTypeSet types1(THEMES), types2(TYPED_URLS), types3(THEMES);
494 TimeTicks optimal_time = TimeTicks::Now() + default_delay();
495 scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
496 scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
497 RunLoop();
499 ASSERT_EQ(1U, times.size());
500 EXPECT_GE(times[0], optimal_time);
502 Mock::VerifyAndClearExpectations(syncer());
504 SyncShareTimes times2;
505 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
506 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
507 RecordSyncShare(&times2)));
508 scheduler()->ScheduleLocalNudge(types3, FROM_HERE);
509 RunLoop();
512 // Test that nudges are coalesced.
513 TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) {
514 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
516 SyncShareTimes times;
517 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
518 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
519 RecordSyncShare(&times)));
520 ModelTypeSet types1(THEMES), types2(TYPED_URLS), types3;
522 // Create a huge time delay.
523 TimeDelta delay = TimeDelta::FromDays(1);
525 std::map<ModelType, TimeDelta> delay_map;
526 delay_map[types1.First().Get()] = delay;
527 scheduler()->OnReceivedCustomNudgeDelays(delay_map);
528 scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
529 scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
531 TimeTicks min_time = TimeTicks::Now();
532 TimeTicks max_time = TimeTicks::Now() + delay;
534 RunLoop();
535 Mock::VerifyAndClearExpectations(syncer());
537 // Make sure the sync happened at the right time.
538 ASSERT_EQ(1U, times.size());
539 EXPECT_GE(times[0], min_time);
540 EXPECT_LE(times[0], max_time);
543 // Test nudge scheduling.
544 TEST_F(SyncSchedulerTest, NudgeWithStates) {
545 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
547 SyncShareTimes times1;
548 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
549 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
550 RecordSyncShare(&times1)))
551 .RetiresOnSaturation();
552 scheduler()->ScheduleInvalidationNudge(
553 THEMES, BuildInvalidation(10, "test"), FROM_HERE);
554 RunLoop();
556 Mock::VerifyAndClearExpectations(syncer());
558 // Make sure a second, later, nudge is unaffected by first (no coalescing).
559 SyncShareTimes times2;
560 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
561 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
562 RecordSyncShare(&times2)));
563 scheduler()->ScheduleInvalidationNudge(
564 TYPED_URLS, BuildInvalidation(10, "test2"), FROM_HERE);
565 RunLoop();
568 // Test that polling works as expected.
569 TEST_F(SyncSchedulerTest, Polling) {
570 SyncShareTimes times;
571 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
572 EXPECT_CALL(*syncer(), PollSyncShare(_,_)).Times(AtLeast(kMinNumSamples))
573 .WillRepeatedly(
574 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
575 RecordSyncShareMultiple(&times, kMinNumSamples)));
577 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
579 TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
580 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
582 // Run again to wait for polling.
583 RunLoop();
585 StopSyncScheduler();
586 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
589 // Test that the short poll interval is used.
590 TEST_F(SyncSchedulerTest, PollNotificationsDisabled) {
591 SyncShareTimes times;
592 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
593 EXPECT_CALL(*syncer(), PollSyncShare(_,_)).Times(AtLeast(kMinNumSamples))
594 .WillRepeatedly(
595 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
596 RecordSyncShareMultiple(&times, kMinNumSamples)));
598 scheduler()->OnReceivedShortPollIntervalUpdate(poll_interval);
599 scheduler()->SetNotificationsEnabled(false);
601 TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
602 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
604 // Run again to wait for polling.
605 RunLoop();
607 StopSyncScheduler();
608 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
611 // Test that polling intervals are updated when needed.
612 TEST_F(SyncSchedulerTest, PollIntervalUpdate) {
613 SyncShareTimes times;
614 TimeDelta poll1(TimeDelta::FromMilliseconds(120));
615 TimeDelta poll2(TimeDelta::FromMilliseconds(30));
616 scheduler()->OnReceivedLongPollIntervalUpdate(poll1);
617 EXPECT_CALL(*syncer(), PollSyncShare(_,_)).Times(AtLeast(kMinNumSamples))
618 .WillOnce(DoAll(
619 WithArgs<0,1>(
620 sessions::test_util::SimulatePollIntervalUpdate(poll2)),
621 Return(true)))
622 .WillRepeatedly(
623 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
624 WithArg<1>(
625 RecordSyncShareMultiple(&times, kMinNumSamples))));
627 TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2;
628 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
630 // Run again to wait for polling.
631 RunLoop();
633 StopSyncScheduler();
634 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll2);
637 // Test that no syncing occurs when throttled.
638 TEST_F(SyncSchedulerTest, ThrottlingDoesThrottle) {
639 const ModelTypeSet types(THEMES);
640 TimeDelta poll(TimeDelta::FromMilliseconds(20));
641 TimeDelta throttle(TimeDelta::FromMinutes(10));
642 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
644 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
645 .WillOnce(DoAll(
646 WithArg<2>(sessions::test_util::SimulateThrottled(throttle)),
647 Return(true)))
648 .WillRepeatedly(AddFailureAndQuitLoopNow());
650 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
652 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
653 PumpLoop();
655 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
657 CallbackCounter ready_counter;
658 CallbackCounter retry_counter;
659 ConfigurationParams params(
660 GetUpdatesCallerInfo::RECONFIGURATION,
661 types,
662 TypesToRoutingInfo(types),
663 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
664 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
665 scheduler()->ScheduleConfiguration(params);
666 PumpLoop();
667 ASSERT_EQ(0, ready_counter.times_called());
668 ASSERT_EQ(1, retry_counter.times_called());
672 TEST_F(SyncSchedulerTest, ThrottlingExpiresFromPoll) {
673 SyncShareTimes times;
674 TimeDelta poll(TimeDelta::FromMilliseconds(15));
675 TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
676 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
678 ::testing::InSequence seq;
679 EXPECT_CALL(*syncer(), PollSyncShare(_,_))
680 .WillOnce(DoAll(
681 WithArg<1>(sessions::test_util::SimulateThrottled(throttle1)),
682 Return(true)))
683 .RetiresOnSaturation();
684 EXPECT_CALL(*syncer(), PollSyncShare(_,_))
685 .WillRepeatedly(
686 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
687 RecordSyncShareMultiple(&times, kMinNumSamples)));
689 TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1;
690 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
692 // Run again to wait for polling.
693 RunLoop();
695 StopSyncScheduler();
696 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll);
699 TEST_F(SyncSchedulerTest, ThrottlingExpiresFromNudge) {
700 SyncShareTimes times;
701 TimeDelta poll(TimeDelta::FromDays(1));
702 TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
703 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
705 ::testing::InSequence seq;
706 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
707 .WillOnce(DoAll(
708 WithArg<2>(sessions::test_util::SimulateThrottled(throttle1)),
709 Return(true)))
710 .RetiresOnSaturation();
711 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
712 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
713 QuitLoopNowAction()));
715 const ModelTypeSet types(THEMES);
716 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
717 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
719 PumpLoop(); // To get PerformDelayedNudge called.
720 PumpLoop(); // To get TrySyncSessionJob called
721 EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
722 RunLoop();
723 EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
725 StopSyncScheduler();
728 TEST_F(SyncSchedulerTest, ThrottlingExpiresFromConfigure) {
729 SyncShareTimes times;
730 TimeDelta poll(TimeDelta::FromDays(1));
731 TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
732 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
734 ::testing::InSequence seq;
735 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
736 .WillOnce(DoAll(
737 WithArg<2>(sessions::test_util::SimulateThrottled(throttle1)),
738 Return(true)))
739 .RetiresOnSaturation();
740 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
741 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
742 QuitLoopNowAction()));
744 const ModelTypeSet types(THEMES);
745 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
747 CallbackCounter ready_counter;
748 CallbackCounter retry_counter;
749 ConfigurationParams params(
750 GetUpdatesCallerInfo::RECONFIGURATION,
751 types,
752 TypesToRoutingInfo(types),
753 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
754 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
755 scheduler()->ScheduleConfiguration(params);
756 PumpLoop();
757 EXPECT_EQ(0, ready_counter.times_called());
758 EXPECT_EQ(1, retry_counter.times_called());
759 EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
761 RunLoop();
762 EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
764 StopSyncScheduler();
767 TEST_F(SyncSchedulerTest, TypeThrottlingBlocksNudge) {
768 UseMockDelayProvider();
769 EXPECT_CALL(*delay(), GetDelay(_))
770 .WillRepeatedly(Return(default_delay()));
772 TimeDelta poll(TimeDelta::FromDays(1));
773 TimeDelta throttle1(TimeDelta::FromSeconds(60));
774 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
776 const ModelTypeSet types(THEMES);
778 ::testing::InSequence seq;
779 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
780 .WillOnce(DoAll(
781 WithArg<2>(
782 sessions::test_util::SimulateTypesThrottled(types, throttle1)),
783 Return(true)))
784 .RetiresOnSaturation();
786 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
787 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
788 PumpLoop(); // To get PerformDelayedNudge called.
789 PumpLoop(); // To get TrySyncSessionJob called
790 EXPECT_TRUE(GetThrottledTypes().HasAll(types));
792 // This won't cause a sync cycle because the types are throttled.
793 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
794 PumpLoop();
796 StopSyncScheduler();
799 TEST_F(SyncSchedulerTest, TypeThrottlingDoesBlockOtherSources) {
800 UseMockDelayProvider();
801 EXPECT_CALL(*delay(), GetDelay(_))
802 .WillRepeatedly(Return(default_delay()));
804 SyncShareTimes times;
805 TimeDelta poll(TimeDelta::FromDays(1));
806 TimeDelta throttle1(TimeDelta::FromSeconds(60));
807 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
809 const ModelTypeSet throttled_types(THEMES);
810 const ModelTypeSet unthrottled_types(PREFERENCES);
812 ::testing::InSequence seq;
813 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
814 .WillOnce(DoAll(
815 WithArg<2>(
816 sessions::test_util::SimulateTypesThrottled(
817 throttled_types, throttle1)),
818 Return(true)))
819 .RetiresOnSaturation();
821 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
822 scheduler()->ScheduleLocalNudge(throttled_types, FROM_HERE);
823 PumpLoop(); // To get PerformDelayedNudge called.
824 PumpLoop(); // To get TrySyncSessionJob called
825 EXPECT_TRUE(GetThrottledTypes().HasAll(throttled_types));
827 // Ignore invalidations for throttled types.
828 scheduler()->ScheduleInvalidationNudge(
829 THEMES, BuildInvalidation(10, "test"), FROM_HERE);
830 PumpLoop();
832 // Ignore refresh requests for throttled types.
833 scheduler()->ScheduleLocalRefreshRequest(throttled_types, FROM_HERE);
834 PumpLoop();
836 Mock::VerifyAndClearExpectations(syncer());
838 // Local nudges for non-throttled types will trigger a sync.
839 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
840 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
841 RecordSyncShare(&times)));
842 scheduler()->ScheduleLocalNudge(unthrottled_types, FROM_HERE);
843 RunLoop();
844 Mock::VerifyAndClearExpectations(syncer());
846 StopSyncScheduler();
849 // Test nudges / polls don't run in config mode and config tasks do.
850 TEST_F(SyncSchedulerTest, ConfigurationMode) {
851 TimeDelta poll(TimeDelta::FromMilliseconds(15));
852 SyncShareTimes times;
853 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
855 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
857 const ModelTypeSet nudge_types(TYPED_URLS);
858 scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
859 scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
861 const ModelTypeSet config_types(THEMES);
863 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
864 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
865 RecordSyncShare(&times)))
866 .RetiresOnSaturation();
867 CallbackCounter ready_counter;
868 CallbackCounter retry_counter;
869 ConfigurationParams params(
870 GetUpdatesCallerInfo::RECONFIGURATION,
871 config_types,
872 TypesToRoutingInfo(config_types),
873 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
874 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
875 scheduler()->ScheduleConfiguration(params);
876 RunLoop();
877 ASSERT_EQ(1, ready_counter.times_called());
878 ASSERT_EQ(0, retry_counter.times_called());
880 Mock::VerifyAndClearExpectations(syncer());
882 // Switch to NORMAL_MODE to ensure NUDGES were properly saved and run.
883 scheduler()->OnReceivedLongPollIntervalUpdate(TimeDelta::FromDays(1));
884 SyncShareTimes times2;
885 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
886 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
887 RecordSyncShare(&times2)));
889 // TODO(tim): Figure out how to remove this dangerous need to reset
890 // routing info between mode switches.
891 context()->SetRoutingInfo(routing_info());
892 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
894 RunLoop();
895 Mock::VerifyAndClearExpectations(syncer());
898 class BackoffTriggersSyncSchedulerTest : public SyncSchedulerTest {
899 void SetUp() override {
900 SyncSchedulerTest::SetUp();
901 UseMockDelayProvider();
902 EXPECT_CALL(*delay(), GetDelay(_))
903 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(10)));
906 void TearDown() override {
907 StopSyncScheduler();
908 SyncSchedulerTest::TearDown();
912 // Have the sycner fail during commit. Expect that the scheduler enters
913 // backoff.
914 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnce) {
915 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
916 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
917 QuitLoopNowAction()));
918 EXPECT_TRUE(RunAndGetBackoff());
921 // Have the syncer fail during download updates and succeed on the first
922 // retry. Expect that this clears the backoff state.
923 TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadOnceThenSucceed) {
924 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
925 .WillOnce(DoAll(
926 Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
927 Return(true)))
928 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
929 QuitLoopNowAction()));
930 EXPECT_FALSE(RunAndGetBackoff());
933 // Have the syncer fail during commit and succeed on the first retry. Expect
934 // that this clears the backoff state.
935 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnceThenSucceed) {
936 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
937 .WillOnce(DoAll(
938 Invoke(sessions::test_util::SimulateCommitFailed),
939 Return(true)))
940 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
941 QuitLoopNowAction()));
942 EXPECT_FALSE(RunAndGetBackoff());
945 // Have the syncer fail to download updates and fail again on the retry.
946 // Expect this will leave the scheduler in backoff.
947 TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadTwice) {
948 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
949 .WillOnce(DoAll(
950 Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
951 Return(true)))
952 .WillRepeatedly(DoAll(
953 Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
954 QuitLoopNowAction()));
955 EXPECT_TRUE(RunAndGetBackoff());
958 // Have the syncer fail to get the encryption key yet succeed in downloading
959 // updates. Expect this will leave the scheduler in backoff.
960 TEST_F(BackoffTriggersSyncSchedulerTest, FailGetEncryptionKey) {
961 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
962 .WillOnce(DoAll(
963 Invoke(sessions::test_util::SimulateGetEncryptionKeyFailed),
964 Return(true)))
965 .WillRepeatedly(DoAll(
966 Invoke(sessions::test_util::SimulateGetEncryptionKeyFailed),
967 QuitLoopNowAction()));
968 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
970 ModelTypeSet types(THEMES);
971 CallbackCounter ready_counter;
972 CallbackCounter retry_counter;
973 ConfigurationParams params(
974 GetUpdatesCallerInfo::RECONFIGURATION,
975 types,
976 TypesToRoutingInfo(types),
977 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
978 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
979 scheduler()->ScheduleConfiguration(params);
980 RunLoop();
982 EXPECT_TRUE(scheduler()->IsBackingOff());
985 // Test that no polls or extraneous nudges occur when in backoff.
986 TEST_F(SyncSchedulerTest, BackoffDropsJobs) {
987 SyncShareTimes times;
988 TimeDelta poll(TimeDelta::FromMilliseconds(10));
989 const ModelTypeSet types(THEMES);
990 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
991 UseMockDelayProvider();
993 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
994 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
995 RecordSyncShareMultiple(&times, 1U)));
996 EXPECT_CALL(*delay(), GetDelay(_)).
997 WillRepeatedly(Return(TimeDelta::FromDays(1)));
999 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1001 // This nudge should fail and put us into backoff. Thanks to our mock
1002 // GetDelay() setup above, this will be a long backoff.
1003 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
1004 RunLoop();
1006 // From this point forward, no SyncShare functions should be invoked.
1007 Mock::VerifyAndClearExpectations(syncer());
1009 // Wait a while (10x poll interval) so a few poll jobs will be attempted.
1010 PumpLoopFor(poll * 10);
1012 // Try (and fail) to schedule a nudge.
1013 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
1015 Mock::VerifyAndClearExpectations(syncer());
1016 Mock::VerifyAndClearExpectations(delay());
1018 EXPECT_CALL(*delay(), GetDelay(_)).Times(0);
1020 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
1022 CallbackCounter ready_counter;
1023 CallbackCounter retry_counter;
1024 ConfigurationParams params(
1025 GetUpdatesCallerInfo::RECONFIGURATION,
1026 types,
1027 TypesToRoutingInfo(types),
1028 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
1029 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
1030 scheduler()->ScheduleConfiguration(params);
1031 PumpLoop();
1032 ASSERT_EQ(0, ready_counter.times_called());
1033 ASSERT_EQ(1, retry_counter.times_called());
1037 // Test that backoff is shaping traffic properly with consecutive errors.
1038 TEST_F(SyncSchedulerTest, BackoffElevation) {
1039 SyncShareTimes times;
1040 UseMockDelayProvider();
1042 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_)).Times(kMinNumSamples)
1043 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
1044 RecordSyncShareMultiple(&times, kMinNumSamples)));
1046 const TimeDelta first = TimeDelta::FromSeconds(kInitialBackoffRetrySeconds);
1047 const TimeDelta second = TimeDelta::FromMilliseconds(20);
1048 const TimeDelta third = TimeDelta::FromMilliseconds(30);
1049 const TimeDelta fourth = TimeDelta::FromMilliseconds(40);
1050 const TimeDelta fifth = TimeDelta::FromMilliseconds(50);
1051 const TimeDelta sixth = TimeDelta::FromDays(1);
1053 EXPECT_CALL(*delay(), GetDelay(first)).WillOnce(Return(second))
1054 .RetiresOnSaturation();
1055 EXPECT_CALL(*delay(), GetDelay(second)).WillOnce(Return(third))
1056 .RetiresOnSaturation();
1057 EXPECT_CALL(*delay(), GetDelay(third)).WillOnce(Return(fourth))
1058 .RetiresOnSaturation();
1059 EXPECT_CALL(*delay(), GetDelay(fourth)).WillOnce(Return(fifth))
1060 .RetiresOnSaturation();
1061 EXPECT_CALL(*delay(), GetDelay(fifth)).WillOnce(Return(sixth));
1063 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1065 // Run again with a nudge.
1066 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1067 RunLoop();
1069 ASSERT_EQ(kMinNumSamples, times.size());
1070 EXPECT_GE(times[1] - times[0], second);
1071 EXPECT_GE(times[2] - times[1], third);
1072 EXPECT_GE(times[3] - times[2], fourth);
1073 EXPECT_GE(times[4] - times[3], fifth);
1076 // Test that things go back to normal once a retry makes forward progress.
1077 TEST_F(SyncSchedulerTest, BackoffRelief) {
1078 SyncShareTimes times;
1079 const TimeDelta poll(TimeDelta::FromMilliseconds(10));
1080 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
1081 UseMockDelayProvider();
1083 const TimeDelta backoff = TimeDelta::FromMilliseconds(10);
1084 EXPECT_CALL(*delay(), GetDelay(_)).WillOnce(Return(backoff));
1086 // Optimal start for the post-backoff poll party.
1087 TimeTicks optimal_start = TimeTicks::Now();
1088 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1090 // Kick off the test with a failed nudge.
1091 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1092 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
1093 RecordSyncShare(&times)));
1094 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1095 RunLoop();
1096 Mock::VerifyAndClearExpectations(syncer());
1097 TimeTicks optimal_job_time = optimal_start;
1098 ASSERT_EQ(1U, times.size());
1099 EXPECT_GE(times[0], optimal_job_time);
1101 // The retry succeeds.
1102 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1103 .WillOnce(DoAll(
1104 Invoke(sessions::test_util::SimulateNormalSuccess),
1105 RecordSyncShare(&times)));
1106 RunLoop();
1107 Mock::VerifyAndClearExpectations(syncer());
1108 optimal_job_time = optimal_job_time + backoff;
1109 ASSERT_EQ(2U, times.size());
1110 EXPECT_GE(times[1], optimal_job_time);
1112 // Now let the Poll timer do its thing.
1113 EXPECT_CALL(*syncer(), PollSyncShare(_,_))
1114 .WillRepeatedly(DoAll(
1115 Invoke(sessions::test_util::SimulatePollSuccess),
1116 RecordSyncShareMultiple(&times, kMinNumSamples)));
1117 RunLoop();
1118 Mock::VerifyAndClearExpectations(syncer());
1119 ASSERT_EQ(kMinNumSamples, times.size());
1120 for (size_t i = 2; i < times.size(); i++) {
1121 optimal_job_time = optimal_job_time + poll;
1122 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
1123 EXPECT_GE(times[i], optimal_job_time);
1126 StopSyncScheduler();
1129 // Test that poll failures are ignored. They should have no effect on
1130 // subsequent poll attempts, nor should they trigger a backoff/retry.
1131 TEST_F(SyncSchedulerTest, TransientPollFailure) {
1132 SyncShareTimes times;
1133 const TimeDelta poll_interval(TimeDelta::FromMilliseconds(10));
1134 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
1135 UseMockDelayProvider(); // Will cause test failure if backoff is initiated.
1137 EXPECT_CALL(*syncer(), PollSyncShare(_,_))
1138 .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollFailed),
1139 RecordSyncShare(&times)))
1140 .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
1141 RecordSyncShare(&times)));
1143 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1145 // Run the unsucessful poll. The failed poll should not trigger backoff.
1146 RunLoop();
1147 EXPECT_FALSE(scheduler()->IsBackingOff());
1149 // Run the successful poll.
1150 RunLoop();
1151 EXPECT_FALSE(scheduler()->IsBackingOff());
1154 // Test that starting the syncer thread without a valid connection doesn't
1155 // break things when a connection is detected.
1156 TEST_F(SyncSchedulerTest, StartWhenNotConnected) {
1157 connection()->SetServerNotReachable();
1158 connection()->UpdateConnectionStatus();
1159 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1160 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
1161 Return(true)))
1162 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1163 Return(true)));
1164 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1166 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1167 // Should save the nudge for until after the server is reachable.
1168 base::RunLoop().RunUntilIdle();
1170 scheduler()->OnConnectionStatusChange();
1171 connection()->SetServerReachable();
1172 connection()->UpdateConnectionStatus();
1173 base::RunLoop().RunUntilIdle();
1176 TEST_F(SyncSchedulerTest, ServerConnectionChangeDuringBackoff) {
1177 UseMockDelayProvider();
1178 EXPECT_CALL(*delay(), GetDelay(_))
1179 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
1181 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1182 connection()->SetServerNotReachable();
1183 connection()->UpdateConnectionStatus();
1185 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1186 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
1187 Return(true)))
1188 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1189 Return(true)));
1191 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1192 PumpLoop(); // To get PerformDelayedNudge called.
1193 PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
1194 ASSERT_TRUE(scheduler()->IsBackingOff());
1196 // Before we run the scheduled canary, trigger a server connection change.
1197 scheduler()->OnConnectionStatusChange();
1198 connection()->SetServerReachable();
1199 connection()->UpdateConnectionStatus();
1200 base::RunLoop().RunUntilIdle();
1203 // This was supposed to test the scenario where we receive a nudge while a
1204 // connection change canary is scheduled, but has not run yet. Since we've made
1205 // the connection change canary synchronous, this is no longer possible.
1206 TEST_F(SyncSchedulerTest, ConnectionChangeCanaryPreemptedByNudge) {
1207 UseMockDelayProvider();
1208 EXPECT_CALL(*delay(), GetDelay(_))
1209 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
1211 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1212 connection()->SetServerNotReachable();
1213 connection()->UpdateConnectionStatus();
1215 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1216 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
1217 Return(true)))
1218 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1219 Return(true)))
1220 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1221 QuitLoopNowAction()));
1223 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1225 PumpLoop(); // To get PerformDelayedNudge called.
1226 PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
1227 ASSERT_TRUE(scheduler()->IsBackingOff());
1229 // Before we run the scheduled canary, trigger a server connection change.
1230 scheduler()->OnConnectionStatusChange();
1231 PumpLoop();
1232 connection()->SetServerReachable();
1233 connection()->UpdateConnectionStatus();
1234 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1235 base::RunLoop().RunUntilIdle();
1238 // Tests that we don't crash trying to run two canaries at once if we receive
1239 // extra connection status change notifications. See crbug.com/190085.
1240 TEST_F(SyncSchedulerTest, DoubleCanaryInConfigure) {
1241 EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
1242 .WillRepeatedly(DoAll(
1243 Invoke(sessions::test_util::SimulateConfigureConnectionFailure),
1244 Return(true)));
1245 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
1246 connection()->SetServerNotReachable();
1247 connection()->UpdateConnectionStatus();
1249 ModelTypeSet model_types(THEMES);
1250 CallbackCounter ready_counter;
1251 CallbackCounter retry_counter;
1252 ConfigurationParams params(
1253 GetUpdatesCallerInfo::RECONFIGURATION,
1254 model_types,
1255 TypesToRoutingInfo(model_types),
1256 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
1257 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
1258 scheduler()->ScheduleConfiguration(params);
1260 scheduler()->OnConnectionStatusChange();
1261 scheduler()->OnConnectionStatusChange();
1263 PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
1266 TEST_F(SyncSchedulerTest, PollFromCanaryAfterAuthError) {
1267 SyncShareTimes times;
1268 TimeDelta poll(TimeDelta::FromMilliseconds(15));
1269 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
1271 ::testing::InSequence seq;
1272 EXPECT_CALL(*syncer(), PollSyncShare(_,_))
1273 .WillRepeatedly(
1274 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
1275 RecordSyncShareMultiple(&times, kMinNumSamples)));
1277 connection()->SetServerStatus(HttpResponse::SYNC_AUTH_ERROR);
1278 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1280 // Run to wait for polling.
1281 RunLoop();
1283 // Normally OnCredentialsUpdated calls TryCanaryJob that doesn't run Poll,
1284 // but after poll finished with auth error from poll timer it should retry
1285 // poll once more
1286 EXPECT_CALL(*syncer(), PollSyncShare(_,_))
1287 .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
1288 RecordSyncShare(&times)));
1289 scheduler()->OnCredentialsUpdated();
1290 connection()->SetServerStatus(HttpResponse::SERVER_CONNECTION_OK);
1291 RunLoop();
1292 StopSyncScheduler();
1295 TEST_F(SyncSchedulerTest, SuccessfulRetry) {
1296 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1298 SyncShareTimes times;
1299 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
1300 scheduler()->OnReceivedGuRetryDelay(delay);
1301 EXPECT_EQ(delay, GetRetryTimerDelay());
1303 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1304 .WillOnce(
1305 DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1306 RecordSyncShare(&times)));
1308 // Run to wait for retrying.
1309 RunLoop();
1311 StopSyncScheduler();
1314 TEST_F(SyncSchedulerTest, FailedRetry) {
1315 UseMockDelayProvider();
1316 EXPECT_CALL(*delay(), GetDelay(_))
1317 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(10)));
1319 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1321 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
1322 scheduler()->OnReceivedGuRetryDelay(delay);
1324 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1325 .WillOnce(
1326 DoAll(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
1327 QuitLoopNowAction()));
1329 // Run to wait for retrying.
1330 RunLoop();
1332 EXPECT_TRUE(scheduler()->IsBackingOff());
1333 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1334 .WillOnce(
1335 DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1336 QuitLoopNowAction()));
1338 // Run to wait for second retrying.
1339 RunLoop();
1341 StopSyncScheduler();
1344 ACTION_P2(VerifyRetryTimerDelay, scheduler_test, expected_delay) {
1345 EXPECT_EQ(expected_delay, scheduler_test->GetRetryTimerDelay());
1348 TEST_F(SyncSchedulerTest, ReceiveNewRetryDelay) {
1349 StartSyncScheduler(SyncScheduler::NORMAL_MODE);
1351 SyncShareTimes times;
1352 base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(100);
1353 base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(200);
1355 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1356 scheduler()->OnReceivedGuRetryDelay(delay1);
1357 EXPECT_EQ(delay1, GetRetryTimerDelay());
1359 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1360 .WillOnce(DoAll(
1361 WithoutArgs(VerifyRetryTimerDelay(this, delay1)),
1362 WithArg<2>(sessions::test_util::SimulateGuRetryDelayCommand(delay2)),
1363 RecordSyncShare(&times)));
1365 // Run nudge GU.
1366 RunLoop();
1367 EXPECT_EQ(delay2, GetRetryTimerDelay());
1369 EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
1370 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1371 RecordSyncShare(&times)));
1373 // Run to wait for retrying.
1374 RunLoop();
1376 StopSyncScheduler();
1379 } // namespace syncer