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 "base/bind.h" | |
6 #include "base/callback.h" | |
7 #include "base/compiler_specific.h" | |
8 #include "base/memory/weak_ptr.h" | |
9 #include "base/message_loop.h" | |
10 #include "base/test/test_timeouts.h" | |
11 #include "chrome/browser/sync/engine/sync_scheduler.h" | |
12 #include "chrome/browser/sync/engine/syncer.h" | |
13 #include "chrome/browser/sync/sessions/test_util.h" | |
14 #include "chrome/browser/sync/test/engine/fake_model_safe_worker_registrar.h" | |
15 #include "chrome/browser/sync/test/engine/mock_connection_manager.h" | |
16 #include "chrome/browser/sync/test/engine/test_directory_setter_upper.h" | |
17 #include "chrome/browser/sync/test/fake_extensions_activity_monitor.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 #include "testing/gmock/include/gmock/gmock.h" | |
20 | |
21 using base::TimeDelta; | |
22 using base::TimeTicks; | |
23 using testing::_; | |
24 using testing::AtLeast; | |
25 using testing::DoAll; | |
26 using testing::Eq; | |
27 using testing::Invoke; | |
28 using testing::Mock; | |
29 using testing::Return; | |
30 using testing::WithArg; | |
31 | |
32 namespace browser_sync { | |
33 using sessions::SyncSession; | |
34 using sessions::SyncSessionContext; | |
35 using sessions::SyncSessionSnapshot; | |
36 using syncable::ModelTypeSet; | |
37 using sync_pb::GetUpdatesCallerInfo; | |
38 | |
39 class MockSyncer : public Syncer { | |
40 public: | |
41 MOCK_METHOD3(SyncShare, void(sessions::SyncSession*, SyncerStep, | |
42 SyncerStep)); | |
43 }; | |
44 | |
45 // Used when tests want to record syncing activity to examine later. | |
46 struct SyncShareRecords { | |
47 std::vector<TimeTicks> times; | |
48 std::vector<linked_ptr<SyncSessionSnapshot> > snapshots; | |
49 }; | |
50 | |
51 void QuitLoopNow() { | |
52 // We use QuitNow() instead of Quit() as the latter may get stalled | |
53 // indefinitely in the presence of repeated timers with low delays | |
54 // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll | |
55 // delay of 5ms] run under TSAN on the trybots). | |
56 MessageLoop::current()->QuitNow(); | |
57 } | |
58 | |
59 void RunLoop() { | |
60 MessageLoop::current()->Run(); | |
61 } | |
62 | |
63 void PumpLoop() { | |
64 // Do it this way instead of RunAllPending to pump loop exactly once | |
65 // (necessary in the presence of timers; see comment in | |
66 // QuitLoopNow). | |
67 MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&QuitLoopNow)); | |
68 RunLoop(); | |
69 } | |
70 | |
71 // Convenient to use in tests wishing to analyze SyncShare calls over time. | |
72 static const size_t kMinNumSamples = 5; | |
73 class SyncSchedulerTest : public testing::Test { | |
74 public: | |
75 SyncSchedulerTest() | |
76 : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | |
77 context_(NULL), | |
78 syncer_(NULL), | |
79 delay_(NULL) {} | |
80 | |
81 class MockDelayProvider : public SyncScheduler::DelayProvider { | |
82 public: | |
83 MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&)); | |
84 }; | |
85 | |
86 virtual void SetUp() { | |
87 dir_maker_.SetUp(); | |
88 syncer_ = new MockSyncer(); | |
89 delay_ = NULL; | |
90 ModelSafeRoutingInfo routing_info; | |
91 routing_info[syncable::BOOKMARKS] = GROUP_UI; | |
92 routing_info[syncable::AUTOFILL] = GROUP_DB; | |
93 routing_info[syncable::THEMES] = GROUP_UI; | |
94 routing_info[syncable::NIGORI] = GROUP_PASSIVE; | |
95 registrar_.reset(new FakeModelSafeWorkerRegistrar(routing_info)); | |
96 connection_.reset(new MockConnectionManager(directory())); | |
97 connection_->SetServerReachable(); | |
98 context_ = new SyncSessionContext( | |
99 connection_.get(), directory(), registrar_.get(), | |
100 &extensions_activity_monitor_, | |
101 std::vector<SyncEngineEventListener*>(), NULL); | |
102 context_->set_notifications_enabled(true); | |
103 context_->set_account_name("Test"); | |
104 scheduler_.reset( | |
105 new SyncScheduler("TestSyncScheduler", context_, syncer_)); | |
106 } | |
107 | |
108 SyncScheduler* scheduler() { return scheduler_.get(); } | |
109 MockSyncer* syncer() { return syncer_; } | |
110 MockDelayProvider* delay() { return delay_; } | |
111 MockConnectionManager* connection() { return connection_.get(); } | |
112 TimeDelta zero() { return TimeDelta::FromSeconds(0); } | |
113 TimeDelta timeout() { | |
114 return TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms()); | |
115 } | |
116 | |
117 virtual void TearDown() { | |
118 PumpLoop(); | |
119 scheduler_.reset(); | |
120 PumpLoop(); | |
121 dir_maker_.TearDown(); | |
122 } | |
123 | |
124 void AnalyzePollRun(const SyncShareRecords& records, size_t min_num_samples, | |
125 const TimeTicks& optimal_start, const TimeDelta& poll_interval) { | |
126 const std::vector<TimeTicks>& data(records.times); | |
127 EXPECT_GE(data.size(), min_num_samples); | |
128 for (size_t i = 0; i < data.size(); i++) { | |
129 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); | |
130 TimeTicks optimal_next_sync = optimal_start + poll_interval * i; | |
131 EXPECT_GE(data[i], optimal_next_sync); | |
132 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, | |
133 records.snapshots[i]->source.updates_source); | |
134 } | |
135 } | |
136 | |
137 void DoQuitLoopNow() { | |
138 QuitLoopNow(); | |
139 } | |
140 | |
141 void StartSyncScheduler(SyncScheduler::Mode mode) { | |
142 scheduler()->Start( | |
143 mode, | |
144 base::Bind(&SyncSchedulerTest::DoQuitLoopNow, | |
145 weak_ptr_factory_.GetWeakPtr())); | |
146 } | |
147 | |
148 // This stops the scheduler synchronously. | |
149 void StopSyncScheduler() { | |
150 scheduler()->RequestStop(base::Bind(&SyncSchedulerTest::DoQuitLoopNow, | |
151 weak_ptr_factory_.GetWeakPtr())); | |
152 RunLoop(); | |
153 } | |
154 | |
155 bool RunAndGetBackoff() { | |
156 ModelTypeSet nudge_types; | |
157 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
158 RunLoop(); | |
159 | |
160 scheduler()->ScheduleNudge( | |
161 zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); | |
162 RunLoop(); | |
163 | |
164 return scheduler()->IsBackingOff(); | |
165 } | |
166 | |
167 void UseMockDelayProvider() { | |
168 delay_ = new MockDelayProvider(); | |
169 scheduler_->delay_provider_.reset(delay_); | |
170 } | |
171 | |
172 // Compare a ModelTypeSet to a ModelTypePayloadMap, ignoring | |
173 // payload values. | |
174 bool CompareModelTypeSetToModelTypePayloadMap( | |
175 ModelTypeSet lhs, | |
176 const syncable::ModelTypePayloadMap& rhs) { | |
177 size_t count = 0; | |
178 for (syncable::ModelTypePayloadMap::const_iterator i = rhs.begin(); | |
179 i != rhs.end(); ++i, ++count) { | |
180 if (!lhs.Has(i->first)) | |
181 return false; | |
182 } | |
183 if (lhs.Size() != count) | |
184 return false; | |
185 return true; | |
186 } | |
187 | |
188 SyncSessionContext* context() { return context_; } | |
189 | |
190 private: | |
191 syncable::Directory* directory() { | |
192 return dir_maker_.directory(); | |
193 } | |
194 | |
195 base::WeakPtrFactory<SyncSchedulerTest> weak_ptr_factory_; | |
196 MessageLoop message_loop_; | |
197 TestDirectorySetterUpper dir_maker_; | |
198 scoped_ptr<SyncScheduler> scheduler_; | |
199 scoped_ptr<MockConnectionManager> connection_; | |
200 SyncSessionContext* context_; | |
201 MockSyncer* syncer_; | |
202 MockDelayProvider* delay_; | |
203 scoped_ptr<FakeModelSafeWorkerRegistrar> registrar_; | |
204 FakeExtensionsActivityMonitor extensions_activity_monitor_; | |
205 }; | |
206 | |
207 class BackoffTriggersSyncSchedulerTest : public SyncSchedulerTest { | |
208 void SetUp() { | |
209 SyncSchedulerTest::SetUp(); | |
210 UseMockDelayProvider(); | |
211 EXPECT_CALL(*delay(), GetDelay(_)) | |
212 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1))); | |
213 } | |
214 | |
215 void TearDown() { | |
216 StopSyncScheduler(); | |
217 SyncSchedulerTest::TearDown(); | |
218 } | |
219 }; | |
220 | |
221 void RecordSyncShareImpl(SyncSession* s, SyncShareRecords* record) { | |
222 record->times.push_back(TimeTicks::Now()); | |
223 record->snapshots.push_back(make_linked_ptr(new SyncSessionSnapshot( | |
224 s->TakeSnapshot()))); | |
225 } | |
226 | |
227 ACTION_P(RecordSyncShare, record) { | |
228 RecordSyncShareImpl(arg0, record); | |
229 QuitLoopNow(); | |
230 } | |
231 | |
232 ACTION_P2(RecordSyncShareMultiple, record, quit_after) { | |
233 RecordSyncShareImpl(arg0, record); | |
234 EXPECT_LE(record->times.size(), quit_after); | |
235 if (record->times.size() >= quit_after) { | |
236 QuitLoopNow(); | |
237 } | |
238 } | |
239 | |
240 ACTION(AddFailureAndQuitLoopNow) { | |
241 ADD_FAILURE(); | |
242 QuitLoopNow(); | |
243 } | |
244 | |
245 ACTION(QuitLoopNowAction) { | |
246 QuitLoopNow(); | |
247 } | |
248 | |
249 // Test nudge scheduling. | |
250 TEST_F(SyncSchedulerTest, Nudge) { | |
251 SyncShareRecords records; | |
252 ModelTypeSet model_types(syncable::BOOKMARKS); | |
253 | |
254 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
255 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
256 WithArg<0>(RecordSyncShare(&records)))) | |
257 .RetiresOnSaturation(); | |
258 | |
259 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
260 RunLoop(); | |
261 | |
262 scheduler()->ScheduleNudge( | |
263 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); | |
264 RunLoop(); | |
265 | |
266 ASSERT_EQ(1U, records.snapshots.size()); | |
267 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | |
268 records.snapshots[0]->source.types)); | |
269 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
270 records.snapshots[0]->source.updates_source); | |
271 | |
272 Mock::VerifyAndClearExpectations(syncer()); | |
273 | |
274 // Make sure a second, later, nudge is unaffected by first (no coalescing). | |
275 SyncShareRecords records2; | |
276 model_types.Remove(syncable::BOOKMARKS); | |
277 model_types.Put(syncable::AUTOFILL); | |
278 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
279 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
280 WithArg<0>(RecordSyncShare(&records2)))); | |
281 scheduler()->ScheduleNudge( | |
282 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); | |
283 RunLoop(); | |
284 | |
285 ASSERT_EQ(1U, records2.snapshots.size()); | |
286 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | |
287 records2.snapshots[0]->source.types)); | |
288 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
289 records2.snapshots[0]->source.updates_source); | |
290 } | |
291 | |
292 // Make sure a regular config command is scheduled fine in the absence of any | |
293 // errors. | |
294 TEST_F(SyncSchedulerTest, Config) { | |
295 SyncShareRecords records; | |
296 const ModelTypeSet model_types(syncable::BOOKMARKS); | |
297 | |
298 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
299 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
300 WithArg<0>(RecordSyncShare(&records)))); | |
301 | |
302 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
303 RunLoop(); | |
304 | |
305 scheduler()->ScheduleConfig( | |
306 model_types, GetUpdatesCallerInfo::RECONFIGURATION); | |
307 RunLoop(); | |
308 | |
309 ASSERT_EQ(1U, records.snapshots.size()); | |
310 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | |
311 records.snapshots[0]->source.types)); | |
312 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | |
313 records.snapshots[0]->source.updates_source); | |
314 } | |
315 | |
316 // Simulate a failure and make sure the config request is retried. | |
317 TEST_F(SyncSchedulerTest, ConfigWithBackingOff) { | |
318 UseMockDelayProvider(); | |
319 EXPECT_CALL(*delay(), GetDelay(_)) | |
320 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1))); | |
321 SyncShareRecords records; | |
322 const ModelTypeSet model_types(syncable::BOOKMARKS); | |
323 | |
324 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
325 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
326 WithArg<0>(RecordSyncShare(&records)))) | |
327 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
328 WithArg<0>(RecordSyncShare(&records)))); | |
329 | |
330 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
331 RunLoop(); | |
332 | |
333 ASSERT_EQ(0U, records.snapshots.size()); | |
334 scheduler()->ScheduleConfig( | |
335 model_types, GetUpdatesCallerInfo::RECONFIGURATION); | |
336 RunLoop(); | |
337 | |
338 ASSERT_EQ(1U, records.snapshots.size()); | |
339 RunLoop(); | |
340 | |
341 ASSERT_EQ(2U, records.snapshots.size()); | |
342 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | |
343 records.snapshots[1]->source.types)); | |
344 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | |
345 records.snapshots[1]->source.updates_source); | |
346 } | |
347 | |
348 // Issue 2 config commands. Second one right after the first has failed | |
349 // and make sure LATEST is executed. | |
350 TEST_F(SyncSchedulerTest, MultipleConfigWithBackingOff) { | |
351 const ModelTypeSet | |
352 model_types1(syncable::BOOKMARKS), | |
353 model_types2(syncable::AUTOFILL); | |
354 UseMockDelayProvider(); | |
355 EXPECT_CALL(*delay(), GetDelay(_)) | |
356 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(30))); | |
357 SyncShareRecords records; | |
358 | |
359 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
360 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
361 WithArg<0>(RecordSyncShare(&records)))) | |
362 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
363 WithArg<0>(RecordSyncShare(&records)))) | |
364 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
365 WithArg<0>(RecordSyncShare(&records)))); | |
366 | |
367 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
368 RunLoop(); | |
369 | |
370 ASSERT_EQ(0U, records.snapshots.size()); | |
371 scheduler()->ScheduleConfig( | |
372 model_types1, GetUpdatesCallerInfo::RECONFIGURATION); | |
373 RunLoop(); | |
374 | |
375 ASSERT_EQ(1U, records.snapshots.size()); | |
376 scheduler()->ScheduleConfig( | |
377 model_types2, GetUpdatesCallerInfo::RECONFIGURATION); | |
378 RunLoop(); | |
379 | |
380 ASSERT_EQ(2U, records.snapshots.size()); | |
381 RunLoop(); | |
382 | |
383 ASSERT_EQ(3U, records.snapshots.size()); | |
384 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types2, | |
385 records.snapshots[2]->source.types)); | |
386 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | |
387 records.snapshots[2]->source.updates_source); | |
388 } | |
389 | |
390 // Issue a nudge when the config has failed. Make sure both the config and | |
391 // nudge are executed. | |
392 TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) { | |
393 const ModelTypeSet model_types(syncable::BOOKMARKS); | |
394 UseMockDelayProvider(); | |
395 EXPECT_CALL(*delay(), GetDelay(_)) | |
396 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50))); | |
397 SyncShareRecords records; | |
398 | |
399 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
400 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
401 WithArg<0>(RecordSyncShare(&records)))) | |
402 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
403 WithArg<0>(RecordSyncShare(&records)))) | |
404 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
405 WithArg<0>(RecordSyncShare(&records)))) | |
406 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
407 WithArg<0>(RecordSyncShare(&records)))); | |
408 | |
409 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
410 RunLoop(); | |
411 | |
412 ASSERT_EQ(0U, records.snapshots.size()); | |
413 scheduler()->ScheduleConfig( | |
414 model_types, GetUpdatesCallerInfo::RECONFIGURATION); | |
415 RunLoop(); | |
416 | |
417 ASSERT_EQ(1U, records.snapshots.size()); | |
418 scheduler()->ScheduleNudge( | |
419 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); | |
420 RunLoop(); | |
421 | |
422 ASSERT_EQ(2U, records.snapshots.size()); | |
423 RunLoop(); | |
424 | |
425 // Now change the mode so nudge can execute. | |
426 ASSERT_EQ(3U, records.snapshots.size()); | |
427 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
428 RunLoop(); | |
429 | |
430 ASSERT_EQ(4U, records.snapshots.size()); | |
431 | |
432 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | |
433 records.snapshots[2]->source.types)); | |
434 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | |
435 records.snapshots[2]->source.updates_source); | |
436 | |
437 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | |
438 records.snapshots[3]->source.types)); | |
439 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
440 records.snapshots[3]->source.updates_source); | |
441 | |
442 } | |
443 | |
444 // Test that nudges are coalesced. | |
445 TEST_F(SyncSchedulerTest, NudgeCoalescing) { | |
446 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
447 RunLoop(); | |
448 | |
449 SyncShareRecords r; | |
450 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
451 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
452 WithArg<0>(RecordSyncShare(&r)))); | |
453 const ModelTypeSet | |
454 types1(syncable::BOOKMARKS), | |
455 types2(syncable::AUTOFILL), | |
456 types3(syncable::THEMES); | |
457 TimeDelta delay = zero(); | |
458 TimeTicks optimal_time = TimeTicks::Now() + delay; | |
459 scheduler()->ScheduleNudge( | |
460 delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); | |
461 scheduler()->ScheduleNudge( | |
462 zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); | |
463 RunLoop(); | |
464 | |
465 ASSERT_EQ(1U, r.snapshots.size()); | |
466 EXPECT_GE(r.times[0], optimal_time); | |
467 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap( | |
468 Union(types1, types2), r.snapshots[0]->source.types)); | |
469 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
470 r.snapshots[0]->source.updates_source); | |
471 | |
472 Mock::VerifyAndClearExpectations(syncer()); | |
473 | |
474 SyncShareRecords r2; | |
475 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
476 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
477 WithArg<0>(RecordSyncShare(&r2)))); | |
478 scheduler()->ScheduleNudge( | |
479 zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); | |
480 RunLoop(); | |
481 | |
482 ASSERT_EQ(1U, r2.snapshots.size()); | |
483 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(types3, | |
484 r2.snapshots[0]->source.types)); | |
485 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, | |
486 r2.snapshots[0]->source.updates_source); | |
487 } | |
488 | |
489 // Test that nudges are coalesced. | |
490 TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) { | |
491 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
492 RunLoop(); | |
493 | |
494 SyncShareRecords r; | |
495 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
496 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
497 WithArg<0>(RecordSyncShare(&r)))); | |
498 syncable::ModelTypeSet types1(syncable::BOOKMARKS), | |
499 types2(syncable::AUTOFILL), types3; | |
500 | |
501 // Create a huge time delay. | |
502 TimeDelta delay = TimeDelta::FromDays(1); | |
503 | |
504 scheduler()->ScheduleNudge( | |
505 delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); | |
506 | |
507 scheduler()->ScheduleNudge( | |
508 zero(), NUDGE_SOURCE_UNKNOWN, types2, FROM_HERE); | |
509 | |
510 TimeTicks min_time = TimeTicks::Now(); | |
511 TimeTicks max_time = TimeTicks::Now() + delay; | |
512 | |
513 RunLoop(); | |
514 | |
515 // Make sure the sync has happened. | |
516 ASSERT_EQ(1U, r.snapshots.size()); | |
517 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap( | |
518 Union(types1, types2), r.snapshots[0]->source.types)); | |
519 | |
520 // Make sure the sync happened at the right time. | |
521 EXPECT_GE(r.times[0], min_time); | |
522 EXPECT_LE(r.times[0], max_time); | |
523 } | |
524 | |
525 // Test nudge scheduling. | |
526 TEST_F(SyncSchedulerTest, NudgeWithPayloads) { | |
527 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
528 RunLoop(); | |
529 | |
530 SyncShareRecords records; | |
531 syncable::ModelTypePayloadMap model_types_with_payloads; | |
532 model_types_with_payloads[syncable::BOOKMARKS] = "test"; | |
533 | |
534 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
535 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
536 WithArg<0>(RecordSyncShare(&records)))) | |
537 .RetiresOnSaturation(); | |
538 scheduler()->ScheduleNudgeWithPayloads( | |
539 zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); | |
540 RunLoop(); | |
541 | |
542 ASSERT_EQ(1U, records.snapshots.size()); | |
543 EXPECT_EQ(model_types_with_payloads, records.snapshots[0]->source.types); | |
544 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
545 records.snapshots[0]->source.updates_source); | |
546 | |
547 Mock::VerifyAndClearExpectations(syncer()); | |
548 | |
549 // Make sure a second, later, nudge is unaffected by first (no coalescing). | |
550 SyncShareRecords records2; | |
551 model_types_with_payloads.erase(syncable::BOOKMARKS); | |
552 model_types_with_payloads[syncable::AUTOFILL] = "test2"; | |
553 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
554 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
555 WithArg<0>(RecordSyncShare(&records2)))); | |
556 scheduler()->ScheduleNudgeWithPayloads( | |
557 zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); | |
558 RunLoop(); | |
559 | |
560 ASSERT_EQ(1U, records2.snapshots.size()); | |
561 EXPECT_EQ(model_types_with_payloads, records2.snapshots[0]->source.types); | |
562 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
563 records2.snapshots[0]->source.updates_source); | |
564 } | |
565 | |
566 // Test that nudges are coalesced. | |
567 TEST_F(SyncSchedulerTest, NudgeWithPayloadsCoalescing) { | |
568 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
569 RunLoop(); | |
570 | |
571 SyncShareRecords r; | |
572 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
573 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
574 WithArg<0>(RecordSyncShare(&r)))); | |
575 syncable::ModelTypePayloadMap types1, types2, types3; | |
576 types1[syncable::BOOKMARKS] = "test1"; | |
577 types2[syncable::AUTOFILL] = "test2"; | |
578 types3[syncable::THEMES] = "test3"; | |
579 TimeDelta delay = zero(); | |
580 TimeTicks optimal_time = TimeTicks::Now() + delay; | |
581 scheduler()->ScheduleNudgeWithPayloads( | |
582 delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); | |
583 scheduler()->ScheduleNudgeWithPayloads( | |
584 zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); | |
585 RunLoop(); | |
586 | |
587 ASSERT_EQ(1U, r.snapshots.size()); | |
588 EXPECT_GE(r.times[0], optimal_time); | |
589 syncable::ModelTypePayloadMap coalesced_types; | |
590 syncable::CoalescePayloads(&coalesced_types, types1); | |
591 syncable::CoalescePayloads(&coalesced_types, types2); | |
592 EXPECT_EQ(coalesced_types, r.snapshots[0]->source.types); | |
593 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
594 r.snapshots[0]->source.updates_source); | |
595 | |
596 Mock::VerifyAndClearExpectations(syncer()); | |
597 | |
598 SyncShareRecords r2; | |
599 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
600 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
601 WithArg<0>(RecordSyncShare(&r2)))); | |
602 scheduler()->ScheduleNudgeWithPayloads( | |
603 zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); | |
604 RunLoop(); | |
605 | |
606 ASSERT_EQ(1U, r2.snapshots.size()); | |
607 EXPECT_EQ(types3, r2.snapshots[0]->source.types); | |
608 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, | |
609 r2.snapshots[0]->source.updates_source); | |
610 } | |
611 | |
612 // Test that polling works as expected. | |
613 TEST_F(SyncSchedulerTest, Polling) { | |
614 SyncShareRecords records; | |
615 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); | |
616 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) | |
617 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
618 WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); | |
619 | |
620 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval); | |
621 | |
622 TimeTicks optimal_start = TimeTicks::Now() + poll_interval; | |
623 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
624 RunLoop(); | |
625 | |
626 // Run again to wait for polling. | |
627 RunLoop(); | |
628 | |
629 StopSyncScheduler(); | |
630 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval); | |
631 } | |
632 | |
633 // Test that the short poll interval is used. | |
634 TEST_F(SyncSchedulerTest, PollNotificationsDisabled) { | |
635 SyncShareRecords records; | |
636 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); | |
637 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) | |
638 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
639 WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); | |
640 | |
641 scheduler()->OnReceivedShortPollIntervalUpdate(poll_interval); | |
642 scheduler()->set_notifications_enabled(false); | |
643 | |
644 TimeTicks optimal_start = TimeTicks::Now() + poll_interval; | |
645 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
646 RunLoop(); | |
647 | |
648 // Run again to wait for polling. | |
649 RunLoop(); | |
650 | |
651 StopSyncScheduler(); | |
652 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval); | |
653 } | |
654 | |
655 // Test that polling intervals are updated when needed. | |
656 TEST_F(SyncSchedulerTest, PollIntervalUpdate) { | |
657 SyncShareRecords records; | |
658 TimeDelta poll1(TimeDelta::FromMilliseconds(120)); | |
659 TimeDelta poll2(TimeDelta::FromMilliseconds(30)); | |
660 scheduler()->OnReceivedLongPollIntervalUpdate(poll1); | |
661 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) | |
662 .WillOnce(WithArg<0>( | |
663 sessions::test_util::SimulatePollIntervalUpdate(poll2))) | |
664 .WillRepeatedly( | |
665 DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
666 WithArg<0>( | |
667 RecordSyncShareMultiple(&records, kMinNumSamples)))); | |
668 | |
669 TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2; | |
670 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
671 RunLoop(); | |
672 | |
673 // Run again to wait for polling. | |
674 RunLoop(); | |
675 | |
676 StopSyncScheduler(); | |
677 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll2); | |
678 } | |
679 | |
680 // Test that the sessions commit delay is updated when needed. | |
681 TEST_F(SyncSchedulerTest, SessionsCommitDelay) { | |
682 SyncShareRecords records; | |
683 TimeDelta delay1(TimeDelta::FromMilliseconds(120)); | |
684 TimeDelta delay2(TimeDelta::FromMilliseconds(30)); | |
685 scheduler()->OnReceivedSessionsCommitDelay(delay1); | |
686 | |
687 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
688 .WillOnce( | |
689 DoAll( | |
690 WithArg<0>( | |
691 sessions::test_util::SimulateSessionsCommitDelayUpdate( | |
692 delay2)), | |
693 Invoke(sessions::test_util::SimulateSuccess), | |
694 QuitLoopNowAction())); | |
695 | |
696 EXPECT_EQ(delay1, scheduler()->sessions_commit_delay()); | |
697 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
698 RunLoop(); | |
699 | |
700 EXPECT_EQ(delay1, scheduler()->sessions_commit_delay()); | |
701 const ModelTypeSet model_types(syncable::BOOKMARKS); | |
702 scheduler()->ScheduleNudge( | |
703 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); | |
704 RunLoop(); | |
705 | |
706 EXPECT_EQ(delay2, scheduler()->sessions_commit_delay()); | |
707 StopSyncScheduler(); | |
708 } | |
709 | |
710 // Test that a sync session is run through to completion. | |
711 TEST_F(SyncSchedulerTest, HasMoreToSync) { | |
712 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
713 .WillOnce(Invoke(sessions::test_util::SimulateHasMoreToSync)) | |
714 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
715 QuitLoopNowAction())); | |
716 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
717 RunLoop(); | |
718 | |
719 scheduler()->ScheduleNudge( | |
720 zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(), FROM_HERE); | |
721 RunLoop(); | |
722 // If more nudges are scheduled, they'll be waited on by TearDown, and would | |
723 // cause our expectation to break. | |
724 } | |
725 | |
726 // Test that no syncing occurs when throttled. | |
727 TEST_F(SyncSchedulerTest, ThrottlingDoesThrottle) { | |
728 const ModelTypeSet types(syncable::BOOKMARKS); | |
729 TimeDelta poll(TimeDelta::FromMilliseconds(5)); | |
730 TimeDelta throttle(TimeDelta::FromMinutes(10)); | |
731 scheduler()->OnReceivedLongPollIntervalUpdate(poll); | |
732 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
733 .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle))) | |
734 .WillRepeatedly(AddFailureAndQuitLoopNow()); | |
735 | |
736 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
737 RunLoop(); | |
738 | |
739 scheduler()->ScheduleNudge( | |
740 zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); | |
741 PumpLoop(); | |
742 | |
743 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
744 RunLoop(); | |
745 | |
746 scheduler()->ScheduleConfig( | |
747 types, GetUpdatesCallerInfo::RECONFIGURATION); | |
748 PumpLoop(); | |
749 } | |
750 | |
751 TEST_F(SyncSchedulerTest, ThrottlingExpires) { | |
752 SyncShareRecords records; | |
753 TimeDelta poll(TimeDelta::FromMilliseconds(15)); | |
754 TimeDelta throttle1(TimeDelta::FromMilliseconds(150)); | |
755 scheduler()->OnReceivedLongPollIntervalUpdate(poll); | |
756 | |
757 ::testing::InSequence seq; | |
758 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
759 .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle1))) | |
760 .RetiresOnSaturation(); | |
761 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
762 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
763 WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); | |
764 | |
765 TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1; | |
766 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
767 RunLoop(); | |
768 | |
769 // Run again to wait for polling. | |
770 RunLoop(); | |
771 | |
772 StopSyncScheduler(); | |
773 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll); | |
774 } | |
775 | |
776 // Test nudges / polls don't run in config mode and config tasks do. | |
777 TEST_F(SyncSchedulerTest, ConfigurationMode) { | |
778 TimeDelta poll(TimeDelta::FromMilliseconds(15)); | |
779 SyncShareRecords records; | |
780 scheduler()->OnReceivedLongPollIntervalUpdate(poll); | |
781 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
782 .WillOnce((Invoke(sessions::test_util::SimulateSuccess), | |
783 WithArg<0>(RecordSyncShare(&records)))); | |
784 | |
785 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
786 RunLoop(); | |
787 | |
788 const ModelTypeSet nudge_types(syncable::AUTOFILL); | |
789 scheduler()->ScheduleNudge( | |
790 zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); | |
791 scheduler()->ScheduleNudge( | |
792 zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); | |
793 | |
794 const ModelTypeSet config_types(syncable::BOOKMARKS); | |
795 | |
796 scheduler()->ScheduleConfig( | |
797 config_types, GetUpdatesCallerInfo::RECONFIGURATION); | |
798 RunLoop(); | |
799 | |
800 ASSERT_EQ(1U, records.snapshots.size()); | |
801 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(config_types, | |
802 records.snapshots[0]->source.types)); | |
803 } | |
804 | |
805 // Have the sycner fail during commit. Expect that the scheduler enters | |
806 // backoff. | |
807 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnce) { | |
808 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
809 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
810 QuitLoopNowAction())); | |
811 EXPECT_TRUE(RunAndGetBackoff()); | |
812 } | |
813 | |
814 // Have the syncer fail during download updates and succeed on the first | |
815 // retry. Expect that this clears the backoff state. | |
816 TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadOnceThenSucceed) { | |
817 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
818 .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed)) | |
819 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
820 QuitLoopNowAction())); | |
821 EXPECT_FALSE(RunAndGetBackoff()); | |
822 } | |
823 | |
824 // Have the syncer fail during commit and succeed on the first retry. Expect | |
825 // that this clears the backoff state. | |
826 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnceThenSucceed) { | |
827 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
828 .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed)) | |
829 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
830 QuitLoopNowAction())); | |
831 EXPECT_FALSE(RunAndGetBackoff()); | |
832 } | |
833 | |
834 // Have the syncer fail to download updates and fail again on the retry. | |
835 // Expect this will leave the scheduler in backoff. | |
836 TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadTwice) { | |
837 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
838 .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed)) | |
839 .WillRepeatedly(DoAll( | |
840 Invoke(sessions::test_util::SimulateDownloadUpdatesFailed), | |
841 QuitLoopNowAction())); | |
842 EXPECT_TRUE(RunAndGetBackoff()); | |
843 } | |
844 | |
845 // Test that no polls or extraneous nudges occur when in backoff. | |
846 TEST_F(SyncSchedulerTest, BackoffDropsJobs) { | |
847 SyncShareRecords r; | |
848 TimeDelta poll(TimeDelta::FromMilliseconds(5)); | |
849 const ModelTypeSet types(syncable::BOOKMARKS); | |
850 scheduler()->OnReceivedLongPollIntervalUpdate(poll); | |
851 UseMockDelayProvider(); | |
852 | |
853 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1) | |
854 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
855 RecordSyncShareMultiple(&r, 1U))); | |
856 EXPECT_CALL(*delay(), GetDelay(_)). | |
857 WillRepeatedly(Return(TimeDelta::FromDays(1))); | |
858 | |
859 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
860 RunLoop(); | |
861 | |
862 // This nudge should fail and put us into backoff. Thanks to our mock | |
863 // GetDelay() setup above, this will be a long backoff. | |
864 scheduler()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); | |
865 RunLoop(); | |
866 | |
867 Mock::VerifyAndClearExpectations(syncer()); | |
868 ASSERT_EQ(1U, r.snapshots.size()); | |
869 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[0]->source.updates_source); | |
870 | |
871 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1) | |
872 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
873 RecordSyncShare(&r))); | |
874 | |
875 // We schedule a nudge with enough delay (10X poll interval) that at least | |
876 // one or two polls would have taken place. The nudge should succeed. | |
877 scheduler()->ScheduleNudge(poll * 10, NUDGE_SOURCE_LOCAL, types, FROM_HERE); | |
878 RunLoop(); | |
879 | |
880 Mock::VerifyAndClearExpectations(syncer()); | |
881 Mock::VerifyAndClearExpectations(delay()); | |
882 ASSERT_EQ(2U, r.snapshots.size()); | |
883 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[1]->source.updates_source); | |
884 | |
885 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(0); | |
886 EXPECT_CALL(*delay(), GetDelay(_)).Times(0); | |
887 | |
888 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
889 RunLoop(); | |
890 | |
891 scheduler()->ScheduleConfig( | |
892 types, GetUpdatesCallerInfo::RECONFIGURATION); | |
893 PumpLoop(); | |
894 | |
895 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
896 RunLoop(); | |
897 | |
898 scheduler()->ScheduleNudge( | |
899 zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); | |
900 scheduler()->ScheduleNudge( | |
901 zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); | |
902 PumpLoop(); | |
903 } | |
904 | |
905 // Test that backoff is shaping traffic properly with consecutive errors. | |
906 TEST_F(SyncSchedulerTest, BackoffElevation) { | |
907 SyncShareRecords r; | |
908 UseMockDelayProvider(); | |
909 | |
910 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(kMinNumSamples) | |
911 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
912 RecordSyncShareMultiple(&r, kMinNumSamples))); | |
913 | |
914 const TimeDelta first = TimeDelta::FromSeconds(1); | |
915 const TimeDelta second = TimeDelta::FromMilliseconds(2); | |
916 const TimeDelta third = TimeDelta::FromMilliseconds(3); | |
917 const TimeDelta fourth = TimeDelta::FromMilliseconds(4); | |
918 const TimeDelta fifth = TimeDelta::FromMilliseconds(5); | |
919 const TimeDelta sixth = TimeDelta::FromDays(1); | |
920 | |
921 EXPECT_CALL(*delay(), GetDelay(Eq(first))).WillOnce(Return(second)) | |
922 .RetiresOnSaturation(); | |
923 EXPECT_CALL(*delay(), GetDelay(Eq(second))).WillOnce(Return(third)) | |
924 .RetiresOnSaturation(); | |
925 EXPECT_CALL(*delay(), GetDelay(Eq(third))).WillOnce(Return(fourth)) | |
926 .RetiresOnSaturation(); | |
927 EXPECT_CALL(*delay(), GetDelay(Eq(fourth))).WillOnce(Return(fifth)) | |
928 .RetiresOnSaturation(); | |
929 EXPECT_CALL(*delay(), GetDelay(Eq(fifth))).WillOnce(Return(sixth)); | |
930 | |
931 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
932 RunLoop(); | |
933 | |
934 // Run again with a nudge. | |
935 scheduler()->ScheduleNudge( | |
936 zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(), FROM_HERE); | |
937 RunLoop(); | |
938 | |
939 ASSERT_EQ(kMinNumSamples, r.snapshots.size()); | |
940 EXPECT_GE(r.times[1] - r.times[0], second); | |
941 EXPECT_GE(r.times[2] - r.times[1], third); | |
942 EXPECT_GE(r.times[3] - r.times[2], fourth); | |
943 EXPECT_GE(r.times[4] - r.times[3], fifth); | |
944 } | |
945 | |
946 // Test that things go back to normal once a retry makes forward progress. | |
947 TEST_F(SyncSchedulerTest, BackoffRelief) { | |
948 SyncShareRecords r; | |
949 const TimeDelta poll(TimeDelta::FromMilliseconds(10)); | |
950 scheduler()->OnReceivedLongPollIntervalUpdate(poll); | |
951 UseMockDelayProvider(); | |
952 | |
953 const TimeDelta backoff = TimeDelta::FromMilliseconds(5); | |
954 | |
955 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
956 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
957 RecordSyncShareMultiple(&r, kMinNumSamples))) | |
958 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
959 RecordSyncShareMultiple(&r, kMinNumSamples))); | |
960 EXPECT_CALL(*delay(), GetDelay(_)).WillOnce(Return(backoff)); | |
961 | |
962 // Optimal start for the post-backoff poll party. | |
963 TimeTicks optimal_start = TimeTicks::Now(); | |
964 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
965 RunLoop(); | |
966 | |
967 // Run again to wait for polling. | |
968 scheduler()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, | |
969 ModelTypeSet(), FROM_HERE); | |
970 RunLoop(); | |
971 | |
972 StopSyncScheduler(); | |
973 | |
974 EXPECT_EQ(kMinNumSamples, r.times.size()); | |
975 | |
976 // The first nudge ran as soon as possible. It failed. | |
977 TimeTicks optimal_job_time = optimal_start; | |
978 EXPECT_GE(r.times[0], optimal_job_time); | |
979 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
980 r.snapshots[0]->source.updates_source); | |
981 | |
982 // It was followed by a successful retry nudge shortly afterward. | |
983 optimal_job_time = optimal_job_time + backoff; | |
984 EXPECT_GE(r.times[1], optimal_job_time); | |
985 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | |
986 r.snapshots[1]->source.updates_source); | |
987 // After that, we went back to polling. | |
988 for (size_t i = 2; i < r.snapshots.size(); i++) { | |
989 optimal_job_time = optimal_job_time + poll; | |
990 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); | |
991 EXPECT_GE(r.times[i], optimal_job_time); | |
992 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, | |
993 r.snapshots[i]->source.updates_source); | |
994 } | |
995 } | |
996 | |
997 // Test that poll failures are ignored. They should have no effect on | |
998 // subsequent poll attempts, nor should they trigger a backoff/retry. | |
999 TEST_F(SyncSchedulerTest, TransientPollFailure) { | |
1000 SyncShareRecords r; | |
1001 const TimeDelta poll_interval(TimeDelta::FromMilliseconds(10)); | |
1002 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval); | |
1003 UseMockDelayProvider(); // Will cause test failure if backoff is initiated. | |
1004 | |
1005 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
1006 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | |
1007 RecordSyncShare(&r))) | |
1008 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | |
1009 RecordSyncShare(&r))); | |
1010 | |
1011 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
1012 RunLoop(); | |
1013 | |
1014 // Run the unsucessful poll. The failed poll should not trigger backoff. | |
1015 RunLoop(); | |
1016 EXPECT_FALSE(scheduler()->IsBackingOff()); | |
1017 | |
1018 // Run the successful poll. | |
1019 RunLoop(); | |
1020 EXPECT_FALSE(scheduler()->IsBackingOff()); | |
1021 | |
1022 // Verify the the two SyncShare() calls were made one poll interval apart. | |
1023 ASSERT_EQ(2U, r.snapshots.size()); | |
1024 EXPECT_GE(r.times[1] - r.times[0], poll_interval); | |
1025 } | |
1026 | |
1027 TEST_F(SyncSchedulerTest, GetRecommendedDelay) { | |
1028 EXPECT_LE(TimeDelta::FromSeconds(0), | |
1029 SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(0))); | |
1030 EXPECT_LE(TimeDelta::FromSeconds(1), | |
1031 SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(1))); | |
1032 EXPECT_LE(TimeDelta::FromSeconds(50), | |
1033 SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(50))); | |
1034 EXPECT_LE(TimeDelta::FromSeconds(10), | |
1035 SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(10))); | |
1036 EXPECT_EQ(TimeDelta::FromSeconds(kMaxBackoffSeconds), | |
1037 SyncScheduler::GetRecommendedDelay( | |
1038 TimeDelta::FromSeconds(kMaxBackoffSeconds))); | |
1039 EXPECT_EQ(TimeDelta::FromSeconds(kMaxBackoffSeconds), | |
1040 SyncScheduler::GetRecommendedDelay( | |
1041 TimeDelta::FromSeconds(kMaxBackoffSeconds + 1))); | |
1042 } | |
1043 | |
1044 // Test that appropriate syncer steps are requested for each job type. | |
1045 TEST_F(SyncSchedulerTest, SyncerSteps) { | |
1046 // Nudges. | |
1047 EXPECT_CALL(*syncer(), SyncShare(_, SYNCER_BEGIN, SYNCER_END)) | |
1048 .Times(1); | |
1049 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
1050 RunLoop(); | |
1051 | |
1052 scheduler()->ScheduleNudge( | |
1053 zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(), FROM_HERE); | |
1054 PumpLoop(); | |
1055 // Pump again to run job. | |
1056 PumpLoop(); | |
1057 | |
1058 StopSyncScheduler(); | |
1059 Mock::VerifyAndClearExpectations(syncer()); | |
1060 | |
1061 // ClearUserData. | |
1062 EXPECT_CALL(*syncer(), SyncShare(_, CLEAR_PRIVATE_DATA, CLEAR_PRIVATE_DATA)) | |
1063 .Times(1); | |
1064 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
1065 RunLoop(); | |
1066 | |
1067 scheduler()->ScheduleClearUserData(); | |
1068 PumpLoop(); | |
1069 PumpLoop(); | |
1070 | |
1071 StopSyncScheduler(); | |
1072 Mock::VerifyAndClearExpectations(syncer()); | |
1073 | |
1074 // Configuration. | |
1075 EXPECT_CALL(*syncer(), SyncShare(_, DOWNLOAD_UPDATES, APPLY_UPDATES)); | |
1076 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | |
1077 RunLoop(); | |
1078 | |
1079 scheduler()->ScheduleConfig( | |
1080 ModelTypeSet(), GetUpdatesCallerInfo::RECONFIGURATION); | |
1081 PumpLoop(); | |
1082 PumpLoop(); | |
1083 | |
1084 StopSyncScheduler(); | |
1085 Mock::VerifyAndClearExpectations(syncer()); | |
1086 | |
1087 // Cleanup disabled types. | |
1088 EXPECT_CALL(*syncer(), | |
1089 SyncShare(_, CLEANUP_DISABLED_TYPES, CLEANUP_DISABLED_TYPES)); | |
1090 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
1091 RunLoop(); | |
1092 | |
1093 scheduler()->ScheduleCleanupDisabledTypes(); | |
1094 // Only need to pump once, as ScheduleCleanupDisabledTypes() | |
1095 // schedules the job directly. | |
1096 PumpLoop(); | |
1097 | |
1098 StopSyncScheduler(); | |
1099 Mock::VerifyAndClearExpectations(syncer()); | |
1100 | |
1101 // Poll. | |
1102 EXPECT_CALL(*syncer(), SyncShare(_, SYNCER_BEGIN, SYNCER_END)) | |
1103 .Times(AtLeast(1)) | |
1104 .WillRepeatedly(QuitLoopNowAction()); | |
1105 const TimeDelta poll(TimeDelta::FromMilliseconds(10)); | |
1106 scheduler()->OnReceivedLongPollIntervalUpdate(poll); | |
1107 | |
1108 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
1109 RunLoop(); | |
1110 | |
1111 // Run again to wait for polling. | |
1112 RunLoop(); | |
1113 | |
1114 StopSyncScheduler(); | |
1115 Mock::VerifyAndClearExpectations(syncer()); | |
1116 } | |
1117 | |
1118 // Test config tasks don't run during normal mode. | |
1119 // TODO(tim): Implement this test and then the functionality! | |
1120 TEST_F(SyncSchedulerTest, DISABLED_NoConfigDuringNormal) { | |
1121 } | |
1122 | |
1123 // Test that starting the syncer thread without a valid connection doesn't | |
1124 // break things when a connection is detected. | |
1125 TEST_F(SyncSchedulerTest, StartWhenNotConnected) { | |
1126 connection()->SetServerNotReachable(); | |
1127 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | |
1128 .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed)) | |
1129 .WillOnce(QuitLoopNowAction()); | |
1130 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
1131 MessageLoop::current()->RunAllPending(); | |
1132 | |
1133 scheduler()->ScheduleNudge( | |
1134 zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(), FROM_HERE); | |
1135 // Should save the nudge for until after the server is reachable. | |
1136 MessageLoop::current()->RunAllPending(); | |
1137 | |
1138 connection()->SetServerReachable(); | |
1139 scheduler()->OnConnectionStatusChange(); | |
1140 MessageLoop::current()->RunAllPending(); | |
1141 } | |
1142 | |
1143 TEST_F(SyncSchedulerTest, SetsPreviousRoutingInfo) { | |
1144 ModelSafeRoutingInfo info; | |
1145 EXPECT_TRUE(info == context()->previous_session_routing_info()); | |
1146 ModelSafeRoutingInfo expected; | |
1147 context()->registrar()->GetModelSafeRoutingInfo(&expected); | |
1148 ASSERT_FALSE(expected.empty()); | |
1149 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1); | |
1150 | |
1151 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | |
1152 RunLoop(); | |
1153 | |
1154 scheduler()->ScheduleNudge( | |
1155 zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(), FROM_HERE); | |
1156 PumpLoop(); | |
1157 // Pump again to run job. | |
1158 PumpLoop(); | |
1159 | |
1160 StopSyncScheduler(); | |
1161 | |
1162 EXPECT_TRUE(expected == context()->previous_session_routing_info()); | |
1163 } | |
1164 | |
1165 } // namespace browser_sync | |
OLD | NEW |