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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/sync/engine/syncer_thread2_unittest.cc
diff --git a/chrome/browser/sync/engine/syncer_thread2_unittest.cc b/chrome/browser/sync/engine/syncer_thread2_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..67373cd84117f5e4a161adc5074d2e85825560e5
--- /dev/null
+++ b/chrome/browser/sync/engine/syncer_thread2_unittest.cc
@@ -0,0 +1,338 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.h"
+#include "chrome/browser/sync/engine/mock_model_safe_workers.h"
+#include "chrome/browser/sync/engine/syncer.h"
+#include "chrome/browser/sync/engine/syncer_thread2.h"
+#include "chrome/browser/sync/sessions/test_util.h"
+#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+using testing::_;
+using testing::AtLeast;
+using testing::DoAll;
+using testing::Field;
+using testing::Invoke;
+using testing::WithArg;
+
+namespace browser_sync {
+using sessions::SyncSession;
+using sessions::SyncSessionContext;
+using sessions::SyncSessionSnapshot;
+using syncable::ModelTypeBitSet;
+using sync_pb::GetUpdatesCallerInfo;
+
+class MockSyncer : public Syncer {
+ public:
+ MOCK_METHOD1(SyncShare, void(sessions::SyncSession*));
+};
+
+namespace s3 {
+
+ACTION_P(SignalEvent, event) {
+ event->Signal();
+}
+
+// Used when tests want to record syncing activity to examine later.
+struct SyncShareRecords {
+ std::vector<TimeTicks> times;
+ std::vector<linked_ptr<SyncSessionSnapshot> > snapshots;
+};
+
+// Convenient to use in tests wishing to analyze SyncShare calls over time.
+static const size_t kMinNumSamples = 5;
+
+class SyncerThread2Test : public testing::Test {
+ public:
+ virtual void SetUp() {
+ syncdb_.SetUp();
+ syncer_ = new MockSyncer();
+ registrar_.reset(MockModelSafeWorkerRegistrar::PassiveBookmarks());
+ context_ = new SyncSessionContext(NULL, syncdb_.manager(),
+ registrar_.get(), std::vector<SyncEngineEventListener*>());
+ context_->set_notifications_enabled(true);
+ context_->set_account_name("Test");
+ syncer_thread_.reset(new SyncerThread(context_, syncer_));
+ // TODO(tim): Once the SCM is hooked up, remove this.
+ syncer_thread_->server_connection_ok_ = true;
+ }
+
+ SyncerThread* syncer_thread() { return syncer_thread_.get(); }
+ MockSyncer* syncer() { return syncer_; }
+ TimeDelta zero() { return TimeDelta::FromSeconds(0); }
+ TimeDelta timeout() {
+ return TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms());
+ }
+
+ virtual void TearDown() {
+ syncer_thread()->Stop();
+ syncdb_.TearDown();
+ }
+
+ void AnalyzePollRun(const SyncShareRecords& records, size_t min_num_samples,
+ const TimeTicks& optimal_start, const TimeDelta& poll_interval) {
+ const std::vector<TimeTicks>& data(records.times);
+ EXPECT_GE(data.size(), min_num_samples);
+ for (size_t i = 0; i < data.size(); i++) {
+ SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
+ TimeTicks optimal_next_sync = optimal_start + poll_interval * i;
+ EXPECT_GE(data[i], optimal_next_sync);
+ EXPECT_LT(data[i], optimal_next_sync + poll_interval);
+ EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC,
+ records.snapshots[i]->source.first);
+ }
+ }
+
+ private:
+ scoped_ptr<SyncerThread> syncer_thread_;
+ SyncSessionContext* context_;
+ MockSyncer* syncer_;
+ scoped_ptr<MockModelSafeWorkerRegistrar> registrar_;
+ MockDirectorySetterUpper syncdb_;
+};
+
+ACTION_P3(RecordSyncShare, record, signal_after, event) {
+ record->times.push_back(TimeTicks::Now());
+ record->snapshots.push_back(make_linked_ptr(new SyncSessionSnapshot(
+ arg0->TakeSnapshot())));
+ if (event && record->times.size() >= signal_after)
+ event->Signal();
+}
+
+// Test nudge scheduling.
+TEST_F(SyncerThread2Test, Nudge) {
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ base::WaitableEvent done(false, false);
+ SyncShareRecords records;
+ syncable::ModelTypeBitSet model_types;
+ model_types[syncable::BOOKMARKS] = true;
+
+ EXPECT_CALL(*syncer(), SyncShare(_))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done))));
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types);
+ done.TimedWait(timeout());
+
+ EXPECT_EQ(1, records.snapshots.size());
+ EXPECT_EQ(model_types, records.snapshots[0]->source.second);
+ EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, records.snapshots[0]->source.first);
+}
+
+// Test that nudges are coalesced.
+TEST_F(SyncerThread2Test, NudgeCoalescing) {
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ base::WaitableEvent done(false, false);
+ SyncShareRecords r;
+ EXPECT_CALL(*syncer(), SyncShare(_))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&r, 1U, &done))));
+ syncable::ModelTypeBitSet types1, types2, types3;
+ types1[syncable::BOOKMARKS] = true;
+ types2[syncable::AUTOFILL] = true;
+ types3[syncable::THEMES] = true;
+ TimeDelta delay = TimeDelta::FromMilliseconds(20);
+ TimeTicks optimal_time = TimeTicks::Now() + delay;
+ syncer_thread()->ScheduleNudge(delay, NUDGE_SOURCE_UNKNOWN, types1);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types2);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3);
+ done.TimedWait(timeout());
+
+ EXPECT_EQ(1, r.snapshots.size());
+ EXPECT_GE(r.times[0], optimal_time);
+ EXPECT_EQ(types1 | types2 | types3, r.snapshots[0]->source.second);
+ EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, r.snapshots[0]->source.first);
+
+ SyncShareRecords r2;
+ EXPECT_CALL(*syncer(), SyncShare(_))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&r2, 1U, &done))));
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3);
+ done.TimedWait(timeout());
+ EXPECT_EQ(1, r2.snapshots.size());
+ EXPECT_EQ(types3, r2.snapshots[0]->source.second);
+ EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, r2.snapshots[0]->source.first);
+}
+
+// Test that polling works as expected.
+TEST_F(SyncerThread2Test, Polling) {
+ SyncShareRecords records;
+ base::WaitableEvent done(false, false);
+ TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
+ syncer_thread()->OnReceivedLongPollIntervalUpdate(poll_interval);
+ EXPECT_CALL(*syncer(), SyncShare(_)).Times(AtLeast(kMinNumSamples))
+ .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
+
+ TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ done.TimedWait(timeout());
+ syncer_thread()->Stop();
+
+ AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval);
+}
+
+// Test that the short poll interval is used.
+TEST_F(SyncerThread2Test, PollNotificationsDisabled) {
+ SyncShareRecords records;
+ base::WaitableEvent done(false, false);
+ TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
+ syncer_thread()->OnReceivedShortPollIntervalUpdate(poll_interval);
+ syncer_thread()->set_notifications_enabled(false);
+ EXPECT_CALL(*syncer(), SyncShare(_)).Times(AtLeast(kMinNumSamples))
+ .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
+
+ TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ done.TimedWait(timeout());
+ syncer_thread()->Stop();
+
+ AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval);
+}
+
+// Test that polling intervals are updated when needed.
+TEST_F(SyncerThread2Test, PollIntervalUpdate) {
+ SyncShareRecords records;
+ base::WaitableEvent done(false, false);
+ TimeDelta poll1(TimeDelta::FromMilliseconds(120));
+ TimeDelta poll2(TimeDelta::FromMilliseconds(30));
+ syncer_thread()->OnReceivedLongPollIntervalUpdate(poll1);
+ EXPECT_CALL(*syncer(), SyncShare(_)).Times(AtLeast(kMinNumSamples))
+ .WillOnce(WithArg<0>(
+ sessions::test_util::SimulatePollIntervalUpdate(poll2)))
+ .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
+
+ TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2;
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ done.TimedWait(timeout());
+ syncer_thread()->Stop();
+
+ AnalyzePollRun(records, kMinNumSamples, optimal_start, poll2);
+}
+
+// Test that a sync session is run through to completion.
+TEST_F(SyncerThread2Test, HasMoreToSync) {
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ base::WaitableEvent done(false, false);
+ EXPECT_CALL(*syncer(), SyncShare(_))
+ .WillOnce(Invoke(sessions::test_util::SimulateHasMoreToSync))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ SignalEvent(&done)));
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet());
+ done.TimedWait(timeout());
+ // If more nudges are scheduled, they'll be waited on by TearDown, and would
+ // cause our expectation to break.
+}
+
+// Test that no syncing occurs when throttled.
+TEST_F(SyncerThread2Test, ThrottlingDoesThrottle) {
+ syncable::ModelTypeBitSet types;
+ types[syncable::BOOKMARKS] = true;
+ base::WaitableEvent done(false, false);
+ TimeDelta poll(TimeDelta::FromMilliseconds(5));
+ TimeDelta throttle(TimeDelta::FromMinutes(10));
+ syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
+ EXPECT_CALL(*syncer(), SyncShare(_))
+ .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle)));
+
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types);
+ done.TimedWait(poll * 10);
+ done.Reset();
+
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
+ syncer_thread()->ScheduleConfig(zero(), types);
+ done.TimedWait(poll * 5);
+}
+
+TEST_F(SyncerThread2Test, ThrottlingExpires) {
+ SyncShareRecords records;
+ base::WaitableEvent done(false, false);
+ TimeDelta poll(TimeDelta::FromMilliseconds(15));
+ TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
+ TimeDelta throttle2(TimeDelta::FromMinutes(10));
+ syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
+
+ ::testing::InSequence seq;
+ EXPECT_CALL(*syncer(), SyncShare(_))
+ .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle1)))
+ .RetiresOnSaturation();
+ EXPECT_CALL(*syncer(), SyncShare(_))
+ .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
+
+ TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1;
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ done.TimedWait(timeout());
+ syncer_thread()->Stop();
+
+ AnalyzePollRun(records, kMinNumSamples, optimal_start, poll);
+}
+
+// Test nudges / polls don't run in config mode and config tasks do.
+TEST_F(SyncerThread2Test, ConfigurationMode) {
+ TimeDelta poll(TimeDelta::FromMilliseconds(15));
+ base::WaitableEvent done(false, false);
+ syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
+ EXPECT_CALL(*syncer(), SyncShare(_)).Times(0);
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
+ syncable::ModelTypeBitSet nudge_types;
+ nudge_types[syncable::AUTOFILL] = true;
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types);
+
+ syncable::ModelTypeBitSet config_types;
+ config_types[syncable::BOOKMARKS] = true;
+ // TODO(tim): This will fail once CONFIGURATION tasks are implemented. Update
+ // the EXPECT call and timeout when that happens.
+ syncer_thread()->ScheduleConfig(zero(), config_types);
+ done.TimedWait(poll * 10);
+}
+
+// Test job/mode contract is honoured when alternating between modes.
+TEST_F(SyncerThread2Test, ChangeModes) {
+
+}
+
+// Test that exponential backoff is properly triggered.
+TEST_F(SyncerThread2Test, BackoffTriggered) {
+}
+
+// Test that no polls or extraneous nudges occur when in backoff.
+TEST_F(SyncerThread2Test, BackoffDropsJobs) {
+}
+
+// Test that backoff is shaping traffic properly with consecutive errors.
+TEST_F(SyncerThread2Test, BackoffElevation) {
+}
+
+// Test that things go back to normal once a canary task makes forward progress
+// following a succession of failures.
+TEST_F(SyncerThread2Test, BackoffRelief) {
+}
+
+TEST_F(SyncerThread2Test, StopSyncPermanently) {
+}
+
+TEST_F(SyncerThread2Test, GetRecommendedDelay) {
+}
+
+// Test config tasks don't run during normal mode.
+TEST_F(SyncerThread2Test, DISABLED_NoConfigDuringNormal) {
+}
+
+// Test that starting the syncer thread without a valid connection doesn't
+// break things when a connection is detected.
+TEST_F(SyncerThread2Test, DISABLED_StartWhenNotConnected) {
+
+}
+
+} // namespace s3
+} // namespace browser_sync

Powered by Google App Engine
This is Rietveld 408576698