OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/bind.h" | 5 #include "base/bind.h" |
6 #include "base/callback.h" | 6 #include "base/callback.h" |
7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
8 #include "base/memory/weak_ptr.h" | 8 #include "base/memory/weak_ptr.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/test/test_timeouts.h" | 10 #include "base/test/test_timeouts.h" |
(...skipping 27 matching lines...) Expand all Loading... |
38 | 38 |
39 class MockSyncer : public Syncer { | 39 class MockSyncer : public Syncer { |
40 public: | 40 public: |
41 MOCK_METHOD3(SyncShare, void(sessions::SyncSession*, SyncerStep, | 41 MOCK_METHOD3(SyncShare, void(sessions::SyncSession*, SyncerStep, |
42 SyncerStep)); | 42 SyncerStep)); |
43 }; | 43 }; |
44 | 44 |
45 // Used when tests want to record syncing activity to examine later. | 45 // Used when tests want to record syncing activity to examine later. |
46 struct SyncShareRecords { | 46 struct SyncShareRecords { |
47 std::vector<TimeTicks> times; | 47 std::vector<TimeTicks> times; |
48 std::vector<linked_ptr<SyncSessionSnapshot> > snapshots; | 48 std::vector<SyncSessionSnapshot> snapshots; |
49 }; | 49 }; |
50 | 50 |
51 void QuitLoopNow() { | 51 void QuitLoopNow() { |
52 // We use QuitNow() instead of Quit() as the latter may get stalled | 52 // We use QuitNow() instead of Quit() as the latter may get stalled |
53 // indefinitely in the presence of repeated timers with low delays | 53 // indefinitely in the presence of repeated timers with low delays |
54 // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll | 54 // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll |
55 // delay of 5ms] run under TSAN on the trybots). | 55 // delay of 5ms] run under TSAN on the trybots). |
56 MessageLoop::current()->QuitNow(); | 56 MessageLoop::current()->QuitNow(); |
57 } | 57 } |
58 | 58 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 | 123 |
124 void AnalyzePollRun(const SyncShareRecords& records, size_t min_num_samples, | 124 void AnalyzePollRun(const SyncShareRecords& records, size_t min_num_samples, |
125 const TimeTicks& optimal_start, const TimeDelta& poll_interval) { | 125 const TimeTicks& optimal_start, const TimeDelta& poll_interval) { |
126 const std::vector<TimeTicks>& data(records.times); | 126 const std::vector<TimeTicks>& data(records.times); |
127 EXPECT_GE(data.size(), min_num_samples); | 127 EXPECT_GE(data.size(), min_num_samples); |
128 for (size_t i = 0; i < data.size(); i++) { | 128 for (size_t i = 0; i < data.size(); i++) { |
129 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); | 129 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); |
130 TimeTicks optimal_next_sync = optimal_start + poll_interval * i; | 130 TimeTicks optimal_next_sync = optimal_start + poll_interval * i; |
131 EXPECT_GE(data[i], optimal_next_sync); | 131 EXPECT_GE(data[i], optimal_next_sync); |
132 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, | 132 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, |
133 records.snapshots[i]->source.updates_source); | 133 records.snapshots[i].source().updates_source); |
134 } | 134 } |
135 } | 135 } |
136 | 136 |
137 void DoQuitLoopNow() { | 137 void DoQuitLoopNow() { |
138 QuitLoopNow(); | 138 QuitLoopNow(); |
139 } | 139 } |
140 | 140 |
141 void StartSyncScheduler(SyncScheduler::Mode mode) { | 141 void StartSyncScheduler(SyncScheduler::Mode mode) { |
142 scheduler()->Start( | 142 scheduler()->Start( |
143 mode, | 143 mode, |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 } | 213 } |
214 | 214 |
215 void TearDown() { | 215 void TearDown() { |
216 StopSyncScheduler(); | 216 StopSyncScheduler(); |
217 SyncSchedulerTest::TearDown(); | 217 SyncSchedulerTest::TearDown(); |
218 } | 218 } |
219 }; | 219 }; |
220 | 220 |
221 void RecordSyncShareImpl(SyncSession* s, SyncShareRecords* record) { | 221 void RecordSyncShareImpl(SyncSession* s, SyncShareRecords* record) { |
222 record->times.push_back(TimeTicks::Now()); | 222 record->times.push_back(TimeTicks::Now()); |
223 record->snapshots.push_back(make_linked_ptr(new SyncSessionSnapshot( | 223 record->snapshots.push_back(s->TakeSnapshot()); |
224 s->TakeSnapshot()))); | |
225 } | 224 } |
226 | 225 |
227 ACTION_P(RecordSyncShare, record) { | 226 ACTION_P(RecordSyncShare, record) { |
228 RecordSyncShareImpl(arg0, record); | 227 RecordSyncShareImpl(arg0, record); |
229 QuitLoopNow(); | 228 QuitLoopNow(); |
230 } | 229 } |
231 | 230 |
232 ACTION_P2(RecordSyncShareMultiple, record, quit_after) { | 231 ACTION_P2(RecordSyncShareMultiple, record, quit_after) { |
233 RecordSyncShareImpl(arg0, record); | 232 RecordSyncShareImpl(arg0, record); |
234 EXPECT_LE(record->times.size(), quit_after); | 233 EXPECT_LE(record->times.size(), quit_after); |
(...skipping 23 matching lines...) Expand all Loading... |
258 | 257 |
259 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | 258 StartSyncScheduler(SyncScheduler::NORMAL_MODE); |
260 RunLoop(); | 259 RunLoop(); |
261 | 260 |
262 scheduler()->ScheduleNudge( | 261 scheduler()->ScheduleNudge( |
263 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); | 262 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); |
264 RunLoop(); | 263 RunLoop(); |
265 | 264 |
266 ASSERT_EQ(1U, records.snapshots.size()); | 265 ASSERT_EQ(1U, records.snapshots.size()); |
267 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | 266 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, |
268 records.snapshots[0]->source.types)); | 267 records.snapshots[0].source().types)); |
269 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 268 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
270 records.snapshots[0]->source.updates_source); | 269 records.snapshots[0].source().updates_source); |
271 | 270 |
272 Mock::VerifyAndClearExpectations(syncer()); | 271 Mock::VerifyAndClearExpectations(syncer()); |
273 | 272 |
274 // Make sure a second, later, nudge is unaffected by first (no coalescing). | 273 // Make sure a second, later, nudge is unaffected by first (no coalescing). |
275 SyncShareRecords records2; | 274 SyncShareRecords records2; |
276 model_types.Remove(syncable::BOOKMARKS); | 275 model_types.Remove(syncable::BOOKMARKS); |
277 model_types.Put(syncable::AUTOFILL); | 276 model_types.Put(syncable::AUTOFILL); |
278 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 277 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
279 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 278 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
280 WithArg<0>(RecordSyncShare(&records2)))); | 279 WithArg<0>(RecordSyncShare(&records2)))); |
281 scheduler()->ScheduleNudge( | 280 scheduler()->ScheduleNudge( |
282 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); | 281 zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); |
283 RunLoop(); | 282 RunLoop(); |
284 | 283 |
285 ASSERT_EQ(1U, records2.snapshots.size()); | 284 ASSERT_EQ(1U, records2.snapshots.size()); |
286 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | 285 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, |
287 records2.snapshots[0]->source.types)); | 286 records2.snapshots[0].source().types)); |
288 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 287 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
289 records2.snapshots[0]->source.updates_source); | 288 records2.snapshots[0].source().updates_source); |
290 } | 289 } |
291 | 290 |
292 // Make sure a regular config command is scheduled fine in the absence of any | 291 // Make sure a regular config command is scheduled fine in the absence of any |
293 // errors. | 292 // errors. |
294 TEST_F(SyncSchedulerTest, Config) { | 293 TEST_F(SyncSchedulerTest, Config) { |
295 SyncShareRecords records; | 294 SyncShareRecords records; |
296 const ModelTypeSet model_types(syncable::BOOKMARKS); | 295 const ModelTypeSet model_types(syncable::BOOKMARKS); |
297 | 296 |
298 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 297 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
299 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 298 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
300 WithArg<0>(RecordSyncShare(&records)))); | 299 WithArg<0>(RecordSyncShare(&records)))); |
301 | 300 |
302 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | 301 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); |
303 RunLoop(); | 302 RunLoop(); |
304 | 303 |
305 scheduler()->ScheduleConfig( | 304 scheduler()->ScheduleConfig( |
306 model_types, GetUpdatesCallerInfo::RECONFIGURATION); | 305 model_types, GetUpdatesCallerInfo::RECONFIGURATION); |
307 RunLoop(); | 306 RunLoop(); |
308 | 307 |
309 ASSERT_EQ(1U, records.snapshots.size()); | 308 ASSERT_EQ(1U, records.snapshots.size()); |
310 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | 309 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, |
311 records.snapshots[0]->source.types)); | 310 records.snapshots[0].source().types)); |
312 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | 311 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, |
313 records.snapshots[0]->source.updates_source); | 312 records.snapshots[0].source().updates_source); |
314 } | 313 } |
315 | 314 |
316 // Simulate a failure and make sure the config request is retried. | 315 // Simulate a failure and make sure the config request is retried. |
317 TEST_F(SyncSchedulerTest, ConfigWithBackingOff) { | 316 TEST_F(SyncSchedulerTest, ConfigWithBackingOff) { |
318 UseMockDelayProvider(); | 317 UseMockDelayProvider(); |
319 EXPECT_CALL(*delay(), GetDelay(_)) | 318 EXPECT_CALL(*delay(), GetDelay(_)) |
320 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1))); | 319 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1))); |
321 SyncShareRecords records; | 320 SyncShareRecords records; |
322 const ModelTypeSet model_types(syncable::BOOKMARKS); | 321 const ModelTypeSet model_types(syncable::BOOKMARKS); |
323 | 322 |
324 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 323 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
325 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | 324 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), |
326 WithArg<0>(RecordSyncShare(&records)))) | 325 WithArg<0>(RecordSyncShare(&records)))) |
327 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 326 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
328 WithArg<0>(RecordSyncShare(&records)))); | 327 WithArg<0>(RecordSyncShare(&records)))); |
329 | 328 |
330 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | 329 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); |
331 RunLoop(); | 330 RunLoop(); |
332 | 331 |
333 ASSERT_EQ(0U, records.snapshots.size()); | 332 ASSERT_EQ(0U, records.snapshots.size()); |
334 scheduler()->ScheduleConfig( | 333 scheduler()->ScheduleConfig( |
335 model_types, GetUpdatesCallerInfo::RECONFIGURATION); | 334 model_types, GetUpdatesCallerInfo::RECONFIGURATION); |
336 RunLoop(); | 335 RunLoop(); |
337 | 336 |
338 ASSERT_EQ(1U, records.snapshots.size()); | 337 ASSERT_EQ(1U, records.snapshots.size()); |
339 RunLoop(); | 338 RunLoop(); |
340 | 339 |
341 ASSERT_EQ(2U, records.snapshots.size()); | 340 ASSERT_EQ(2U, records.snapshots.size()); |
342 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | 341 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, |
343 records.snapshots[1]->source.types)); | 342 records.snapshots[1].source().types)); |
344 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | 343 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, |
345 records.snapshots[1]->source.updates_source); | 344 records.snapshots[1].source().updates_source); |
346 } | 345 } |
347 | 346 |
348 // Issue 2 config commands. Second one right after the first has failed | 347 // Issue 2 config commands. Second one right after the first has failed |
349 // and make sure LATEST is executed. | 348 // and make sure LATEST is executed. |
350 TEST_F(SyncSchedulerTest, MultipleConfigWithBackingOff) { | 349 TEST_F(SyncSchedulerTest, MultipleConfigWithBackingOff) { |
351 const ModelTypeSet | 350 const ModelTypeSet |
352 model_types1(syncable::BOOKMARKS), | 351 model_types1(syncable::BOOKMARKS), |
353 model_types2(syncable::AUTOFILL); | 352 model_types2(syncable::AUTOFILL); |
354 UseMockDelayProvider(); | 353 UseMockDelayProvider(); |
355 EXPECT_CALL(*delay(), GetDelay(_)) | 354 EXPECT_CALL(*delay(), GetDelay(_)) |
(...skipping 19 matching lines...) Expand all Loading... |
375 ASSERT_EQ(1U, records.snapshots.size()); | 374 ASSERT_EQ(1U, records.snapshots.size()); |
376 scheduler()->ScheduleConfig( | 375 scheduler()->ScheduleConfig( |
377 model_types2, GetUpdatesCallerInfo::RECONFIGURATION); | 376 model_types2, GetUpdatesCallerInfo::RECONFIGURATION); |
378 RunLoop(); | 377 RunLoop(); |
379 | 378 |
380 ASSERT_EQ(2U, records.snapshots.size()); | 379 ASSERT_EQ(2U, records.snapshots.size()); |
381 RunLoop(); | 380 RunLoop(); |
382 | 381 |
383 ASSERT_EQ(3U, records.snapshots.size()); | 382 ASSERT_EQ(3U, records.snapshots.size()); |
384 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types2, | 383 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types2, |
385 records.snapshots[2]->source.types)); | 384 records.snapshots[2].source().types)); |
386 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | 385 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, |
387 records.snapshots[2]->source.updates_source); | 386 records.snapshots[2].source().updates_source); |
388 } | 387 } |
389 | 388 |
390 // Issue a nudge when the config has failed. Make sure both the config and | 389 // Issue a nudge when the config has failed. Make sure both the config and |
391 // nudge are executed. | 390 // nudge are executed. |
392 TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) { | 391 TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) { |
393 const ModelTypeSet model_types(syncable::BOOKMARKS); | 392 const ModelTypeSet model_types(syncable::BOOKMARKS); |
394 UseMockDelayProvider(); | 393 UseMockDelayProvider(); |
395 EXPECT_CALL(*delay(), GetDelay(_)) | 394 EXPECT_CALL(*delay(), GetDelay(_)) |
396 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50))); | 395 .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50))); |
397 SyncShareRecords records; | 396 SyncShareRecords records; |
(...skipping 25 matching lines...) Expand all Loading... |
423 RunLoop(); | 422 RunLoop(); |
424 | 423 |
425 // Now change the mode so nudge can execute. | 424 // Now change the mode so nudge can execute. |
426 ASSERT_EQ(3U, records.snapshots.size()); | 425 ASSERT_EQ(3U, records.snapshots.size()); |
427 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | 426 StartSyncScheduler(SyncScheduler::NORMAL_MODE); |
428 RunLoop(); | 427 RunLoop(); |
429 | 428 |
430 ASSERT_EQ(4U, records.snapshots.size()); | 429 ASSERT_EQ(4U, records.snapshots.size()); |
431 | 430 |
432 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | 431 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, |
433 records.snapshots[2]->source.types)); | 432 records.snapshots[2].source().types)); |
434 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, | 433 EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, |
435 records.snapshots[2]->source.updates_source); | 434 records.snapshots[2].source().updates_source); |
436 | 435 |
437 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, | 436 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(model_types, |
438 records.snapshots[3]->source.types)); | 437 records.snapshots[3].source().types)); |
439 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 438 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
440 records.snapshots[3]->source.updates_source); | 439 records.snapshots[3].source().updates_source); |
441 | 440 |
442 } | 441 } |
443 | 442 |
444 // Test that nudges are coalesced. | 443 // Test that nudges are coalesced. |
445 TEST_F(SyncSchedulerTest, NudgeCoalescing) { | 444 TEST_F(SyncSchedulerTest, NudgeCoalescing) { |
446 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | 445 StartSyncScheduler(SyncScheduler::NORMAL_MODE); |
447 RunLoop(); | 446 RunLoop(); |
448 | 447 |
449 SyncShareRecords r; | 448 SyncShareRecords r; |
450 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 449 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
451 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 450 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
452 WithArg<0>(RecordSyncShare(&r)))); | 451 WithArg<0>(RecordSyncShare(&r)))); |
453 const ModelTypeSet | 452 const ModelTypeSet |
454 types1(syncable::BOOKMARKS), | 453 types1(syncable::BOOKMARKS), |
455 types2(syncable::AUTOFILL), | 454 types2(syncable::AUTOFILL), |
456 types3(syncable::THEMES); | 455 types3(syncable::THEMES); |
457 TimeDelta delay = zero(); | 456 TimeDelta delay = zero(); |
458 TimeTicks optimal_time = TimeTicks::Now() + delay; | 457 TimeTicks optimal_time = TimeTicks::Now() + delay; |
459 scheduler()->ScheduleNudge( | 458 scheduler()->ScheduleNudge( |
460 delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); | 459 delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); |
461 scheduler()->ScheduleNudge( | 460 scheduler()->ScheduleNudge( |
462 zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); | 461 zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); |
463 RunLoop(); | 462 RunLoop(); |
464 | 463 |
465 ASSERT_EQ(1U, r.snapshots.size()); | 464 ASSERT_EQ(1U, r.snapshots.size()); |
466 EXPECT_GE(r.times[0], optimal_time); | 465 EXPECT_GE(r.times[0], optimal_time); |
467 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap( | 466 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap( |
468 Union(types1, types2), r.snapshots[0]->source.types)); | 467 Union(types1, types2), r.snapshots[0].source().types)); |
469 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 468 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
470 r.snapshots[0]->source.updates_source); | 469 r.snapshots[0].source().updates_source); |
471 | 470 |
472 Mock::VerifyAndClearExpectations(syncer()); | 471 Mock::VerifyAndClearExpectations(syncer()); |
473 | 472 |
474 SyncShareRecords r2; | 473 SyncShareRecords r2; |
475 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 474 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
476 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 475 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
477 WithArg<0>(RecordSyncShare(&r2)))); | 476 WithArg<0>(RecordSyncShare(&r2)))); |
478 scheduler()->ScheduleNudge( | 477 scheduler()->ScheduleNudge( |
479 zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); | 478 zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); |
480 RunLoop(); | 479 RunLoop(); |
481 | 480 |
482 ASSERT_EQ(1U, r2.snapshots.size()); | 481 ASSERT_EQ(1U, r2.snapshots.size()); |
483 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(types3, | 482 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(types3, |
484 r2.snapshots[0]->source.types)); | 483 r2.snapshots[0].source().types)); |
485 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, | 484 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, |
486 r2.snapshots[0]->source.updates_source); | 485 r2.snapshots[0].source().updates_source); |
487 } | 486 } |
488 | 487 |
489 // Test that nudges are coalesced. | 488 // Test that nudges are coalesced. |
490 TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) { | 489 TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) { |
491 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | 490 StartSyncScheduler(SyncScheduler::NORMAL_MODE); |
492 RunLoop(); | 491 RunLoop(); |
493 | 492 |
494 SyncShareRecords r; | 493 SyncShareRecords r; |
495 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 494 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
496 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 495 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
(...skipping 11 matching lines...) Expand all Loading... |
508 zero(), NUDGE_SOURCE_UNKNOWN, types2, FROM_HERE); | 507 zero(), NUDGE_SOURCE_UNKNOWN, types2, FROM_HERE); |
509 | 508 |
510 TimeTicks min_time = TimeTicks::Now(); | 509 TimeTicks min_time = TimeTicks::Now(); |
511 TimeTicks max_time = TimeTicks::Now() + delay; | 510 TimeTicks max_time = TimeTicks::Now() + delay; |
512 | 511 |
513 RunLoop(); | 512 RunLoop(); |
514 | 513 |
515 // Make sure the sync has happened. | 514 // Make sure the sync has happened. |
516 ASSERT_EQ(1U, r.snapshots.size()); | 515 ASSERT_EQ(1U, r.snapshots.size()); |
517 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap( | 516 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap( |
518 Union(types1, types2), r.snapshots[0]->source.types)); | 517 Union(types1, types2), r.snapshots[0].source().types)); |
519 | 518 |
520 // Make sure the sync happened at the right time. | 519 // Make sure the sync happened at the right time. |
521 EXPECT_GE(r.times[0], min_time); | 520 EXPECT_GE(r.times[0], min_time); |
522 EXPECT_LE(r.times[0], max_time); | 521 EXPECT_LE(r.times[0], max_time); |
523 } | 522 } |
524 | 523 |
525 // Test nudge scheduling. | 524 // Test nudge scheduling. |
526 TEST_F(SyncSchedulerTest, NudgeWithPayloads) { | 525 TEST_F(SyncSchedulerTest, NudgeWithPayloads) { |
527 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | 526 StartSyncScheduler(SyncScheduler::NORMAL_MODE); |
528 RunLoop(); | 527 RunLoop(); |
529 | 528 |
530 SyncShareRecords records; | 529 SyncShareRecords records; |
531 syncable::ModelTypePayloadMap model_types_with_payloads; | 530 syncable::ModelTypePayloadMap model_types_with_payloads; |
532 model_types_with_payloads[syncable::BOOKMARKS] = "test"; | 531 model_types_with_payloads[syncable::BOOKMARKS] = "test"; |
533 | 532 |
534 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 533 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
535 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 534 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
536 WithArg<0>(RecordSyncShare(&records)))) | 535 WithArg<0>(RecordSyncShare(&records)))) |
537 .RetiresOnSaturation(); | 536 .RetiresOnSaturation(); |
538 scheduler()->ScheduleNudgeWithPayloads( | 537 scheduler()->ScheduleNudgeWithPayloads( |
539 zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); | 538 zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); |
540 RunLoop(); | 539 RunLoop(); |
541 | 540 |
542 ASSERT_EQ(1U, records.snapshots.size()); | 541 ASSERT_EQ(1U, records.snapshots.size()); |
543 EXPECT_EQ(model_types_with_payloads, records.snapshots[0]->source.types); | 542 EXPECT_EQ(model_types_with_payloads, records.snapshots[0].source().types); |
544 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 543 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
545 records.snapshots[0]->source.updates_source); | 544 records.snapshots[0].source().updates_source); |
546 | 545 |
547 Mock::VerifyAndClearExpectations(syncer()); | 546 Mock::VerifyAndClearExpectations(syncer()); |
548 | 547 |
549 // Make sure a second, later, nudge is unaffected by first (no coalescing). | 548 // Make sure a second, later, nudge is unaffected by first (no coalescing). |
550 SyncShareRecords records2; | 549 SyncShareRecords records2; |
551 model_types_with_payloads.erase(syncable::BOOKMARKS); | 550 model_types_with_payloads.erase(syncable::BOOKMARKS); |
552 model_types_with_payloads[syncable::AUTOFILL] = "test2"; | 551 model_types_with_payloads[syncable::AUTOFILL] = "test2"; |
553 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 552 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
554 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 553 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
555 WithArg<0>(RecordSyncShare(&records2)))); | 554 WithArg<0>(RecordSyncShare(&records2)))); |
556 scheduler()->ScheduleNudgeWithPayloads( | 555 scheduler()->ScheduleNudgeWithPayloads( |
557 zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); | 556 zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); |
558 RunLoop(); | 557 RunLoop(); |
559 | 558 |
560 ASSERT_EQ(1U, records2.snapshots.size()); | 559 ASSERT_EQ(1U, records2.snapshots.size()); |
561 EXPECT_EQ(model_types_with_payloads, records2.snapshots[0]->source.types); | 560 EXPECT_EQ(model_types_with_payloads, records2.snapshots[0].source().types); |
562 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 561 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
563 records2.snapshots[0]->source.updates_source); | 562 records2.snapshots[0].source().updates_source); |
564 } | 563 } |
565 | 564 |
566 // Test that nudges are coalesced. | 565 // Test that nudges are coalesced. |
567 TEST_F(SyncSchedulerTest, NudgeWithPayloadsCoalescing) { | 566 TEST_F(SyncSchedulerTest, NudgeWithPayloadsCoalescing) { |
568 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | 567 StartSyncScheduler(SyncScheduler::NORMAL_MODE); |
569 RunLoop(); | 568 RunLoop(); |
570 | 569 |
571 SyncShareRecords r; | 570 SyncShareRecords r; |
572 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 571 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
573 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 572 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
574 WithArg<0>(RecordSyncShare(&r)))); | 573 WithArg<0>(RecordSyncShare(&r)))); |
575 syncable::ModelTypePayloadMap types1, types2, types3; | 574 syncable::ModelTypePayloadMap types1, types2, types3; |
576 types1[syncable::BOOKMARKS] = "test1"; | 575 types1[syncable::BOOKMARKS] = "test1"; |
577 types2[syncable::AUTOFILL] = "test2"; | 576 types2[syncable::AUTOFILL] = "test2"; |
578 types3[syncable::THEMES] = "test3"; | 577 types3[syncable::THEMES] = "test3"; |
579 TimeDelta delay = zero(); | 578 TimeDelta delay = zero(); |
580 TimeTicks optimal_time = TimeTicks::Now() + delay; | 579 TimeTicks optimal_time = TimeTicks::Now() + delay; |
581 scheduler()->ScheduleNudgeWithPayloads( | 580 scheduler()->ScheduleNudgeWithPayloads( |
582 delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); | 581 delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); |
583 scheduler()->ScheduleNudgeWithPayloads( | 582 scheduler()->ScheduleNudgeWithPayloads( |
584 zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); | 583 zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); |
585 RunLoop(); | 584 RunLoop(); |
586 | 585 |
587 ASSERT_EQ(1U, r.snapshots.size()); | 586 ASSERT_EQ(1U, r.snapshots.size()); |
588 EXPECT_GE(r.times[0], optimal_time); | 587 EXPECT_GE(r.times[0], optimal_time); |
589 syncable::ModelTypePayloadMap coalesced_types; | 588 syncable::ModelTypePayloadMap coalesced_types; |
590 syncable::CoalescePayloads(&coalesced_types, types1); | 589 syncable::CoalescePayloads(&coalesced_types, types1); |
591 syncable::CoalescePayloads(&coalesced_types, types2); | 590 syncable::CoalescePayloads(&coalesced_types, types2); |
592 EXPECT_EQ(coalesced_types, r.snapshots[0]->source.types); | 591 EXPECT_EQ(coalesced_types, r.snapshots[0].source().types); |
593 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 592 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
594 r.snapshots[0]->source.updates_source); | 593 r.snapshots[0].source().updates_source); |
595 | 594 |
596 Mock::VerifyAndClearExpectations(syncer()); | 595 Mock::VerifyAndClearExpectations(syncer()); |
597 | 596 |
598 SyncShareRecords r2; | 597 SyncShareRecords r2; |
599 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 598 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
600 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 599 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
601 WithArg<0>(RecordSyncShare(&r2)))); | 600 WithArg<0>(RecordSyncShare(&r2)))); |
602 scheduler()->ScheduleNudgeWithPayloads( | 601 scheduler()->ScheduleNudgeWithPayloads( |
603 zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); | 602 zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); |
604 RunLoop(); | 603 RunLoop(); |
605 | 604 |
606 ASSERT_EQ(1U, r2.snapshots.size()); | 605 ASSERT_EQ(1U, r2.snapshots.size()); |
607 EXPECT_EQ(types3, r2.snapshots[0]->source.types); | 606 EXPECT_EQ(types3, r2.snapshots[0].source().types); |
608 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, | 607 EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, |
609 r2.snapshots[0]->source.updates_source); | 608 r2.snapshots[0].source().updates_source); |
610 } | 609 } |
611 | 610 |
612 // Test that polling works as expected. | 611 // Test that polling works as expected. |
613 TEST_F(SyncSchedulerTest, Polling) { | 612 TEST_F(SyncSchedulerTest, Polling) { |
614 SyncShareRecords records; | 613 SyncShareRecords records; |
615 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); | 614 TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); |
616 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) | 615 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) |
617 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), | 616 .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), |
618 WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); | 617 WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); |
619 | 618 |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
792 zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); | 791 zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); |
793 | 792 |
794 const ModelTypeSet config_types(syncable::BOOKMARKS); | 793 const ModelTypeSet config_types(syncable::BOOKMARKS); |
795 | 794 |
796 scheduler()->ScheduleConfig( | 795 scheduler()->ScheduleConfig( |
797 config_types, GetUpdatesCallerInfo::RECONFIGURATION); | 796 config_types, GetUpdatesCallerInfo::RECONFIGURATION); |
798 RunLoop(); | 797 RunLoop(); |
799 | 798 |
800 ASSERT_EQ(1U, records.snapshots.size()); | 799 ASSERT_EQ(1U, records.snapshots.size()); |
801 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(config_types, | 800 EXPECT_TRUE(CompareModelTypeSetToModelTypePayloadMap(config_types, |
802 records.snapshots[0]->source.types)); | 801 records.snapshots[0].source().types)); |
803 } | 802 } |
804 | 803 |
805 // Have the sycner fail during commit. Expect that the scheduler enters | 804 // Have the sycner fail during commit. Expect that the scheduler enters |
806 // backoff. | 805 // backoff. |
807 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnce) { | 806 TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnce) { |
808 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) | 807 EXPECT_CALL(*syncer(), SyncShare(_,_,_)) |
809 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | 808 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), |
810 QuitLoopNowAction())); | 809 QuitLoopNowAction())); |
811 EXPECT_TRUE(RunAndGetBackoff()); | 810 EXPECT_TRUE(RunAndGetBackoff()); |
812 } | 811 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
859 StartSyncScheduler(SyncScheduler::NORMAL_MODE); | 858 StartSyncScheduler(SyncScheduler::NORMAL_MODE); |
860 RunLoop(); | 859 RunLoop(); |
861 | 860 |
862 // This nudge should fail and put us into backoff. Thanks to our mock | 861 // This nudge should fail and put us into backoff. Thanks to our mock |
863 // GetDelay() setup above, this will be a long backoff. | 862 // GetDelay() setup above, this will be a long backoff. |
864 scheduler()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); | 863 scheduler()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); |
865 RunLoop(); | 864 RunLoop(); |
866 | 865 |
867 Mock::VerifyAndClearExpectations(syncer()); | 866 Mock::VerifyAndClearExpectations(syncer()); |
868 ASSERT_EQ(1U, r.snapshots.size()); | 867 ASSERT_EQ(1U, r.snapshots.size()); |
869 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[0]->source.updates_source); | 868 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
| 869 r.snapshots[0].source().updates_source); |
870 | 870 |
871 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1) | 871 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1) |
872 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), | 872 .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), |
873 RecordSyncShare(&r))); | 873 RecordSyncShare(&r))); |
874 | 874 |
875 // We schedule a nudge with enough delay (10X poll interval) that at least | 875 // We schedule a nudge with enough delay (10X poll interval) that at least |
876 // one or two polls would have taken place. The nudge should succeed. | 876 // one or two polls would have taken place. The nudge should succeed. |
877 scheduler()->ScheduleNudge(poll * 10, NUDGE_SOURCE_LOCAL, types, FROM_HERE); | 877 scheduler()->ScheduleNudge(poll * 10, NUDGE_SOURCE_LOCAL, types, FROM_HERE); |
878 RunLoop(); | 878 RunLoop(); |
879 | 879 |
880 Mock::VerifyAndClearExpectations(syncer()); | 880 Mock::VerifyAndClearExpectations(syncer()); |
881 Mock::VerifyAndClearExpectations(delay()); | 881 Mock::VerifyAndClearExpectations(delay()); |
882 ASSERT_EQ(2U, r.snapshots.size()); | 882 ASSERT_EQ(2U, r.snapshots.size()); |
883 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[1]->source.updates_source); | 883 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
| 884 r.snapshots[1].source().updates_source); |
884 | 885 |
885 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(0); | 886 EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(0); |
886 EXPECT_CALL(*delay(), GetDelay(_)).Times(0); | 887 EXPECT_CALL(*delay(), GetDelay(_)).Times(0); |
887 | 888 |
888 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); | 889 StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); |
889 RunLoop(); | 890 RunLoop(); |
890 | 891 |
891 scheduler()->ScheduleConfig( | 892 scheduler()->ScheduleConfig( |
892 types, GetUpdatesCallerInfo::RECONFIGURATION); | 893 types, GetUpdatesCallerInfo::RECONFIGURATION); |
893 PumpLoop(); | 894 PumpLoop(); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
970 RunLoop(); | 971 RunLoop(); |
971 | 972 |
972 StopSyncScheduler(); | 973 StopSyncScheduler(); |
973 | 974 |
974 EXPECT_EQ(kMinNumSamples, r.times.size()); | 975 EXPECT_EQ(kMinNumSamples, r.times.size()); |
975 | 976 |
976 // The first nudge ran as soon as possible. It failed. | 977 // The first nudge ran as soon as possible. It failed. |
977 TimeTicks optimal_job_time = optimal_start; | 978 TimeTicks optimal_job_time = optimal_start; |
978 EXPECT_GE(r.times[0], optimal_job_time); | 979 EXPECT_GE(r.times[0], optimal_job_time); |
979 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 980 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
980 r.snapshots[0]->source.updates_source); | 981 r.snapshots[0].source().updates_source); |
981 | 982 |
982 // It was followed by a successful retry nudge shortly afterward. | 983 // It was followed by a successful retry nudge shortly afterward. |
983 optimal_job_time = optimal_job_time + backoff; | 984 optimal_job_time = optimal_job_time + backoff; |
984 EXPECT_GE(r.times[1], optimal_job_time); | 985 EXPECT_GE(r.times[1], optimal_job_time); |
985 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, | 986 EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, |
986 r.snapshots[1]->source.updates_source); | 987 r.snapshots[1].source().updates_source); |
987 // After that, we went back to polling. | 988 // After that, we went back to polling. |
988 for (size_t i = 2; i < r.snapshots.size(); i++) { | 989 for (size_t i = 2; i < r.snapshots.size(); i++) { |
989 optimal_job_time = optimal_job_time + poll; | 990 optimal_job_time = optimal_job_time + poll; |
990 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); | 991 SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")"); |
991 EXPECT_GE(r.times[i], optimal_job_time); | 992 EXPECT_GE(r.times[i], optimal_job_time); |
992 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, | 993 EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, |
993 r.snapshots[i]->source.updates_source); | 994 r.snapshots[i].source().updates_source); |
994 } | 995 } |
995 } | 996 } |
996 | 997 |
997 // Test that poll failures are ignored. They should have no effect on | 998 // Test that poll failures are ignored. They should have no effect on |
998 // subsequent poll attempts, nor should they trigger a backoff/retry. | 999 // subsequent poll attempts, nor should they trigger a backoff/retry. |
999 // This test is flaky under memory tools, see http://crbug.com/118370. | 1000 // This test is flaky under memory tools, see http://crbug.com/118370. |
1000 TEST_F(SyncSchedulerTest, FLAKY_TransientPollFailure) { | 1001 TEST_F(SyncSchedulerTest, FLAKY_TransientPollFailure) { |
1001 SyncShareRecords r; | 1002 SyncShareRecords r; |
1002 const TimeDelta poll_interval(TimeDelta::FromMilliseconds(10)); | 1003 const TimeDelta poll_interval(TimeDelta::FromMilliseconds(10)); |
1003 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval); | 1004 scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval); |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1159 PumpLoop(); | 1160 PumpLoop(); |
1160 // Pump again to run job. | 1161 // Pump again to run job. |
1161 PumpLoop(); | 1162 PumpLoop(); |
1162 | 1163 |
1163 StopSyncScheduler(); | 1164 StopSyncScheduler(); |
1164 | 1165 |
1165 EXPECT_TRUE(expected == context()->previous_session_routing_info()); | 1166 EXPECT_TRUE(expected == context()->previous_session_routing_info()); |
1166 } | 1167 } |
1167 | 1168 |
1168 } // namespace browser_sync | 1169 } // namespace browser_sync |
OLD | NEW |