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 "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(×, 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(×2, 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(×, 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(×, false))) | |
376 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed), | |
377 RecordSyncShare(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×2, 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(×, 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(×1, 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(×2, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×2, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, false))) | |
1204 .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollSuccess), | |
1205 RecordSyncShare(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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(×, 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 | |
OLD | NEW |