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