OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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/synchronization/waitable_event.h" |
| 6 #include "base/test/test_timeouts.h" |
| 7 #include "chrome/browser/sync/engine/mock_model_safe_workers.h" |
| 8 #include "chrome/browser/sync/engine/syncer.h" |
| 9 #include "chrome/browser/sync/engine/syncer_thread2.h" |
| 10 #include "chrome/browser/sync/sessions/test_util.h" |
| 11 #include "chrome/test/sync/engine/test_directory_setter_upper.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 #include "testing/gmock/include/gmock/gmock.h" |
| 14 |
| 15 using base::TimeDelta; |
| 16 using base::TimeTicks; |
| 17 using testing::_; |
| 18 using testing::AtLeast; |
| 19 using testing::DoAll; |
| 20 using testing::Field; |
| 21 using testing::Invoke; |
| 22 using testing::WithArg; |
| 23 |
| 24 namespace browser_sync { |
| 25 using sessions::SyncSession; |
| 26 using sessions::SyncSessionContext; |
| 27 using sessions::SyncSessionSnapshot; |
| 28 using syncable::ModelTypeBitSet; |
| 29 using sync_pb::GetUpdatesCallerInfo; |
| 30 |
| 31 class MockSyncer : public Syncer { |
| 32 public: |
| 33 MOCK_METHOD1(SyncShare, void(sessions::SyncSession*)); |
| 34 }; |
| 35 |
| 36 namespace s3 { |
| 37 |
| 38 ACTION_P(SignalEvent, event) { |
| 39 event->Signal(); |
| 40 } |
| 41 |
| 42 // Used when tests want to record syncing activity to examine later. |
| 43 struct SyncShareRecords { |
| 44 std::vector<TimeTicks> times; |
| 45 std::vector<linked_ptr<SyncSessionSnapshot> > snapshots; |
| 46 }; |
| 47 |
| 48 // Convenient to use in tests wishing to analyze SyncShare calls over time. |
| 49 static const size_t kMinNumSamples = 5; |
| 50 |
| 51 class SyncerThread2Test : public testing::Test { |
| 52 public: |
| 53 virtual void SetUp() { |
| 54 syncdb_.SetUp(); |
| 55 syncer_ = new MockSyncer(); |
| 56 registrar_.reset(MockModelSafeWorkerRegistrar::PassiveBookmarks()); |
| 57 context_ = new SyncSessionContext(NULL, syncdb_.manager(), |
| 58 registrar_.get(), std::vector<SyncEngineEventListener*>()); |
| 59 context_->set_notifications_enabled(true); |
| 60 context_->set_account_name("Test"); |
| 61 syncer_thread_.reset(new SyncerThread(context_, syncer_)); |
| 62 // TODO(tim): Once the SCM is hooked up, remove this. |
| 63 syncer_thread_->server_connection_ok_ = true; |
| 64 } |
| 65 |
| 66 SyncerThread* syncer_thread() { return syncer_thread_.get(); } |
| 67 MockSyncer* syncer() { return syncer_; } |
| 68 TimeDelta zero() { return TimeDelta::FromSeconds(0); } |
| 69 TimeDelta timeout() { |
| 70 return TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms()); |
| 71 } |
| 72 |
| 73 virtual void TearDown() { |
| 74 syncer_thread()->Stop(); |
| 75 syncdb_.TearDown(); |
| 76 } |
| 77 |
| 78 void AnalyzePollRun(const SyncShareRecords& records, size_t min_num_samples, |
| 79 const TimeTicks& optimal_start, const TimeDelta& poll_interval) { |
| 80 const std::vector<TimeTicks>& data(records.times); |
| 81 EXPECT_GE(data.size(), min_num_samples); |
| 82 for (size_t i = 0; i < data.size(); i++) { |
| 83 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); |
| 84 TimeTicks optimal_next_sync = optimal_start + poll_interval * i; |
| 85 EXPECT_GE(data[i], optimal_next_sync); |
| 86 EXPECT_LT(data[i], optimal_next_sync + poll_interval); |
| 87 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, |
| 88 records.snapshots[i]->source.first); |
| 89 } |
| 90 } |
| 91 |
| 92 private: |
| 93 scoped_ptr<SyncerThread> syncer_thread_; |
| 94 SyncSessionContext* context_; |
| 95 MockSyncer* syncer_; |
| 96 scoped_ptr<MockModelSafeWorkerRegistrar> registrar_; |
| 97 MockDirectorySetterUpper syncdb_; |
| 98 }; |
| 99 |
| 100 ACTION_P3(RecordSyncShare, record, signal_after, event) { |
| 101 record->times.push_back(TimeTicks::Now()); |
| 102 record->snapshots.push_back(make_linked_ptr(new SyncSessionSnapshot( |
| 103 arg0->TakeSnapshot()))); |
| 104 if (event && record->times.size() >= signal_after) |
| 105 event->Signal(); |
| 106 } |
| 107 |
| 108 // Test nudge scheduling. |
| 109 TEST_F(SyncerThread2Test, Nudge) { |
| 110 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 111 base::WaitableEvent done(false, false); |
| 112 SyncShareRecords records; |
| 113 syncable::ModelTypeBitSet model_types; |
| 114 model_types[syncable::BOOKMARKS] = true; |
| 115 |
| 116 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 117 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 118 WithArg<0>(RecordSyncShare(&records, 1U, &done)))); |
| 119 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types); |
| 120 done.TimedWait(timeout()); |
| 121 |
| 122 EXPECT_EQ(1, records.snapshots.size()); |
| 123 EXPECT_EQ(model_types, records.snapshots[0]->source.second); |
| 124 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, records.snapshots[0]->source.first); |
| 125 } |
| 126 |
| 127 // Test that nudges are coalesced. |
| 128 TEST_F(SyncerThread2Test, NudgeCoalescing) { |
| 129 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 130 base::WaitableEvent done(false, false); |
| 131 SyncShareRecords r; |
| 132 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 133 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 134 WithArg<0>(RecordSyncShare(&r, 1U, &done)))); |
| 135 syncable::ModelTypeBitSet types1, types2, types3; |
| 136 types1[syncable::BOOKMARKS] = true; |
| 137 types2[syncable::AUTOFILL] = true; |
| 138 types3[syncable::THEMES] = true; |
| 139 TimeDelta delay = TimeDelta::FromMilliseconds(20); |
| 140 TimeTicks optimal_time = TimeTicks::Now() + delay; |
| 141 syncer_thread()->ScheduleNudge(delay, NUDGE_SOURCE_UNKNOWN, types1); |
| 142 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types2); |
| 143 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3); |
| 144 done.TimedWait(timeout()); |
| 145 |
| 146 EXPECT_EQ(1, r.snapshots.size()); |
| 147 EXPECT_GE(r.times[0], optimal_time); |
| 148 EXPECT_EQ(types1 | types2 | types3, r.snapshots[0]->source.second); |
| 149 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, r.snapshots[0]->source.first); |
| 150 |
| 151 SyncShareRecords r2; |
| 152 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 153 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 154 WithArg<0>(RecordSyncShare(&r2, 1U, &done)))); |
| 155 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3); |
| 156 done.TimedWait(timeout()); |
| 157 EXPECT_EQ(1, r2.snapshots.size()); |
| 158 EXPECT_EQ(types3, r2.snapshots[0]->source.second); |
| 159 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, r2.snapshots[0]->source.first); |
| 160 } |
| 161 |
| 162 // Test that polling works as expected. |
| 163 TEST_F(SyncerThread2Test, Polling) { |
| 164 SyncShareRecords records; |
| 165 base::WaitableEvent done(false, false); |
| 166 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); |
| 167 syncer_thread()->OnReceivedLongPollIntervalUpdate(poll_interval); |
| 168 EXPECT_CALL(*syncer(), SyncShare(_)).Times(AtLeast(kMinNumSamples)) |
| 169 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 170 WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); |
| 171 |
| 172 TimeTicks optimal_start = TimeTicks::Now() + poll_interval; |
| 173 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 174 done.TimedWait(timeout()); |
| 175 syncer_thread()->Stop(); |
| 176 |
| 177 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval); |
| 178 } |
| 179 |
| 180 // Test that the short poll interval is used. |
| 181 TEST_F(SyncerThread2Test, PollNotificationsDisabled) { |
| 182 SyncShareRecords records; |
| 183 base::WaitableEvent done(false, false); |
| 184 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); |
| 185 syncer_thread()->OnReceivedShortPollIntervalUpdate(poll_interval); |
| 186 syncer_thread()->set_notifications_enabled(false); |
| 187 EXPECT_CALL(*syncer(), SyncShare(_)).Times(AtLeast(kMinNumSamples)) |
| 188 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 189 WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); |
| 190 |
| 191 TimeTicks optimal_start = TimeTicks::Now() + poll_interval; |
| 192 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 193 done.TimedWait(timeout()); |
| 194 syncer_thread()->Stop(); |
| 195 |
| 196 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval); |
| 197 } |
| 198 |
| 199 // Test that polling intervals are updated when needed. |
| 200 TEST_F(SyncerThread2Test, PollIntervalUpdate) { |
| 201 SyncShareRecords records; |
| 202 base::WaitableEvent done(false, false); |
| 203 TimeDelta poll1(TimeDelta::FromMilliseconds(120)); |
| 204 TimeDelta poll2(TimeDelta::FromMilliseconds(30)); |
| 205 syncer_thread()->OnReceivedLongPollIntervalUpdate(poll1); |
| 206 EXPECT_CALL(*syncer(), SyncShare(_)).Times(AtLeast(kMinNumSamples)) |
| 207 .WillOnce(WithArg<0>( |
| 208 sessions::test_util::SimulatePollIntervalUpdate(poll2))) |
| 209 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 210 WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); |
| 211 |
| 212 TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2; |
| 213 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 214 done.TimedWait(timeout()); |
| 215 syncer_thread()->Stop(); |
| 216 |
| 217 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll2); |
| 218 } |
| 219 |
| 220 // Test that a sync session is run through to completion. |
| 221 TEST_F(SyncerThread2Test, HasMoreToSync) { |
| 222 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 223 base::WaitableEvent done(false, false); |
| 224 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 225 .WillOnce(Invoke(sessions::test_util::SimulateHasMoreToSync)) |
| 226 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 227 SignalEvent(&done))); |
| 228 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet()); |
| 229 done.TimedWait(timeout()); |
| 230 // If more nudges are scheduled, they'll be waited on by TearDown, and would |
| 231 // cause our expectation to break. |
| 232 } |
| 233 |
| 234 // Test that no syncing occurs when throttled. |
| 235 TEST_F(SyncerThread2Test, ThrottlingDoesThrottle) { |
| 236 syncable::ModelTypeBitSet types; |
| 237 types[syncable::BOOKMARKS] = true; |
| 238 base::WaitableEvent done(false, false); |
| 239 TimeDelta poll(TimeDelta::FromMilliseconds(5)); |
| 240 TimeDelta throttle(TimeDelta::FromMinutes(10)); |
| 241 syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); |
| 242 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 243 .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle))); |
| 244 |
| 245 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 246 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types); |
| 247 done.TimedWait(poll * 10); |
| 248 done.Reset(); |
| 249 |
| 250 syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE); |
| 251 syncer_thread()->ScheduleConfig(zero(), types); |
| 252 done.TimedWait(poll * 5); |
| 253 } |
| 254 |
| 255 TEST_F(SyncerThread2Test, ThrottlingExpires) { |
| 256 SyncShareRecords records; |
| 257 base::WaitableEvent done(false, false); |
| 258 TimeDelta poll(TimeDelta::FromMilliseconds(15)); |
| 259 TimeDelta throttle1(TimeDelta::FromMilliseconds(150)); |
| 260 TimeDelta throttle2(TimeDelta::FromMinutes(10)); |
| 261 syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); |
| 262 |
| 263 ::testing::InSequence seq; |
| 264 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 265 .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle1))) |
| 266 .RetiresOnSaturation(); |
| 267 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 268 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 269 WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); |
| 270 |
| 271 TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1; |
| 272 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 273 done.TimedWait(timeout()); |
| 274 syncer_thread()->Stop(); |
| 275 |
| 276 AnalyzePollRun(records, kMinNumSamples, optimal_start, poll); |
| 277 } |
| 278 |
| 279 // Test nudges / polls don't run in config mode and config tasks do. |
| 280 TEST_F(SyncerThread2Test, ConfigurationMode) { |
| 281 TimeDelta poll(TimeDelta::FromMilliseconds(15)); |
| 282 base::WaitableEvent done(false, false); |
| 283 syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); |
| 284 EXPECT_CALL(*syncer(), SyncShare(_)).Times(0); |
| 285 syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE); |
| 286 syncable::ModelTypeBitSet nudge_types; |
| 287 nudge_types[syncable::AUTOFILL] = true; |
| 288 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types); |
| 289 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types); |
| 290 |
| 291 syncable::ModelTypeBitSet config_types; |
| 292 config_types[syncable::BOOKMARKS] = true; |
| 293 // TODO(tim): This will fail once CONFIGURATION tasks are implemented. Update |
| 294 // the EXPECT call and timeout when that happens. |
| 295 syncer_thread()->ScheduleConfig(zero(), config_types); |
| 296 done.TimedWait(poll * 10); |
| 297 } |
| 298 |
| 299 // Test job/mode contract is honoured when alternating between modes. |
| 300 TEST_F(SyncerThread2Test, ChangeModes) { |
| 301 |
| 302 } |
| 303 |
| 304 // Test that exponential backoff is properly triggered. |
| 305 TEST_F(SyncerThread2Test, BackoffTriggered) { |
| 306 } |
| 307 |
| 308 // Test that no polls or extraneous nudges occur when in backoff. |
| 309 TEST_F(SyncerThread2Test, BackoffDropsJobs) { |
| 310 } |
| 311 |
| 312 // Test that backoff is shaping traffic properly with consecutive errors. |
| 313 TEST_F(SyncerThread2Test, BackoffElevation) { |
| 314 } |
| 315 |
| 316 // Test that things go back to normal once a canary task makes forward progress |
| 317 // following a succession of failures. |
| 318 TEST_F(SyncerThread2Test, BackoffRelief) { |
| 319 } |
| 320 |
| 321 TEST_F(SyncerThread2Test, StopSyncPermanently) { |
| 322 } |
| 323 |
| 324 TEST_F(SyncerThread2Test, GetRecommendedDelay) { |
| 325 } |
| 326 |
| 327 // Test config tasks don't run during normal mode. |
| 328 TEST_F(SyncerThread2Test, DISABLED_NoConfigDuringNormal) { |
| 329 } |
| 330 |
| 331 // Test that starting the syncer thread without a valid connection doesn't |
| 332 // break things when a connection is detected. |
| 333 TEST_F(SyncerThread2Test, DISABLED_StartWhenNotConnected) { |
| 334 |
| 335 } |
| 336 |
| 337 } // namespace s3 |
| 338 } // namespace browser_sync |
OLD | NEW |