| 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 |