Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(421)

Side by Side Diff: sync/engine/sync_scheduler_unittest.cc

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sync/engine/sync_scheduler_impl.cc ('k') | sync/engine/syncer.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include <stddef.h>
6 #include <stdint.h>
7
8 #include "base/bind.h"
9 #include "base/callback.h"
10 #include "base/compiler_specific.h"
11 #include "base/location.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/run_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "sync/engine/backoff_delay_provider.h"
18 #include "sync/engine/sync_scheduler_impl.h"
19 #include "sync/engine/syncer.h"
20 #include "sync/internal_api/public/base/cancelation_signal.h"
21 #include "sync/internal_api/public/base/model_type_test_util.h"
22 #include "sync/sessions/test_util.h"
23 #include "sync/test/callback_counter.h"
24 #include "sync/test/engine/fake_model_worker.h"
25 #include "sync/test/engine/mock_connection_manager.h"
26 #include "sync/test/engine/mock_nudge_handler.h"
27 #include "sync/test/engine/test_directory_setter_upper.h"
28 #include "sync/test/mock_invalidation.h"
29 #include "sync/util/extensions_activity.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32
33 using base::TimeDelta;
34 using base::TimeTicks;
35 using testing::_;
36 using testing::AtLeast;
37 using testing::DoAll;
38 using testing::Invoke;
39 using testing::Mock;
40 using testing::Return;
41 using testing::WithArg;
42 using testing::WithArgs;
43 using testing::WithoutArgs;
44
45 namespace syncer {
46 using sessions::SyncSession;
47 using sessions::SyncSessionContext;
48 using sync_pb::GetUpdatesCallerInfo;
49
50 class MockSyncer : public Syncer {
51 public:
52 MockSyncer();
53 MOCK_METHOD3(NormalSyncShare,
54 bool(ModelTypeSet,
55 sessions::NudgeTracker*,
56 sessions::SyncSession*));
57 MOCK_METHOD3(ConfigureSyncShare,
58 bool(ModelTypeSet,
59 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource,
60 SyncSession*));
61 MOCK_METHOD2(PollSyncShare, bool(ModelTypeSet, sessions::SyncSession*));
62 };
63
64 MockSyncer::MockSyncer()
65 : Syncer(NULL) {}
66
67 typedef std::vector<TimeTicks> SyncShareTimes;
68
69 void QuitLoopNow() {
70 // We use QuitNow() instead of Quit() as the latter may get stalled
71 // indefinitely in the presence of repeated timers with low delays
72 // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll
73 // delay of 5ms] run under TSAN on the trybots).
74 base::MessageLoop::current()->QuitNow();
75 }
76
77 void RunLoop() {
78 base::RunLoop().Run();
79 }
80
81 void PumpLoop() {
82 // Do it this way instead of RunAllPending to pump loop exactly once
83 // (necessary in the presence of timers; see comment in
84 // QuitLoopNow).
85 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
86 base::Bind(&QuitLoopNow));
87 RunLoop();
88 }
89
90 void PumpLoopFor(base::TimeDelta time) {
91 // Allow the loop to run for the specified amount of time.
92 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
93 FROM_HERE, base::Bind(&QuitLoopNow), time);
94 RunLoop();
95 }
96
97 ModelSafeRoutingInfo TypesToRoutingInfo(ModelTypeSet types) {
98 ModelSafeRoutingInfo routes;
99 for (ModelTypeSet::Iterator iter = types.First(); iter.Good(); iter.Inc()) {
100 routes[iter.Get()] = GROUP_PASSIVE;
101 }
102 return routes;
103 }
104
105
106 static const size_t kMinNumSamples = 5;
107
108 // Test harness for the SyncScheduler. Test the delays and backoff timers used
109 // in response to various events.
110 //
111 // These tests execute in real time with real timers. We try to keep the
112 // delays short, but there is a limit to how short we can make them. The
113 // timers on some platforms (ie. Windows) have a timer resolution greater than
114 // 1ms. Using 1ms delays may result in test flakiness.
115 //
116 // See crbug.com/402212 for more info.
117 class SyncSchedulerTest : public testing::Test {
118 public:
119 SyncSchedulerTest() : syncer_(NULL), delay_(NULL), weak_ptr_factory_(this) {}
120
121 class MockDelayProvider : public BackoffDelayProvider {
122 public:
123 MockDelayProvider() : BackoffDelayProvider(
124 TimeDelta::FromSeconds(kInitialBackoffRetrySeconds),
125 TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds)) {
126 }
127
128 MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&));
129 };
130
131 void SetUp() override {
132 dir_maker_.SetUp();
133 syncer_ = new testing::StrictMock<MockSyncer>();
134 delay_ = NULL;
135 extensions_activity_ = new ExtensionsActivity();
136
137 routing_info_[THEMES] = GROUP_UI;
138 routing_info_[TYPED_URLS] = GROUP_DB;
139 routing_info_[THEMES] = GROUP_UI;
140 routing_info_[NIGORI] = GROUP_PASSIVE;
141
142 workers_.clear();
143 workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_UI)));
144 workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_DB)));
145 workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_PASSIVE)));
146
147 connection_.reset(new MockConnectionManager(directory(),
148 &cancelation_signal_));
149 connection_->SetServerReachable();
150
151 model_type_registry_.reset(
152 new ModelTypeRegistry(workers_, directory(), &mock_nudge_handler_));
153
154 context_.reset(new SyncSessionContext(
155 connection_.get(), directory(),
156 extensions_activity_.get(),
157 std::vector<SyncEngineEventListener*>(), NULL,
158 model_type_registry_.get(),
159 true, // enable keystore encryption
160 false, // force enable pre-commit GU avoidance
161 "fake_invalidator_client_id"));
162 context_->SetRoutingInfo(routing_info_);
163 context_->set_notifications_enabled(true);
164 context_->set_account_name("Test");
165 scheduler_.reset(
166 new SyncSchedulerImpl("TestSyncScheduler",
167 BackoffDelayProvider::FromDefaults(),
168 context(),
169 syncer_));
170 scheduler_->SetDefaultNudgeDelay(default_delay());
171 }
172
173 SyncSchedulerImpl* scheduler() { return scheduler_.get(); }
174 const ModelSafeRoutingInfo& routing_info() { return routing_info_; }
175 MockSyncer* syncer() { return syncer_; }
176 MockDelayProvider* delay() { return delay_; }
177 MockConnectionManager* connection() { return connection_.get(); }
178 TimeDelta default_delay() { return TimeDelta::FromSeconds(0); }
179 TimeDelta timeout() {
180 return TestTimeouts::action_timeout();
181 }
182
183 void TearDown() override {
184 PumpLoop();
185 scheduler_.reset();
186 PumpLoop();
187 dir_maker_.TearDown();
188 }
189
190 void AnalyzePollRun(const SyncShareTimes& times, size_t min_num_samples,
191 const TimeTicks& optimal_start, const TimeDelta& poll_interval) {
192 EXPECT_GE(times.size(), min_num_samples);
193 for (size_t i = 0; i < times.size(); i++) {
194 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
195 TimeTicks optimal_next_sync = optimal_start + poll_interval * i;
196 EXPECT_GE(times[i], optimal_next_sync);
197 }
198 }
199
200 void DoQuitLoopNow() {
201 QuitLoopNow();
202 }
203
204 void StartSyncConfiguration() {
205 scheduler()->Start(SyncScheduler::CONFIGURATION_MODE, base::Time());
206 }
207
208 void StartSyncScheduler(base::Time last_poll_time) {
209 scheduler()->Start(SyncScheduler::NORMAL_MODE, last_poll_time);
210 }
211
212 // This stops the scheduler synchronously.
213 void StopSyncScheduler() {
214 base::ThreadTaskRunnerHandle::Get()->PostTask(
215 FROM_HERE, base::Bind(&SyncSchedulerTest::DoQuitLoopNow,
216 weak_ptr_factory_.GetWeakPtr()));
217 RunLoop();
218 }
219
220 bool RunAndGetBackoff() {
221 ModelTypeSet nudge_types(THEMES);
222 StartSyncScheduler(base::Time());
223
224 scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
225 RunLoop();
226
227 return scheduler()->IsBackingOff();
228 }
229
230 void UseMockDelayProvider() {
231 delay_ = new MockDelayProvider();
232 scheduler_->delay_provider_.reset(delay_);
233 }
234
235 SyncSessionContext* context() { return context_.get(); }
236
237 ModelTypeSet GetThrottledTypes() {
238 return scheduler_->nudge_tracker_.GetThrottledTypes();
239 }
240
241 base::TimeDelta GetRetryTimerDelay() {
242 EXPECT_TRUE(scheduler_->retry_timer_.IsRunning());
243 return scheduler_->retry_timer_.GetCurrentDelay();
244 }
245
246 static std::unique_ptr<InvalidationInterface> BuildInvalidation(
247 int64_t version,
248 const std::string& payload) {
249 return MockInvalidation::Build(version, payload);
250 }
251
252 private:
253 syncable::Directory* directory() {
254 return dir_maker_.directory();
255 }
256
257 base::MessageLoop loop_;
258 TestDirectorySetterUpper dir_maker_;
259 CancelationSignal cancelation_signal_;
260 std::unique_ptr<MockConnectionManager> connection_;
261 std::unique_ptr<ModelTypeRegistry> model_type_registry_;
262 std::unique_ptr<SyncSessionContext> context_;
263 std::unique_ptr<SyncSchedulerImpl> scheduler_;
264 MockNudgeHandler mock_nudge_handler_;
265 MockSyncer* syncer_;
266 MockDelayProvider* delay_;
267 std::vector<scoped_refptr<ModelSafeWorker> > workers_;
268 scoped_refptr<ExtensionsActivity> extensions_activity_;
269 ModelSafeRoutingInfo routing_info_;
270 base::WeakPtrFactory<SyncSchedulerTest> weak_ptr_factory_;
271 };
272
273 void RecordSyncShareImpl(SyncShareTimes* times) {
274 times->push_back(TimeTicks::Now());
275 }
276
277 ACTION_P2(RecordSyncShare, times, success) {
278 RecordSyncShareImpl(times);
279 if (base::MessageLoop::current()->is_running())
280 QuitLoopNow();
281 return success;
282 }
283
284 ACTION_P3(RecordSyncShareMultiple, times, quit_after, success) {
285 RecordSyncShareImpl(times);
286 EXPECT_LE(times->size(), quit_after);
287 if (times->size() >= quit_after &&
288 base::MessageLoop::current()->is_running()) {
289 QuitLoopNow();
290 }
291 return success;
292 }
293
294 ACTION_P(StopScheduler, scheduler) {
295 scheduler->Stop();
296 }
297
298 ACTION(AddFailureAndQuitLoopNow) {
299 ADD_FAILURE();
300 QuitLoopNow();
301 return true;
302 }
303
304 ACTION_P(QuitLoopNowAction, success) {
305 QuitLoopNow();
306 return success;
307 }
308
309 // Test nudge scheduling.
310 TEST_F(SyncSchedulerTest, Nudge) {
311 SyncShareTimes times;
312 ModelTypeSet model_types(THEMES);
313
314 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
315 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
316 RecordSyncShare(&times, true)))
317 .RetiresOnSaturation();
318
319 StartSyncScheduler(base::Time());
320
321 scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
322 RunLoop();
323
324 Mock::VerifyAndClearExpectations(syncer());
325
326 // Make sure a second, later, nudge is unaffected by first (no coalescing).
327 SyncShareTimes times2;
328 model_types.Remove(THEMES);
329 model_types.Put(TYPED_URLS);
330 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
331 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
332 RecordSyncShare(&times2, true)));
333 scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
334 RunLoop();
335 }
336
337 // Make sure a regular config command is scheduled fine in the absence of any
338 // errors.
339 TEST_F(SyncSchedulerTest, Config) {
340 SyncShareTimes times;
341 const ModelTypeSet model_types(THEMES);
342
343 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
344 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
345 RecordSyncShare(&times, true)));
346
347 StartSyncConfiguration();
348
349 CallbackCounter ready_counter;
350 CallbackCounter retry_counter;
351 ConfigurationParams params(
352 GetUpdatesCallerInfo::RECONFIGURATION,
353 model_types,
354 TypesToRoutingInfo(model_types),
355 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
356 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
357 scheduler()->ScheduleConfiguration(params);
358 PumpLoop();
359 ASSERT_EQ(1, ready_counter.times_called());
360 ASSERT_EQ(0, retry_counter.times_called());
361 }
362
363 // Simulate a failure and make sure the config request is retried.
364 TEST_F(SyncSchedulerTest, ConfigWithBackingOff) {
365 UseMockDelayProvider();
366 EXPECT_CALL(*delay(), GetDelay(_))
367 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(20)));
368 SyncShareTimes times;
369 const ModelTypeSet model_types(THEMES);
370
371 StartSyncConfiguration();
372
373 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
374 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
375 RecordSyncShare(&times, false)))
376 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
377 RecordSyncShare(&times, false)));
378
379 CallbackCounter ready_counter;
380 CallbackCounter retry_counter;
381 ConfigurationParams params(
382 GetUpdatesCallerInfo::RECONFIGURATION,
383 model_types,
384 TypesToRoutingInfo(model_types),
385 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
386 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
387 scheduler()->ScheduleConfiguration(params);
388 RunLoop();
389 ASSERT_EQ(0, ready_counter.times_called());
390 ASSERT_EQ(1, retry_counter.times_called());
391
392 // RunLoop() will trigger TryCanaryJob which will retry configuration.
393 // Since retry_task was already called it shouldn't be called again.
394 RunLoop();
395 ASSERT_EQ(0, ready_counter.times_called());
396 ASSERT_EQ(1, retry_counter.times_called());
397
398 Mock::VerifyAndClearExpectations(syncer());
399
400 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
401 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
402 RecordSyncShare(&times, true)));
403 RunLoop();
404
405 ASSERT_EQ(1, ready_counter.times_called());
406 }
407
408 // Simuilate SyncSchedulerImpl::Stop being called in the middle of Configure.
409 // This can happen if server returns NOT_MY_BIRTHDAY.
410 TEST_F(SyncSchedulerTest, ConfigWithStop) {
411 UseMockDelayProvider();
412 EXPECT_CALL(*delay(), GetDelay(_))
413 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(20)));
414 SyncShareTimes times;
415 const ModelTypeSet model_types(THEMES);
416
417 StartSyncConfiguration();
418
419 // Make ConfigureSyncShare call scheduler->Stop(). It is not supposed to call
420 // retry_task or dereference configuration params.
421 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
422 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
423 StopScheduler(scheduler()),
424 RecordSyncShare(&times, false)));
425
426 CallbackCounter ready_counter;
427 CallbackCounter retry_counter;
428 ConfigurationParams params(
429 GetUpdatesCallerInfo::RECONFIGURATION,
430 model_types,
431 TypesToRoutingInfo(model_types),
432 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
433 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
434 scheduler()->ScheduleConfiguration(params);
435 PumpLoop();
436 ASSERT_EQ(0, ready_counter.times_called());
437 ASSERT_EQ(0, retry_counter.times_called());
438 }
439
440 // Issue a nudge when the config has failed. Make sure both the config and
441 // nudge are executed.
442 TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) {
443 const ModelTypeSet model_types(THEMES);
444 UseMockDelayProvider();
445 EXPECT_CALL(*delay(), GetDelay(_))
446 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50)));
447 SyncShareTimes times;
448
449 StartSyncConfiguration();
450
451 // Request a configure and make sure it fails.
452 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
453 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
454 RecordSyncShare(&times, false)));
455 CallbackCounter ready_counter;
456 CallbackCounter retry_counter;
457 ConfigurationParams params(
458 GetUpdatesCallerInfo::RECONFIGURATION,
459 model_types,
460 TypesToRoutingInfo(model_types),
461 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
462 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
463 scheduler()->ScheduleConfiguration(params);
464 RunLoop();
465 ASSERT_EQ(0, ready_counter.times_called());
466 ASSERT_EQ(1, retry_counter.times_called());
467 Mock::VerifyAndClearExpectations(syncer());
468
469 // Ask for a nudge while dealing with repeated configure failure.
470 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
471 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
472 RecordSyncShare(&times, false)));
473 scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
474 RunLoop();
475 // Note that we're not RunLoop()ing for the NUDGE we just scheduled, but
476 // for the first retry attempt from the config job (after
477 // waiting ~+/- 50ms).
478 Mock::VerifyAndClearExpectations(syncer());
479 ASSERT_EQ(0, ready_counter.times_called());
480
481 // Let the next configure retry succeed.
482 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
483 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
484 RecordSyncShare(&times, true)));
485 RunLoop();
486
487 // Now change the mode so nudge can execute.
488 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
489 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
490 RecordSyncShare(&times, true)));
491 StartSyncScheduler(base::Time());
492 PumpLoop();
493 }
494
495 // Test that nudges are coalesced.
496 TEST_F(SyncSchedulerTest, NudgeCoalescing) {
497 StartSyncScheduler(base::Time());
498
499 SyncShareTimes times;
500 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
501 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
502 RecordSyncShare(&times, true)));
503 const ModelTypeSet types1(THEMES), types2(TYPED_URLS), types3(THEMES);
504 TimeTicks optimal_time = TimeTicks::Now() + default_delay();
505 scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
506 scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
507 RunLoop();
508
509 ASSERT_EQ(1U, times.size());
510 EXPECT_GE(times[0], optimal_time);
511
512 Mock::VerifyAndClearExpectations(syncer());
513
514 SyncShareTimes times2;
515 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
516 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
517 RecordSyncShare(&times2, true)));
518 scheduler()->ScheduleLocalNudge(types3, FROM_HERE);
519 RunLoop();
520 }
521
522 // Test that nudges are coalesced.
523 TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) {
524 StartSyncScheduler(base::Time());
525
526 SyncShareTimes times;
527 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
528 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
529 RecordSyncShare(&times, true)));
530 ModelTypeSet types1(THEMES), types2(TYPED_URLS), types3;
531
532 // Create a huge time delay.
533 TimeDelta delay = TimeDelta::FromDays(1);
534
535 std::map<ModelType, TimeDelta> delay_map;
536 delay_map[types1.First().Get()] = delay;
537 scheduler()->OnReceivedCustomNudgeDelays(delay_map);
538 scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
539 scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
540
541 TimeTicks min_time = TimeTicks::Now();
542 TimeTicks max_time = TimeTicks::Now() + delay;
543
544 RunLoop();
545 Mock::VerifyAndClearExpectations(syncer());
546
547 // Make sure the sync happened at the right time.
548 ASSERT_EQ(1U, times.size());
549 EXPECT_GE(times[0], min_time);
550 EXPECT_LE(times[0], max_time);
551 }
552
553 // Test nudge scheduling.
554 TEST_F(SyncSchedulerTest, NudgeWithStates) {
555 StartSyncScheduler(base::Time());
556
557 SyncShareTimes times1;
558 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
559 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
560 RecordSyncShare(&times1, true)))
561 .RetiresOnSaturation();
562 scheduler()->ScheduleInvalidationNudge(
563 THEMES, BuildInvalidation(10, "test"), FROM_HERE);
564 RunLoop();
565
566 Mock::VerifyAndClearExpectations(syncer());
567
568 // Make sure a second, later, nudge is unaffected by first (no coalescing).
569 SyncShareTimes times2;
570 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
571 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
572 RecordSyncShare(&times2, true)));
573 scheduler()->ScheduleInvalidationNudge(
574 TYPED_URLS, BuildInvalidation(10, "test2"), FROM_HERE);
575 RunLoop();
576 }
577
578 // Test that polling works as expected.
579 TEST_F(SyncSchedulerTest, Polling) {
580 SyncShareTimes times;
581 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
582 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
583 .Times(AtLeast(kMinNumSamples))
584 .WillRepeatedly(
585 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
586 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
587
588 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
589
590 TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
591 StartSyncScheduler(base::Time());
592
593 // Run again to wait for polling.
594 RunLoop();
595
596 StopSyncScheduler();
597 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
598 }
599
600 // Test that we reuse the previous poll time on startup, triggering the first
601 // poll based on when the last one happened. Subsequent polls should have the
602 // normal delay.
603 TEST_F(SyncSchedulerTest, PollingPersistence) {
604 SyncShareTimes times;
605 // Use a large poll interval that wouldn't normally get hit on its own for
606 // some time yet.
607 TimeDelta poll_interval(TimeDelta::FromMilliseconds(500));
608 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
609 .Times(AtLeast(kMinNumSamples))
610 .WillRepeatedly(
611 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
612 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
613
614 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
615
616 // Set the start time to now, as the poll was overdue.
617 TimeTicks optimal_start = TimeTicks::Now();
618 StartSyncScheduler(base::Time::Now() - poll_interval);
619
620 // Run again to wait for polling.
621 RunLoop();
622
623 StopSyncScheduler();
624 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
625 }
626
627 // Test that if the persisted poll is in the future, it's ignored (the case
628 // where the local time has been modified).
629 TEST_F(SyncSchedulerTest, PollingPersistenceBadClock) {
630 SyncShareTimes times;
631 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
632 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
633 .Times(AtLeast(kMinNumSamples))
634 .WillRepeatedly(
635 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
636 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
637
638 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
639
640 // Set the start time to |poll_interval| in the future.
641 TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
642 StartSyncScheduler(base::Time::Now() + base::TimeDelta::FromMinutes(10));
643
644 // Run again to wait for polling.
645 RunLoop();
646
647 StopSyncScheduler();
648 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
649 }
650
651 // Test that the short poll interval is used.
652 TEST_F(SyncSchedulerTest, PollNotificationsDisabled) {
653 SyncShareTimes times;
654 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
655 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
656 .Times(AtLeast(kMinNumSamples))
657 .WillRepeatedly(
658 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
659 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
660
661 scheduler()->OnReceivedShortPollIntervalUpdate(poll_interval);
662 scheduler()->SetNotificationsEnabled(false);
663
664 TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
665 StartSyncScheduler(base::Time());
666
667 // Run again to wait for polling.
668 RunLoop();
669
670 StopSyncScheduler();
671 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll_interval);
672 }
673
674 // Test that polling intervals are updated when needed.
675 TEST_F(SyncSchedulerTest, PollIntervalUpdate) {
676 SyncShareTimes times;
677 TimeDelta poll1(TimeDelta::FromMilliseconds(120));
678 TimeDelta poll2(TimeDelta::FromMilliseconds(30));
679 scheduler()->OnReceivedLongPollIntervalUpdate(poll1);
680 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
681 .Times(AtLeast(kMinNumSamples))
682 .WillOnce(
683 DoAll(WithArgs<0, 1>(
684 sessions::test_util::SimulatePollIntervalUpdate(poll2)),
685 Return(true)))
686 .WillRepeatedly(DoAll(
687 Invoke(sessions::test_util::SimulatePollSuccess),
688 WithArg<1>(RecordSyncShareMultiple(&times, kMinNumSamples, true))));
689
690 TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2;
691 StartSyncScheduler(base::Time());
692
693 // Run again to wait for polling.
694 RunLoop();
695
696 StopSyncScheduler();
697 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll2);
698 }
699
700 // Test that no syncing occurs when throttled.
701 TEST_F(SyncSchedulerTest, ThrottlingDoesThrottle) {
702 const ModelTypeSet types(THEMES);
703 TimeDelta poll(TimeDelta::FromMilliseconds(20));
704 TimeDelta throttle(TimeDelta::FromMinutes(10));
705 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
706
707 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
708 .WillOnce(
709 DoAll(WithArg<2>(sessions::test_util::SimulateThrottled(throttle)),
710 Return(false)))
711 .WillRepeatedly(AddFailureAndQuitLoopNow());
712
713 StartSyncScheduler(base::Time());
714
715 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
716 PumpLoop();
717
718 StartSyncConfiguration();
719
720 CallbackCounter ready_counter;
721 CallbackCounter retry_counter;
722 ConfigurationParams params(
723 GetUpdatesCallerInfo::RECONFIGURATION,
724 types,
725 TypesToRoutingInfo(types),
726 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
727 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
728 scheduler()->ScheduleConfiguration(params);
729 PumpLoop();
730 ASSERT_EQ(0, ready_counter.times_called());
731 ASSERT_EQ(1, retry_counter.times_called());
732 }
733
734 TEST_F(SyncSchedulerTest, ThrottlingExpiresFromPoll) {
735 SyncShareTimes times;
736 TimeDelta poll(TimeDelta::FromMilliseconds(15));
737 TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
738 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
739
740 ::testing::InSequence seq;
741 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
742 .WillOnce(
743 DoAll(WithArg<1>(sessions::test_util::SimulateThrottled(throttle1)),
744 Return(false)))
745 .RetiresOnSaturation();
746 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
747 .WillRepeatedly(
748 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
749 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
750
751 TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1;
752 StartSyncScheduler(base::Time());
753
754 // Run again to wait for polling.
755 RunLoop();
756
757 StopSyncScheduler();
758 AnalyzePollRun(times, kMinNumSamples, optimal_start, poll);
759 }
760
761 TEST_F(SyncSchedulerTest, ThrottlingExpiresFromNudge) {
762 SyncShareTimes times;
763 TimeDelta poll(TimeDelta::FromDays(1));
764 TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
765 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
766
767 ::testing::InSequence seq;
768 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
769 .WillOnce(
770 DoAll(WithArg<2>(sessions::test_util::SimulateThrottled(throttle1)),
771 Return(false)))
772 .RetiresOnSaturation();
773 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
774 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
775 QuitLoopNowAction(true)));
776
777 const ModelTypeSet types(THEMES);
778 StartSyncScheduler(base::Time());
779 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
780
781 PumpLoop(); // To get PerformDelayedNudge called.
782 PumpLoop(); // To get TrySyncSessionJob called
783 EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
784 RunLoop();
785 EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
786
787 StopSyncScheduler();
788 }
789
790 TEST_F(SyncSchedulerTest, ThrottlingExpiresFromConfigure) {
791 SyncShareTimes times;
792 TimeDelta poll(TimeDelta::FromDays(1));
793 TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
794 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
795
796 ::testing::InSequence seq;
797 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
798 .WillOnce(
799 DoAll(WithArg<2>(sessions::test_util::SimulateThrottled(throttle1)),
800 Return(false)))
801 .RetiresOnSaturation();
802 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
803 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
804 QuitLoopNowAction(true)));
805
806 const ModelTypeSet types(THEMES);
807 StartSyncConfiguration();
808
809 CallbackCounter ready_counter;
810 CallbackCounter retry_counter;
811 ConfigurationParams params(
812 GetUpdatesCallerInfo::RECONFIGURATION,
813 types,
814 TypesToRoutingInfo(types),
815 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
816 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
817 scheduler()->ScheduleConfiguration(params);
818 PumpLoop();
819 EXPECT_EQ(0, ready_counter.times_called());
820 EXPECT_EQ(1, retry_counter.times_called());
821 EXPECT_TRUE(scheduler()->IsCurrentlyThrottled());
822
823 RunLoop();
824 EXPECT_FALSE(scheduler()->IsCurrentlyThrottled());
825
826 StopSyncScheduler();
827 }
828
829 TEST_F(SyncSchedulerTest, TypeThrottlingBlocksNudge) {
830 UseMockDelayProvider();
831 EXPECT_CALL(*delay(), GetDelay(_))
832 .WillRepeatedly(Return(default_delay()));
833
834 TimeDelta poll(TimeDelta::FromDays(1));
835 TimeDelta throttle1(TimeDelta::FromSeconds(60));
836 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
837
838 const ModelTypeSet types(THEMES);
839
840 ::testing::InSequence seq;
841 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
842 .WillOnce(DoAll(WithArg<2>(sessions::test_util::SimulateTypesThrottled(
843 types, throttle1)),
844 Return(false)))
845 .RetiresOnSaturation();
846
847 StartSyncScheduler(base::Time());
848 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
849 PumpLoop(); // To get PerformDelayedNudge called.
850 PumpLoop(); // To get TrySyncSessionJob called
851 EXPECT_TRUE(GetThrottledTypes().HasAll(types));
852
853 // This won't cause a sync cycle because the types are throttled.
854 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
855 PumpLoop();
856
857 StopSyncScheduler();
858 }
859
860 TEST_F(SyncSchedulerTest, TypeThrottlingDoesBlockOtherSources) {
861 UseMockDelayProvider();
862 EXPECT_CALL(*delay(), GetDelay(_))
863 .WillRepeatedly(Return(default_delay()));
864
865 SyncShareTimes times;
866 TimeDelta poll(TimeDelta::FromDays(1));
867 TimeDelta throttle1(TimeDelta::FromSeconds(60));
868 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
869
870 const ModelTypeSet throttled_types(THEMES);
871 const ModelTypeSet unthrottled_types(PREFERENCES);
872
873 ::testing::InSequence seq;
874 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
875 .WillOnce(DoAll(WithArg<2>(sessions::test_util::SimulateTypesThrottled(
876 throttled_types, throttle1)),
877 Return(false)))
878 .RetiresOnSaturation();
879
880 StartSyncScheduler(base::Time());
881 scheduler()->ScheduleLocalNudge(throttled_types, FROM_HERE);
882 PumpLoop(); // To get PerformDelayedNudge called.
883 PumpLoop(); // To get TrySyncSessionJob called
884 EXPECT_TRUE(GetThrottledTypes().HasAll(throttled_types));
885
886 // Ignore invalidations for throttled types.
887 scheduler()->ScheduleInvalidationNudge(
888 THEMES, BuildInvalidation(10, "test"), FROM_HERE);
889 PumpLoop();
890
891 // Ignore refresh requests for throttled types.
892 scheduler()->ScheduleLocalRefreshRequest(throttled_types, FROM_HERE);
893 PumpLoop();
894
895 Mock::VerifyAndClearExpectations(syncer());
896
897 // Local nudges for non-throttled types will trigger a sync.
898 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
899 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
900 RecordSyncShare(&times, true)));
901 scheduler()->ScheduleLocalNudge(unthrottled_types, FROM_HERE);
902 RunLoop();
903 Mock::VerifyAndClearExpectations(syncer());
904
905 StopSyncScheduler();
906 }
907
908 // Test nudges / polls don't run in config mode and config tasks do.
909 TEST_F(SyncSchedulerTest, ConfigurationMode) {
910 TimeDelta poll(TimeDelta::FromMilliseconds(15));
911 SyncShareTimes times;
912 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
913
914 StartSyncConfiguration();
915
916 const ModelTypeSet nudge_types(TYPED_URLS);
917 scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
918 scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
919
920 const ModelTypeSet config_types(THEMES);
921
922 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
923 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureSuccess),
924 RecordSyncShare(&times, true)))
925 .RetiresOnSaturation();
926 CallbackCounter ready_counter;
927 CallbackCounter retry_counter;
928 ConfigurationParams params(
929 GetUpdatesCallerInfo::RECONFIGURATION,
930 config_types,
931 TypesToRoutingInfo(config_types),
932 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
933 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
934 scheduler()->ScheduleConfiguration(params);
935 RunLoop();
936 ASSERT_EQ(1, ready_counter.times_called());
937 ASSERT_EQ(0, retry_counter.times_called());
938
939 Mock::VerifyAndClearExpectations(syncer());
940
941 // Switch to NORMAL_MODE to ensure NUDGES were properly saved and run.
942 scheduler()->OnReceivedLongPollIntervalUpdate(TimeDelta::FromDays(1));
943 SyncShareTimes times2;
944 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
945 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
946 RecordSyncShare(&times2, true)));
947
948 // TODO(tim): Figure out how to remove this dangerous need to reset
949 // routing info between mode switches.
950 context()->SetRoutingInfo(routing_info());
951 StartSyncScheduler(base::Time());
952
953 RunLoop();
954 Mock::VerifyAndClearExpectations(syncer());
955 }
956
957 class BackoffTriggersSyncSchedulerTest : public SyncSchedulerTest {
958 void SetUp() override {
959 SyncSchedulerTest::SetUp();
960 UseMockDelayProvider();
961 EXPECT_CALL(*delay(), GetDelay(_))
962 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(10)));
963 }
964
965 void TearDown() override {
966 StopSyncScheduler();
967 SyncSchedulerTest::TearDown();
968 }
969 };
970
971 // Have the syncer fail during commit. Expect that the scheduler enters
972 // backoff.
973 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnce) {
974 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
975 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
976 QuitLoopNowAction(false)));
977 EXPECT_TRUE(RunAndGetBackoff());
978 }
979
980 // Have the syncer fail during download updates and succeed on the first
981 // retry. Expect that this clears the backoff state.
982 TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadOnceThenSucceed) {
983 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
984 .WillOnce(
985 DoAll(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
986 Return(false)))
987 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
988 QuitLoopNowAction(true)));
989 EXPECT_FALSE(RunAndGetBackoff());
990 }
991
992 // Have the syncer fail during commit and succeed on the first retry. Expect
993 // that this clears the backoff state.
994 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnceThenSucceed) {
995 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
996 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
997 Return(false)))
998 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
999 QuitLoopNowAction(true)));
1000 EXPECT_FALSE(RunAndGetBackoff());
1001 }
1002
1003 // Have the syncer fail to download updates and fail again on the retry.
1004 // Expect this will leave the scheduler in backoff.
1005 TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadTwice) {
1006 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1007 .WillOnce(
1008 DoAll(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
1009 Return(false)))
1010 .WillRepeatedly(
1011 DoAll(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
1012 QuitLoopNowAction(false)));
1013 EXPECT_TRUE(RunAndGetBackoff());
1014 }
1015
1016 // Have the syncer fail to get the encryption key yet succeed in downloading
1017 // updates. Expect this will leave the scheduler in backoff.
1018 TEST_F(BackoffTriggersSyncSchedulerTest, FailGetEncryptionKey) {
1019 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
1020 .WillOnce(
1021 DoAll(Invoke(sessions::test_util::SimulateGetEncryptionKeyFailed),
1022 Return(false)))
1023 .WillRepeatedly(
1024 DoAll(Invoke(sessions::test_util::SimulateGetEncryptionKeyFailed),
1025 QuitLoopNowAction(false)));
1026 StartSyncConfiguration();
1027
1028 ModelTypeSet types(THEMES);
1029 CallbackCounter ready_counter;
1030 CallbackCounter retry_counter;
1031 ConfigurationParams params(
1032 GetUpdatesCallerInfo::RECONFIGURATION,
1033 types,
1034 TypesToRoutingInfo(types),
1035 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
1036 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
1037 scheduler()->ScheduleConfiguration(params);
1038 RunLoop();
1039
1040 EXPECT_TRUE(scheduler()->IsBackingOff());
1041 }
1042
1043 // Test that no polls or extraneous nudges occur when in backoff.
1044 TEST_F(SyncSchedulerTest, BackoffDropsJobs) {
1045 SyncShareTimes times;
1046 TimeDelta poll(TimeDelta::FromMilliseconds(10));
1047 const ModelTypeSet types(THEMES);
1048 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
1049 UseMockDelayProvider();
1050
1051 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1052 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
1053 RecordSyncShareMultiple(&times, 1U, false)));
1054 EXPECT_CALL(*delay(), GetDelay(_)).
1055 WillRepeatedly(Return(TimeDelta::FromDays(1)));
1056
1057 StartSyncScheduler(base::Time());
1058
1059 // This nudge should fail and put us into backoff. Thanks to our mock
1060 // GetDelay() setup above, this will be a long backoff.
1061 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
1062 RunLoop();
1063
1064 // From this point forward, no SyncShare functions should be invoked.
1065 Mock::VerifyAndClearExpectations(syncer());
1066
1067 // Wait a while (10x poll interval) so a few poll jobs will be attempted.
1068 PumpLoopFor(poll * 10);
1069
1070 // Try (and fail) to schedule a nudge.
1071 scheduler()->ScheduleLocalNudge(types, FROM_HERE);
1072
1073 Mock::VerifyAndClearExpectations(syncer());
1074 Mock::VerifyAndClearExpectations(delay());
1075
1076 EXPECT_CALL(*delay(), GetDelay(_)).Times(0);
1077
1078 StartSyncConfiguration();
1079
1080 CallbackCounter ready_counter;
1081 CallbackCounter retry_counter;
1082 ConfigurationParams params(
1083 GetUpdatesCallerInfo::RECONFIGURATION,
1084 types,
1085 TypesToRoutingInfo(types),
1086 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
1087 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
1088 scheduler()->ScheduleConfiguration(params);
1089 PumpLoop();
1090 ASSERT_EQ(0, ready_counter.times_called());
1091 ASSERT_EQ(1, retry_counter.times_called());
1092 }
1093
1094 // Test that backoff is shaping traffic properly with consecutive errors.
1095 TEST_F(SyncSchedulerTest, BackoffElevation) {
1096 SyncShareTimes times;
1097 UseMockDelayProvider();
1098
1099 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1100 .Times(kMinNumSamples)
1101 .WillRepeatedly(
1102 DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
1103 RecordSyncShareMultiple(&times, kMinNumSamples, false)));
1104
1105 const TimeDelta first = TimeDelta::FromSeconds(kInitialBackoffRetrySeconds);
1106 const TimeDelta second = TimeDelta::FromMilliseconds(20);
1107 const TimeDelta third = TimeDelta::FromMilliseconds(30);
1108 const TimeDelta fourth = TimeDelta::FromMilliseconds(40);
1109 const TimeDelta fifth = TimeDelta::FromMilliseconds(50);
1110 const TimeDelta sixth = TimeDelta::FromDays(1);
1111
1112 EXPECT_CALL(*delay(), GetDelay(first)).WillOnce(Return(second))
1113 .RetiresOnSaturation();
1114 EXPECT_CALL(*delay(), GetDelay(second)).WillOnce(Return(third))
1115 .RetiresOnSaturation();
1116 EXPECT_CALL(*delay(), GetDelay(third)).WillOnce(Return(fourth))
1117 .RetiresOnSaturation();
1118 EXPECT_CALL(*delay(), GetDelay(fourth)).WillOnce(Return(fifth))
1119 .RetiresOnSaturation();
1120 EXPECT_CALL(*delay(), GetDelay(fifth)).WillOnce(Return(sixth));
1121
1122 StartSyncScheduler(base::Time());
1123
1124 // Run again with a nudge.
1125 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1126 RunLoop();
1127
1128 ASSERT_EQ(kMinNumSamples, times.size());
1129 EXPECT_GE(times[1] - times[0], second);
1130 EXPECT_GE(times[2] - times[1], third);
1131 EXPECT_GE(times[3] - times[2], fourth);
1132 EXPECT_GE(times[4] - times[3], fifth);
1133 }
1134
1135 // Test that things go back to normal once a retry makes forward progress.
1136 TEST_F(SyncSchedulerTest, BackoffRelief) {
1137 SyncShareTimes times;
1138 UseMockDelayProvider();
1139
1140 const TimeDelta backoff = TimeDelta::FromMilliseconds(10);
1141 EXPECT_CALL(*delay(), GetDelay(_)).WillOnce(Return(backoff));
1142
1143 // Optimal start for the post-backoff poll party.
1144 TimeTicks optimal_start = TimeTicks::Now();
1145 StartSyncScheduler(base::Time());
1146
1147 // Kick off the test with a failed nudge.
1148 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1149 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
1150 RecordSyncShare(&times, false)));
1151 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1152 RunLoop();
1153 Mock::VerifyAndClearExpectations(syncer());
1154 TimeTicks optimal_job_time = optimal_start;
1155 ASSERT_EQ(1U, times.size());
1156 EXPECT_GE(times[0], optimal_job_time);
1157
1158 // The retry succeeds.
1159 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1160 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1161 RecordSyncShare(&times, true)));
1162 RunLoop();
1163 Mock::VerifyAndClearExpectations(syncer());
1164 optimal_job_time = optimal_job_time + backoff;
1165 ASSERT_EQ(2U, times.size());
1166 EXPECT_GE(times[1], optimal_job_time);
1167
1168 // Now let the Poll timer do its thing.
1169 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
1170 .WillRepeatedly(
1171 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
1172 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
1173 const TimeDelta poll(TimeDelta::FromMilliseconds(10));
1174 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
1175
1176 // The new optimal time is now, since the desired poll should have happened
1177 // in the past.
1178 optimal_job_time = base::TimeTicks::Now();
1179 RunLoop();
1180 Mock::VerifyAndClearExpectations(syncer());
1181 ASSERT_EQ(kMinNumSamples, times.size());
1182 for (size_t i = 2; i < times.size(); i++) {
1183 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
1184 EXPECT_GE(times[i], optimal_job_time);
1185 optimal_job_time = optimal_job_time + poll;
1186 }
1187
1188 StopSyncScheduler();
1189 }
1190
1191 // Test that poll failures are treated like any other failure. They should
1192 // result in retry with backoff.
1193 TEST_F(SyncSchedulerTest, TransientPollFailure) {
1194 SyncShareTimes times;
1195 const TimeDelta poll_interval(TimeDelta::FromMilliseconds(10));
1196 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
1197 UseMockDelayProvider(); // Will cause test failure if backoff is initiated.
1198 EXPECT_CALL(*delay(), GetDelay(_))
1199 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
1200
1201 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
1202 .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollFailed),
1203 RecordSyncShare(&times, false)))
1204 .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
1205 RecordSyncShare(&times, true)));
1206
1207 StartSyncScheduler(base::Time());
1208
1209 // Run the unsuccessful poll. The failed poll should not trigger backoff.
1210 RunLoop();
1211 EXPECT_TRUE(scheduler()->IsBackingOff());
1212
1213 // Run the successful poll.
1214 RunLoop();
1215 EXPECT_FALSE(scheduler()->IsBackingOff());
1216 }
1217
1218 // Test that starting the syncer thread without a valid connection doesn't
1219 // break things when a connection is detected.
1220 TEST_F(SyncSchedulerTest, StartWhenNotConnected) {
1221 connection()->SetServerNotReachable();
1222 connection()->UpdateConnectionStatus();
1223 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1224 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
1225 Return(false)))
1226 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1227 Return(true)));
1228 StartSyncScheduler(base::Time());
1229
1230 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1231 // Should save the nudge for until after the server is reachable.
1232 base::RunLoop().RunUntilIdle();
1233
1234 scheduler()->OnConnectionStatusChange();
1235 connection()->SetServerReachable();
1236 connection()->UpdateConnectionStatus();
1237 base::RunLoop().RunUntilIdle();
1238 }
1239
1240 TEST_F(SyncSchedulerTest, ServerConnectionChangeDuringBackoff) {
1241 UseMockDelayProvider();
1242 EXPECT_CALL(*delay(), GetDelay(_))
1243 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
1244
1245 StartSyncScheduler(base::Time());
1246 connection()->SetServerNotReachable();
1247 connection()->UpdateConnectionStatus();
1248
1249 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1250 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
1251 Return(false)))
1252 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1253 Return(true)));
1254
1255 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1256 PumpLoop(); // To get PerformDelayedNudge called.
1257 PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
1258 ASSERT_TRUE(scheduler()->IsBackingOff());
1259
1260 // Before we run the scheduled canary, trigger a server connection change.
1261 scheduler()->OnConnectionStatusChange();
1262 connection()->SetServerReachable();
1263 connection()->UpdateConnectionStatus();
1264 base::RunLoop().RunUntilIdle();
1265 }
1266
1267 // This was supposed to test the scenario where we receive a nudge while a
1268 // connection change canary is scheduled, but has not run yet. Since we've made
1269 // the connection change canary synchronous, this is no longer possible.
1270 TEST_F(SyncSchedulerTest, ConnectionChangeCanaryPreemptedByNudge) {
1271 UseMockDelayProvider();
1272 EXPECT_CALL(*delay(), GetDelay(_))
1273 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));
1274
1275 StartSyncScheduler(base::Time());
1276 connection()->SetServerNotReachable();
1277 connection()->UpdateConnectionStatus();
1278
1279 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1280 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
1281 Return(false)))
1282 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1283 Return(true)))
1284 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1285 QuitLoopNowAction(true)));
1286
1287 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1288
1289 PumpLoop(); // To get PerformDelayedNudge called.
1290 PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
1291 ASSERT_TRUE(scheduler()->IsBackingOff());
1292
1293 // Before we run the scheduled canary, trigger a server connection change.
1294 scheduler()->OnConnectionStatusChange();
1295 PumpLoop();
1296 connection()->SetServerReachable();
1297 connection()->UpdateConnectionStatus();
1298 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1299 base::RunLoop().RunUntilIdle();
1300 }
1301
1302 // Tests that we don't crash trying to run two canaries at once if we receive
1303 // extra connection status change notifications. See crbug.com/190085.
1304 TEST_F(SyncSchedulerTest, DoubleCanaryInConfigure) {
1305 EXPECT_CALL(*syncer(), ConfigureSyncShare(_, _, _))
1306 .WillRepeatedly(
1307 DoAll(Invoke(sessions::test_util::SimulateConfigureConnectionFailure),
1308 Return(true)));
1309 StartSyncConfiguration();
1310 connection()->SetServerNotReachable();
1311 connection()->UpdateConnectionStatus();
1312
1313 ModelTypeSet model_types(THEMES);
1314 CallbackCounter ready_counter;
1315 CallbackCounter retry_counter;
1316 ConfigurationParams params(
1317 GetUpdatesCallerInfo::RECONFIGURATION,
1318 model_types,
1319 TypesToRoutingInfo(model_types),
1320 base::Bind(&CallbackCounter::Callback, base::Unretained(&ready_counter)),
1321 base::Bind(&CallbackCounter::Callback, base::Unretained(&retry_counter)));
1322 scheduler()->ScheduleConfiguration(params);
1323
1324 scheduler()->OnConnectionStatusChange();
1325 scheduler()->OnConnectionStatusChange();
1326
1327 PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
1328 }
1329
1330 TEST_F(SyncSchedulerTest, PollFromCanaryAfterAuthError) {
1331 SyncShareTimes times;
1332 TimeDelta poll(TimeDelta::FromMilliseconds(15));
1333 scheduler()->OnReceivedLongPollIntervalUpdate(poll);
1334
1335 ::testing::InSequence seq;
1336 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
1337 .WillRepeatedly(
1338 DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
1339 RecordSyncShareMultiple(&times, kMinNumSamples, true)));
1340
1341 connection()->SetServerStatus(HttpResponse::SYNC_AUTH_ERROR);
1342 StartSyncScheduler(base::Time());
1343
1344 // Run to wait for polling.
1345 RunLoop();
1346
1347 // Normally OnCredentialsUpdated calls TryCanaryJob that doesn't run Poll,
1348 // but after poll finished with auth error from poll timer it should retry
1349 // poll once more
1350 EXPECT_CALL(*syncer(), PollSyncShare(_, _))
1351 .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
1352 RecordSyncShare(&times, true)));
1353 scheduler()->OnCredentialsUpdated();
1354 connection()->SetServerStatus(HttpResponse::SERVER_CONNECTION_OK);
1355 RunLoop();
1356 StopSyncScheduler();
1357 }
1358
1359 TEST_F(SyncSchedulerTest, SuccessfulRetry) {
1360 StartSyncScheduler(base::Time());
1361
1362 SyncShareTimes times;
1363 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
1364 scheduler()->OnReceivedGuRetryDelay(delay);
1365 EXPECT_EQ(delay, GetRetryTimerDelay());
1366
1367 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1368 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1369 RecordSyncShare(&times, true)));
1370
1371 // Run to wait for retrying.
1372 RunLoop();
1373
1374 StopSyncScheduler();
1375 }
1376
1377 TEST_F(SyncSchedulerTest, FailedRetry) {
1378 SyncShareTimes times;
1379
1380 UseMockDelayProvider();
1381 EXPECT_CALL(*delay(), GetDelay(_))
1382 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(10)));
1383
1384 StartSyncScheduler(base::Time());
1385
1386 base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
1387 scheduler()->OnReceivedGuRetryDelay(delay);
1388
1389 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1390 .WillOnce(
1391 DoAll(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
1392 RecordSyncShare(&times, false)));
1393
1394 // Run to wait for retrying.
1395 RunLoop();
1396
1397 EXPECT_TRUE(scheduler()->IsBackingOff());
1398 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1399 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1400 RecordSyncShare(&times, true)));
1401
1402 // Run to wait for second retrying.
1403 RunLoop();
1404
1405 StopSyncScheduler();
1406 }
1407
1408 ACTION_P2(VerifyRetryTimerDelay, scheduler_test, expected_delay) {
1409 EXPECT_EQ(expected_delay, scheduler_test->GetRetryTimerDelay());
1410 }
1411
1412 TEST_F(SyncSchedulerTest, ReceiveNewRetryDelay) {
1413 StartSyncScheduler(base::Time());
1414
1415 SyncShareTimes times;
1416 base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(100);
1417 base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(200);
1418
1419 scheduler()->ScheduleLocalNudge(ModelTypeSet(THEMES), FROM_HERE);
1420 scheduler()->OnReceivedGuRetryDelay(delay1);
1421 EXPECT_EQ(delay1, GetRetryTimerDelay());
1422
1423 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1424 .WillOnce(DoAll(
1425 WithoutArgs(VerifyRetryTimerDelay(this, delay1)),
1426 WithArg<2>(sessions::test_util::SimulateGuRetryDelayCommand(delay2)),
1427 RecordSyncShare(&times, true)));
1428
1429 // Run nudge GU.
1430 RunLoop();
1431 EXPECT_EQ(delay2, GetRetryTimerDelay());
1432
1433 EXPECT_CALL(*syncer(), NormalSyncShare(_, _, _))
1434 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
1435 RecordSyncShare(&times, true)));
1436
1437 // Run to wait for retrying.
1438 RunLoop();
1439
1440 StopSyncScheduler();
1441 }
1442
1443 TEST_F(SyncSchedulerTest, ScheduleClearServerData_Succeeds) {
1444 StartSyncConfiguration();
1445 scheduler()->Start(SyncScheduler::CLEAR_SERVER_DATA_MODE, base::Time());
1446 CallbackCounter success_counter;
1447 ClearParams params(base::Bind(&CallbackCounter::Callback,
1448 base::Unretained(&success_counter)));
1449 scheduler()->ScheduleClearServerData(params);
1450 PumpLoop();
1451 ASSERT_EQ(1, success_counter.times_called());
1452 }
1453
1454 TEST_F(SyncSchedulerTest, ScheduleClearServerData_FailsRetriesSucceeds) {
1455 UseMockDelayProvider();
1456 TimeDelta delta(TimeDelta::FromMilliseconds(20));
1457 EXPECT_CALL(*delay(), GetDelay(_)).WillRepeatedly(Return(delta));
1458
1459 StartSyncConfiguration();
1460 scheduler()->Start(SyncScheduler::CLEAR_SERVER_DATA_MODE, base::Time());
1461 CallbackCounter success_counter;
1462 ClearParams params(base::Bind(&CallbackCounter::Callback,
1463 base::Unretained(&success_counter)));
1464
1465 // Next request will fail.
1466 connection()->SetServerNotReachable();
1467 scheduler()->ScheduleClearServerData(params);
1468 PumpLoop();
1469 ASSERT_EQ(0, success_counter.times_called());
1470 ASSERT_TRUE(scheduler()->IsBackingOff());
1471
1472 // Now succeed.
1473 connection()->SetServerReachable();
1474 PumpLoopFor(2 * delta);
1475 ASSERT_EQ(1, success_counter.times_called());
1476 ASSERT_FALSE(scheduler()->IsBackingOff());
1477 }
1478
1479 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/engine/sync_scheduler_impl.cc ('k') | sync/engine/syncer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698