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

Unified Diff: sync/engine/sync_scheduler_unittest.cc

Issue 488843002: [Sync] Add support for server controlled nudge delays (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Self review Created 6 years, 4 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 side-by-side diff with in-line comments
Download patch
Index: sync/engine/sync_scheduler_unittest.cc
diff --git a/sync/engine/sync_scheduler_unittest.cc b/sync/engine/sync_scheduler_unittest.cc
index 268fae2c768b550241e9e3b389be868c8fa8b29f..9ae06b85c646adf938cc7b2e32333fc8dace106d 100644
--- a/sync/engine/sync_scheduler_unittest.cc
+++ b/sync/engine/sync_scheduler_unittest.cc
@@ -127,7 +127,7 @@ class SyncSchedulerTest : public testing::Test {
extensions_activity_ = new ExtensionsActivity();
routing_info_[BOOKMARKS] = GROUP_UI;
- routing_info_[AUTOFILL] = GROUP_DB;
+ routing_info_[TYPED_URLS] = GROUP_DB;
routing_info_[THEMES] = GROUP_UI;
routing_info_[NIGORI] = GROUP_PASSIVE;
@@ -159,6 +159,11 @@ class SyncSchedulerTest : public testing::Test {
BackoffDelayProvider::FromDefaults(),
context(),
syncer_));
+ scheduler_->SetDefaultNudgeDelay(default_delay().InMilliseconds());
+ std::map<ModelType, int> delay_map;
+ // Override the bookmarks value since so many tests rely on it.
+ delay_map[BOOKMARKS] = default_delay().InMilliseconds();
+ scheduler_->OnReceivedCustomNudgeDelays(delay_map);
}
SyncSchedulerImpl* scheduler() { return scheduler_.get(); }
@@ -166,7 +171,7 @@ class SyncSchedulerTest : public testing::Test {
MockSyncer* syncer() { return syncer_; }
MockDelayProvider* delay() { return delay_; }
MockConnectionManager* connection() { return connection_.get(); }
- TimeDelta zero() { return TimeDelta::FromSeconds(0); }
+ TimeDelta default_delay() { return TimeDelta::FromSeconds(0); }
TimeDelta timeout() {
return TestTimeouts::action_timeout();
}
@@ -209,7 +214,7 @@ class SyncSchedulerTest : public testing::Test {
ModelTypeSet nudge_types(BOOKMARKS);
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- scheduler()->ScheduleLocalNudge(zero(), nudge_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
RunLoop();
return scheduler()->IsBackingOff();
@@ -307,7 +312,7 @@ TEST_F(SyncSchedulerTest, Nudge) {
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- scheduler()->ScheduleLocalNudge(zero(), model_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
RunLoop();
Mock::VerifyAndClearExpectations(syncer());
@@ -315,11 +320,11 @@ TEST_F(SyncSchedulerTest, Nudge) {
// Make sure a second, later, nudge is unaffected by first (no coalescing).
SyncShareTimes times2;
model_types.Remove(BOOKMARKS);
- model_types.Put(AUTOFILL);
+ model_types.Put(TYPED_URLS);
EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
RecordSyncShare(&times2)));
- scheduler()->ScheduleLocalNudge(zero(), model_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
RunLoop();
}
@@ -459,7 +464,7 @@ TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) {
EXPECT_CALL(*syncer(), ConfigureSyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateConfigureFailed),
RecordSyncShare(&times)));
- scheduler()->ScheduleLocalNudge(zero(), model_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
RunLoop();
// Note that we're not RunLoop()ing for the NUDGE we just scheduled, but
// for the first retry attempt from the config job (after
@@ -489,11 +494,11 @@ TEST_F(SyncSchedulerTest, NudgeCoalescing) {
EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
RecordSyncShare(&times)));
- const ModelTypeSet types1(BOOKMARKS), types2(AUTOFILL), types3(THEMES);
- TimeDelta delay = zero();
+ const ModelTypeSet types1(BOOKMARKS), types2(TYPED_URLS), types3(THEMES);
+ TimeDelta delay = default_delay();
rlarocque 2014/08/20 00:13:31 Remove |delay|?
Nicolas Zea 2014/08/20 22:49:43 Done.
TimeTicks optimal_time = TimeTicks::Now() + delay;
- scheduler()->ScheduleLocalNudge(delay, types1, FROM_HERE);
- scheduler()->ScheduleLocalNudge(zero(), types2, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
RunLoop();
ASSERT_EQ(1U, times.size());
@@ -505,7 +510,7 @@ TEST_F(SyncSchedulerTest, NudgeCoalescing) {
EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
RecordSyncShare(&times2)));
- scheduler()->ScheduleLocalNudge(zero(), types3, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types3, FROM_HERE);
RunLoop();
}
@@ -517,13 +522,16 @@ TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) {
EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
RecordSyncShare(&times)));
- ModelTypeSet types1(BOOKMARKS), types2(AUTOFILL), types3;
+ ModelTypeSet types1(BOOKMARKS), types2(TYPED_URLS), types3;
// Create a huge time delay.
TimeDelta delay = TimeDelta::FromDays(1);
- scheduler()->ScheduleLocalNudge(delay, types1, FROM_HERE);
- scheduler()->ScheduleLocalNudge(zero(), types2, FROM_HERE);
+ std::map<ModelType, int> delay_map;
+ delay_map[types1.First().Get()] = delay.InMilliseconds();
+ scheduler()->OnReceivedCustomNudgeDelays(delay_map);
+ scheduler()->ScheduleLocalNudge(types1, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types2, FROM_HERE);
TimeTicks min_time = TimeTicks::Now();
TimeTicks max_time = TimeTicks::Now() + delay;
@@ -547,7 +555,7 @@ TEST_F(SyncSchedulerTest, NudgeWithStates) {
RecordSyncShare(&times1)))
.RetiresOnSaturation();
scheduler()->ScheduleInvalidationNudge(
- zero(), BOOKMARKS, BuildInvalidation(10, "test"), FROM_HERE);
+ BOOKMARKS, BuildInvalidation(10, "test"), FROM_HERE);
RunLoop();
Mock::VerifyAndClearExpectations(syncer());
@@ -558,7 +566,7 @@ TEST_F(SyncSchedulerTest, NudgeWithStates) {
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
RecordSyncShare(&times2)));
scheduler()->ScheduleInvalidationNudge(
- zero(), AUTOFILL, BuildInvalidation(10, "test2"), FROM_HERE);
+ TYPED_URLS, BuildInvalidation(10, "test2"), FROM_HERE);
RunLoop();
}
@@ -632,11 +640,13 @@ TEST_F(SyncSchedulerTest, PollIntervalUpdate) {
}
// Test that the sessions commit delay is updated when needed.
-TEST_F(SyncSchedulerTest, SessionsCommitDelay) {
+TEST_F(SyncSchedulerTest, CustomNudgeDelays) {
SyncShareTimes times;
TimeDelta delay1(TimeDelta::FromMilliseconds(120));
TimeDelta delay2(TimeDelta::FromMilliseconds(30));
- scheduler()->OnReceivedSessionsCommitDelay(delay1);
+ std::map<ModelType, int> delay_map;
+ delay_map[SESSIONS] = delay1.InMilliseconds();
+ scheduler()->OnReceivedCustomNudgeDelays(delay_map);
EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
.WillOnce(
@@ -647,15 +657,15 @@ TEST_F(SyncSchedulerTest, SessionsCommitDelay) {
Invoke(sessions::test_util::SimulateNormalSuccess),
QuitLoopNowAction()));
- EXPECT_EQ(delay1, scheduler()->GetSessionsCommitDelay());
+ EXPECT_EQ(delay1, scheduler()->GetNudgeDelayForTypes(ModelTypeSet(SESSIONS)));
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- EXPECT_EQ(delay1, scheduler()->GetSessionsCommitDelay());
+ EXPECT_EQ(delay1, scheduler()->GetNudgeDelayForTypes(ModelTypeSet(SESSIONS)));
const ModelTypeSet model_types(BOOKMARKS);
- scheduler()->ScheduleLocalNudge(zero(), model_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(model_types, FROM_HERE);
RunLoop();
- EXPECT_EQ(delay2, scheduler()->GetSessionsCommitDelay());
+ EXPECT_EQ(delay2, scheduler()->GetNudgeDelayForTypes(ModelTypeSet(SESSIONS)));
StopSyncScheduler();
}
@@ -674,8 +684,7 @@ TEST_F(SyncSchedulerTest, ThrottlingDoesThrottle) {
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- scheduler()->ScheduleLocalNudge(
- TimeDelta::FromMicroseconds(1), types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types, FROM_HERE);
PumpLoop();
StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
@@ -740,7 +749,7 @@ TEST_F(SyncSchedulerTest, ThrottlingExpiresFromNudge) {
const ModelTypeSet types(BOOKMARKS);
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- scheduler()->ScheduleLocalNudge(zero(), types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types, FROM_HERE);
PumpLoop(); // To get PerformDelayedNudge called.
PumpLoop(); // To get TrySyncSessionJob called
@@ -793,7 +802,7 @@ TEST_F(SyncSchedulerTest, ThrottlingExpiresFromConfigure) {
TEST_F(SyncSchedulerTest, TypeThrottlingBlocksNudge) {
UseMockDelayProvider();
EXPECT_CALL(*delay(), GetDelay(_))
- .WillRepeatedly(Return(zero()));
+ .WillRepeatedly(Return(default_delay()));
TimeDelta poll(TimeDelta::FromDays(1));
TimeDelta throttle1(TimeDelta::FromSeconds(60));
@@ -810,13 +819,13 @@ TEST_F(SyncSchedulerTest, TypeThrottlingBlocksNudge) {
.RetiresOnSaturation();
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- scheduler()->ScheduleLocalNudge(zero(), types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types, FROM_HERE);
PumpLoop(); // To get PerformDelayedNudge called.
PumpLoop(); // To get TrySyncSessionJob called
EXPECT_TRUE(GetThrottledTypes().HasAll(types));
// This won't cause a sync cycle because the types are throttled.
- scheduler()->ScheduleLocalNudge(zero(), types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types, FROM_HERE);
PumpLoop();
StopSyncScheduler();
@@ -825,7 +834,7 @@ TEST_F(SyncSchedulerTest, TypeThrottlingBlocksNudge) {
TEST_F(SyncSchedulerTest, TypeThrottlingDoesBlockOtherSources) {
UseMockDelayProvider();
EXPECT_CALL(*delay(), GetDelay(_))
- .WillRepeatedly(Return(zero()));
+ .WillRepeatedly(Return(default_delay()));
SyncShareTimes times;
TimeDelta poll(TimeDelta::FromDays(1));
@@ -845,18 +854,18 @@ TEST_F(SyncSchedulerTest, TypeThrottlingDoesBlockOtherSources) {
.RetiresOnSaturation();
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- scheduler()->ScheduleLocalNudge(zero(), throttled_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(throttled_types, FROM_HERE);
PumpLoop(); // To get PerformDelayedNudge called.
PumpLoop(); // To get TrySyncSessionJob called
EXPECT_TRUE(GetThrottledTypes().HasAll(throttled_types));
// Ignore invalidations for throttled types.
scheduler()->ScheduleInvalidationNudge(
- zero(), BOOKMARKS, BuildInvalidation(10, "test"), FROM_HERE);
+ BOOKMARKS, BuildInvalidation(10, "test"), FROM_HERE);
PumpLoop();
// Ignore refresh requests for throttled types.
- scheduler()->ScheduleLocalRefreshRequest(zero(), throttled_types, FROM_HERE);
+ scheduler()->ScheduleLocalRefreshRequest(throttled_types, FROM_HERE);
PumpLoop();
Mock::VerifyAndClearExpectations(syncer());
@@ -865,7 +874,7 @@ TEST_F(SyncSchedulerTest, TypeThrottlingDoesBlockOtherSources) {
EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
.WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
RecordSyncShare(&times)));
- scheduler()->ScheduleLocalNudge(zero(), unthrottled_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(unthrottled_types, FROM_HERE);
RunLoop();
Mock::VerifyAndClearExpectations(syncer());
@@ -880,9 +889,9 @@ TEST_F(SyncSchedulerTest, ConfigurationMode) {
StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);
- const ModelTypeSet nudge_types(AUTOFILL);
- scheduler()->ScheduleLocalNudge(zero(), nudge_types, FROM_HERE);
- scheduler()->ScheduleLocalNudge(zero(), nudge_types, FROM_HERE);
+ const ModelTypeSet nudge_types(TYPED_URLS);
+ scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(nudge_types, FROM_HERE);
const ModelTypeSet config_types(BOOKMARKS);
@@ -1026,7 +1035,7 @@ TEST_F(SyncSchedulerTest, BackoffDropsJobs) {
// This nudge should fail and put us into backoff. Thanks to our mock
// GetDelay() setup above, this will be a long backoff.
- scheduler()->ScheduleLocalNudge(zero(), types, FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types, FROM_HERE);
RunLoop();
// From this point forward, no SyncShare functions should be invoked.
@@ -1036,10 +1045,7 @@ TEST_F(SyncSchedulerTest, BackoffDropsJobs) {
PumpLoopFor(poll * 10);
// Try (and fail) to schedule a nudge.
- scheduler()->ScheduleLocalNudge(
- base::TimeDelta::FromMilliseconds(10),
- types,
- FROM_HERE);
+ scheduler()->ScheduleLocalNudge(types, FROM_HERE);
Mock::VerifyAndClearExpectations(syncer());
Mock::VerifyAndClearExpectations(delay());
@@ -1092,7 +1098,7 @@ TEST_F(SyncSchedulerTest, BackoffElevation) {
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
// Run again with a nudge.
- scheduler()->ScheduleLocalNudge(zero(), ModelTypeSet(BOOKMARKS), FROM_HERE);
+ scheduler()->ScheduleLocalNudge(ModelTypeSet(BOOKMARKS), FROM_HERE);
RunLoop();
ASSERT_EQ(kMinNumSamples, times.size());
@@ -1120,7 +1126,7 @@ TEST_F(SyncSchedulerTest, BackoffRelief) {
EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
RecordSyncShare(&times)));
- scheduler()->ScheduleLocalNudge(zero(), ModelTypeSet(BOOKMARKS), FROM_HERE);
+ scheduler()->ScheduleLocalNudge(ModelTypeSet(BOOKMARKS), FROM_HERE);
RunLoop();
Mock::VerifyAndClearExpectations(syncer());
TimeTicks optimal_job_time = optimal_start;
@@ -1192,7 +1198,7 @@ TEST_F(SyncSchedulerTest, StartWhenNotConnected) {
Return(true)));
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
- scheduler()->ScheduleLocalNudge(zero(), ModelTypeSet(BOOKMARKS), FROM_HERE);
+ scheduler()->ScheduleLocalNudge(ModelTypeSet(BOOKMARKS), FROM_HERE);
// Should save the nudge for until after the server is reachable.
base::MessageLoop::current()->RunUntilIdle();
@@ -1217,7 +1223,7 @@ TEST_F(SyncSchedulerTest, ServerConnectionChangeDuringBackoff) {
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
Return(true)));
- scheduler()->ScheduleLocalNudge(zero(), ModelTypeSet(BOOKMARKS), FROM_HERE);
+ scheduler()->ScheduleLocalNudge(ModelTypeSet(BOOKMARKS), FROM_HERE);
PumpLoop(); // To get PerformDelayedNudge called.
PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
ASSERT_TRUE(scheduler()->IsBackingOff());
@@ -1249,7 +1255,7 @@ TEST_F(SyncSchedulerTest, ConnectionChangeCanaryPreemptedByNudge) {
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateNormalSuccess),
QuitLoopNowAction()));
- scheduler()->ScheduleLocalNudge(zero(), ModelTypeSet(BOOKMARKS), FROM_HERE);
+ scheduler()->ScheduleLocalNudge(ModelTypeSet(BOOKMARKS), FROM_HERE);
PumpLoop(); // To get PerformDelayedNudge called.
PumpLoop(); // Run the nudge, that will fail and schedule a quick retry.
@@ -1260,7 +1266,7 @@ TEST_F(SyncSchedulerTest, ConnectionChangeCanaryPreemptedByNudge) {
PumpLoop();
connection()->SetServerReachable();
connection()->UpdateConnectionStatus();
- scheduler()->ScheduleLocalNudge(zero(), ModelTypeSet(BOOKMARKS), FROM_HERE);
+ scheduler()->ScheduleLocalNudge(ModelTypeSet(BOOKMARKS), FROM_HERE);
base::MessageLoop::current()->RunUntilIdle();
}
@@ -1381,8 +1387,7 @@ TEST_F(SyncSchedulerTest, ReceiveNewRetryDelay) {
base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(100);
base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(200);
- scheduler()->ScheduleLocalRefreshRequest(zero(), ModelTypeSet(BOOKMARKS),
- FROM_HERE);
+ scheduler()->ScheduleLocalNudge(ModelTypeSet(BOOKMARKS), FROM_HERE);
scheduler()->OnReceivedGuRetryDelay(delay1);
EXPECT_EQ(delay1, GetRetryTimerDelay());
@@ -1406,4 +1411,64 @@ TEST_F(SyncSchedulerTest, ReceiveNewRetryDelay) {
StopSyncScheduler();
}
+// Test the default nudge delays for various types, and that we can overwrite
+// them with custom delays.
+TEST_F(SyncSchedulerTest, NudgeDelayTest) {
+ // Reset all delays to default
+ std::map<ModelType, int> delay_map;
+ ModelTypeSet protocol_types = syncer::ProtocolTypes();
+ for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
+ iter.Inc()) {
+ delay_map[iter.Get()] = -1;
+ }
+ scheduler()->OnReceivedCustomNudgeDelays(delay_map);
+
+ // Bookmarks and preference both have "slow nudge" delays.
+ EXPECT_EQ(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(BOOKMARKS)),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(PREFERENCES)));
+
+ // Typed URLs has a default delay.
+ EXPECT_EQ(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(TYPED_URLS)),
+ default_delay());
+
+ // "Slow nudge" delays are longer than the default.
+ EXPECT_GT(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(BOOKMARKS)),
+ default_delay());
+
+ // Sessions is longer than "slow nudge".
+ EXPECT_GT(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(SESSIONS)),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(BOOKMARKS)));
+
+ // Favicons have the same delay as sessions.
+ EXPECT_EQ(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(SESSIONS)),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(FAVICON_TRACKING)));
+
+ // Autofill has the longer delay of all.
+ EXPECT_GT(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(AUTOFILL)),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(SESSIONS)));
+
+ // A nudge with no types takes the longest delay.
+ EXPECT_GT(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(AUTOFILL)),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet()));
+
+ // The actual nudge delay should be the shortest of the set.
+ EXPECT_EQ(
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(TYPED_URLS)),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(TYPED_URLS, AUTOFILL)));
+
+ // Set some custom delays.
+ delay_map[BOOKMARKS] = 10000;
+ delay_map[SESSIONS] = 2000;
+ scheduler()->OnReceivedCustomNudgeDelays(delay_map);
+
+ // Only those with custom delays should be affected, not another type.
+ EXPECT_NE(scheduler()->GetNudgeDelayForTypes(ModelTypeSet(BOOKMARKS)),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(PREFERENCES)));
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(10000),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(BOOKMARKS)));
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(2000),
+ scheduler()->GetNudgeDelayForTypes(ModelTypeSet(SESSIONS)));
+}
+
} // namespace syncer

Powered by Google App Engine
This is Rietveld 408576698