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