Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: chrome/browser/sync/engine/syncer_thread2_unittest.cc

Issue 5939006: sync: beginnings of MessageLoop based SyncerThread (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: revert syncer_thread.cc Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698