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/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 syncable::ModelTypeBitSet; |
| 28 using sync_pb::GetUpdatesCallerInfo; |
| 29 |
| 30 class MockSyncer : public Syncer { |
| 31 public: |
| 32 MOCK_METHOD1(SyncShare, void(sessions::SyncSession*)); |
| 33 }; |
| 34 |
| 35 namespace s3 { |
| 36 |
| 37 ACTION_P(SignalEvent, event) { |
| 38 event->Signal(); |
| 39 } |
| 40 |
| 41 // Used when tests want to record syncing activity to examine later. |
| 42 struct SyncShareRecords { |
| 43 std::vector<TimeTicks> times; |
| 44 std::vector<scoped_refptr<SyncSession> > sessions; |
| 45 }; |
| 46 |
| 47 class SyncerThread2Test : public testing::Test { |
| 48 public: |
| 49 virtual void SetUp() { |
| 50 syncdb_.SetUp(); |
| 51 syncer_ = new MockSyncer(); |
| 52 registrar_.reset(MockModelSafeWorkerRegistrar::PassiveBookmarks()); |
| 53 context_ = new SyncSessionContext(NULL, syncdb_.manager(), |
| 54 registrar_.get(), std::vector<SyncEngineEventListener*>()); |
| 55 context_->set_notifications_enabled(true); |
| 56 context_->set_account_name("Test"); |
| 57 syncer_thread_ = new SyncerThread(context_, syncer_); |
| 58 // TODO(tim): Once the SCM is hooked up, remove this. |
| 59 syncer_thread_->server_connection_ok_ = true; |
| 60 } |
| 61 |
| 62 SyncerThread* syncer_thread() { return syncer_thread_.get(); } |
| 63 MockSyncer* syncer() { return syncer_; } |
| 64 TimeDelta zero() { return TimeDelta::FromSeconds(0); } |
| 65 TimeDelta timeout() { |
| 66 return TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms()); |
| 67 } |
| 68 |
| 69 virtual void TearDown() { |
| 70 syncer_thread()->Stop(); |
| 71 syncdb_.TearDown(); |
| 72 } |
| 73 |
| 74 private: |
| 75 scoped_refptr<SyncerThread> syncer_thread_; |
| 76 SyncSessionContext* context_; |
| 77 MockSyncer* syncer_; |
| 78 scoped_ptr<MockModelSafeWorkerRegistrar> registrar_; |
| 79 MockDirectorySetterUpper syncdb_; |
| 80 }; |
| 81 |
| 82 ACTION_P3(RecordSyncShare, record, signal_after, event) { |
| 83 record->times.push_back(TimeTicks::Now()); |
| 84 record->sessions.push_back(arg0); |
| 85 if (record->times.size() >= signal_after) |
| 86 event->Signal(); |
| 87 } |
| 88 |
| 89 // Test nudge scheduling. |
| 90 TEST_F(SyncerThread2Test, Nudge) { |
| 91 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 92 base::WaitableEvent done(false, false); |
| 93 SyncShareRecords records; |
| 94 syncable::ModelTypeBitSet model_types; |
| 95 model_types[syncable::BOOKMARKS] = true; |
| 96 |
| 97 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 98 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 99 WithArg<0>(RecordSyncShare(&records, 1U, &done)))); |
| 100 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types); |
| 101 done.TimedWait(timeout()); |
| 102 |
| 103 EXPECT_EQ(1, records.sessions.size()); |
| 104 EXPECT_EQ(model_types, records.sessions[0]->source().second); |
| 105 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, records.sessions[0]->source().first); |
| 106 } |
| 107 |
| 108 // Test that nudges are coalesced. |
| 109 TEST_F(SyncerThread2Test, NudgeCoalescing) { |
| 110 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 111 base::WaitableEvent done(false, false); |
| 112 SyncShareRecords r; |
| 113 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 114 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 115 WithArg<0>(RecordSyncShare(&r, 1U, &done)))); |
| 116 syncable::ModelTypeBitSet types1, types2, types3; |
| 117 types1[syncable::BOOKMARKS] = true; |
| 118 types2[syncable::AUTOFILL] = true; |
| 119 types3[syncable::THEMES] = true; |
| 120 TimeDelta delay = TimeDelta::FromMilliseconds(20); |
| 121 TimeTicks optimal_time = TimeTicks::Now() + delay; |
| 122 syncer_thread()->ScheduleNudge(delay, NUDGE_SOURCE_UNKNOWN, types1); |
| 123 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types2); |
| 124 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3); |
| 125 done.TimedWait(timeout()); |
| 126 |
| 127 EXPECT_EQ(1, r.sessions.size()); |
| 128 EXPECT_GE(r.times[0], optimal_time); |
| 129 EXPECT_EQ(types1 | types2 | types3, r.sessions[0]->source().second); |
| 130 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, r.sessions[0]->source().first); |
| 131 |
| 132 SyncShareRecords r2; |
| 133 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 134 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 135 WithArg<0>(RecordSyncShare(&r2, 1U, &done)))); |
| 136 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3); |
| 137 done.TimedWait(timeout()); |
| 138 EXPECT_EQ(1, r2.sessions.size()); |
| 139 EXPECT_EQ(types3, r2.sessions[0]->source().second); |
| 140 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, r2.sessions[0]->source().first); |
| 141 } |
| 142 |
| 143 // Test that polling works as expected. |
| 144 TEST_F(SyncerThread2Test, Polling) { |
| 145 SyncShareRecords records; |
| 146 base::WaitableEvent done(false, false); |
| 147 const size_t min_num_samples = 5; |
| 148 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); |
| 149 syncer_thread()->OnReceivedLongPollIntervalUpdate(poll_interval); |
| 150 EXPECT_CALL(*syncer(), SyncShare(_)).Times(AtLeast(min_num_samples)) |
| 151 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 152 WithArg<0>(RecordSyncShare(&records, min_num_samples, &done)))); |
| 153 |
| 154 TimeTicks optimal_start = TimeTicks::Now() + poll_interval; |
| 155 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 156 done.TimedWait(timeout()); |
| 157 syncer_thread()->Stop(); |
| 158 |
| 159 // Now analyze the run. |
| 160 const std::vector<TimeTicks>& data(records.times); |
| 161 EXPECT_GE(data.size(), min_num_samples); |
| 162 for (size_t i = 0; i < data.size(); i++) { |
| 163 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); |
| 164 TimeTicks optimal_next_sync = optimal_start + poll_interval * i; |
| 165 EXPECT_GE(data[i], optimal_next_sync); |
| 166 EXPECT_LT(data[i], optimal_next_sync + poll_interval); |
| 167 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, |
| 168 records.sessions[i]->source().first); |
| 169 } |
| 170 } |
| 171 |
| 172 // Test that the short poll interval is used. |
| 173 TEST_F(SyncerThread2Test, PollNotificationsDisabled) { |
| 174 |
| 175 } |
| 176 |
| 177 // Test that polling intervals are updated when needed. |
| 178 TEST_F(SyncerThread2Test, PollIntervalUpdate) { |
| 179 |
| 180 } |
| 181 |
| 182 // Test that a sync session is run through to completion. |
| 183 TEST_F(SyncerThread2Test, HasMoreToSync) { |
| 184 syncer_thread()->Start(SyncerThread::NORMAL_MODE); |
| 185 base::WaitableEvent done(false, false); |
| 186 EXPECT_CALL(*syncer(), SyncShare(_)) |
| 187 .WillOnce(Invoke(sessions::test_util::SimulateHasMoreToSync)) |
| 188 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
| 189 SignalEvent(&done))); |
| 190 syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet()); |
| 191 done.TimedWait(timeout()); |
| 192 // If more nudges are scheduled, they'll be waited on by TearDown, and would |
| 193 // cause our expectation to break. |
| 194 } |
| 195 |
| 196 // Test nudges / polls don't run in config mode. |
| 197 TEST_F(SyncerThread2Test, ConfigurationMode) { |
| 198 // TODO(tim): To test mode contract, use orthogonal NudgeSources for config |
| 199 // vs nudge, expect config source. |
| 200 } |
| 201 |
| 202 // Test that no syncing occurs when throttled. |
| 203 TEST_F(SyncerThread2Test, Throttled) { |
| 204 } |
| 205 |
| 206 // Test that exponential backoff is properly triggered. |
| 207 TEST_F(SyncerThread2Test, BackoffTriggered) { |
| 208 } |
| 209 |
| 210 // Test that no polls or extraneous nudges occur when in backoff. |
| 211 TEST_F(SyncerThread2Test, BackoffDropsJobs) { |
| 212 } |
| 213 |
| 214 // Test that backoff is shaping traffic properly with consecutive errors. |
| 215 TEST_F(SyncerThread2Test, BackoffElevation) { |
| 216 } |
| 217 |
| 218 // Test that things go back to normal once a canary task makes forward progress |
| 219 // following a succession of failures. |
| 220 TEST_F(SyncerThread2Test, BackoffRelief) { |
| 221 } |
| 222 |
| 223 TEST_F(SyncerThread2Test, StopSyncPermanently) { |
| 224 } |
| 225 |
| 226 TEST_F(SyncerThread2Test, GetRecommendedDelay) { |
| 227 } |
| 228 |
| 229 // Test config tasks don't run during normal mode. |
| 230 TEST_F(SyncerThread2Test, DISABLED_NoConfigDuringNormal) { |
| 231 } |
| 232 |
| 233 // Test that starting the syncer thread without a valid connection doesn't |
| 234 // break things when a connection is detected. |
| 235 TEST_F(SyncerThread2Test, DISABLED_StartWhenNotConnected) { |
| 236 |
| 237 } |
| 238 |
| 239 } // namespace s3 |
| 240 } // namespace browser_sync |
OLD | NEW |