| 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..b0adecd42b1c9589f849d8a7ca5796c0cea80348
|
| --- /dev/null
|
| +++ b/chrome/browser/sync/engine/syncer_thread2_unittest.cc
|
| @@ -0,0 +1,554 @@
|
| +// Copyright (c) 2011 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::Eq;
|
| +using testing::Invoke;
|
| +using testing::Mock;
|
| +using testing::Return;
|
| +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 {
|
| +
|
| +// 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:
|
| + class MockDelayProvider : public SyncerThread::DelayProvider {
|
| + public:
|
| + MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&));
|
| + };
|
| +
|
| + virtual void SetUp() {
|
| + syncdb_.SetUp();
|
| + syncer_ = new MockSyncer();
|
| + delay_ = NULL;
|
| + 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_; }
|
| + MockDelayProvider* delay() { return delay_; }
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + bool GetBackoffAndResetTest(base::WaitableEvent* done) {
|
| + syncable::ModelTypeBitSet nudge_types;
|
| + syncer_thread()->Start(SyncerThread::NORMAL_MODE);
|
| + syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types);
|
| + done->TimedWait(timeout());
|
| + TearDown();
|
| + done->Reset();
|
| + Mock::VerifyAndClearExpectations(syncer());
|
| + bool backing_off = syncer_thread()->IsBackingOff();
|
| + SetUp();
|
| + UseMockDelayProvider();
|
| + EXPECT_CALL(*delay(), GetDelay(_))
|
| + .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1)));
|
| + return backing_off;
|
| + }
|
| +
|
| + void UseMockDelayProvider() {
|
| + delay_ = new MockDelayProvider();
|
| + syncer_thread_->delay_provider_.reset(delay_);
|
| + }
|
| +
|
| + void PostSignalTask(base::WaitableEvent* done) {
|
| + syncer_thread_->thread_.message_loop()->PostTask(FROM_HERE,
|
| + NewRunnableFunction(&SyncerThread2Test::SignalWaitableEvent, done));
|
| + }
|
| +
|
| + void FlushLastTask(base::WaitableEvent* done) {
|
| + PostSignalTask(done);
|
| + done->TimedWait(timeout());
|
| + done->Reset();
|
| + }
|
| +
|
| + static void SignalWaitableEvent(base::WaitableEvent* event) {
|
| + event->Signal();
|
| + }
|
| +
|
| + private:
|
| + scoped_ptr<SyncerThread> syncer_thread_;
|
| + SyncSessionContext* context_;
|
| + MockSyncer* syncer_;
|
| + MockDelayProvider* delay_;
|
| + scoped_ptr<MockModelSafeWorkerRegistrar> registrar_;
|
| + MockDirectorySetterUpper syncdb_;
|
| +};
|
| +
|
| +bool RecordSyncShareImpl(SyncSession* s, SyncShareRecords* record,
|
| + size_t signal_after) {
|
| + record->times.push_back(TimeTicks::Now());
|
| + record->snapshots.push_back(make_linked_ptr(new SyncSessionSnapshot(
|
| + s->TakeSnapshot())));
|
| + return record->times.size() >= signal_after;
|
| +}
|
| +
|
| +ACTION_P4(RecordSyncShareAndPostSignal, record, signal_after, test, event) {
|
| + if (RecordSyncShareImpl(arg0, record, signal_after) && event)
|
| + test->PostSignalTask(event);
|
| +}
|
| +
|
| +ACTION_P3(RecordSyncShare, record, signal_after, event) {
|
| + if (RecordSyncShareImpl(arg0, record, signal_after) && event)
|
| + event->Signal();
|
| +}
|
| +
|
| +ACTION_P(SignalEvent, event) {
|
| + SyncerThread2Test::SignalWaitableEvent(event);
|
| +}
|
| +
|
| +// 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(1U, 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(1U, 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(1U, 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);
|
| + FlushLastTask(&done);
|
| +
|
| + syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
|
| + syncer_thread()->ScheduleConfig(zero(), types);
|
| + FlushLastTask(&done);
|
| +}
|
| +
|
| +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 when that happens.
|
| + syncer_thread()->ScheduleConfig(zero(), config_types);
|
| + FlushLastTask(&done);
|
| +}
|
| +
|
| +// Test that exponential backoff is properly triggered.
|
| +TEST_F(SyncerThread2Test, BackoffTriggers) {
|
| + base::WaitableEvent done(false, false);
|
| + UseMockDelayProvider();
|
| +
|
| + EXPECT_CALL(*syncer(), SyncShare(_))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed))
|
| + .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
|
| + SignalEvent(&done)));
|
| + EXPECT_FALSE(GetBackoffAndResetTest(&done));
|
| + // Note GetBackoffAndResetTest clears mocks and re-instantiates the syncer.
|
| + EXPECT_CALL(*syncer(), SyncShare(_))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed))
|
| + .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
|
| + SignalEvent(&done)));
|
| + EXPECT_FALSE(GetBackoffAndResetTest(&done));
|
| + EXPECT_CALL(*syncer(), SyncShare(_))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed))
|
| + .WillRepeatedly(DoAll(Invoke(
|
| + sessions::test_util::SimulateDownloadUpdatesFailed),
|
| + SignalEvent(&done)));
|
| + EXPECT_TRUE(GetBackoffAndResetTest(&done));
|
| + EXPECT_CALL(*syncer(), SyncShare(_))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed))
|
| + .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
|
| + SignalEvent(&done)));
|
| + EXPECT_TRUE(GetBackoffAndResetTest(&done));
|
| + EXPECT_CALL(*syncer(), SyncShare(_))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed))
|
| + .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
|
| + SignalEvent(&done)));
|
| + EXPECT_FALSE(GetBackoffAndResetTest(&done));
|
| + EXPECT_CALL(*syncer(), SyncShare(_))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed))
|
| + .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
|
| + SignalEvent(&done)));
|
| + EXPECT_FALSE(GetBackoffAndResetTest(&done));
|
| +}
|
| +
|
| +// Test that no polls or extraneous nudges occur when in backoff.
|
| +TEST_F(SyncerThread2Test, BackoffDropsJobs) {
|
| + SyncShareRecords r;
|
| + TimeDelta poll(TimeDelta::FromMilliseconds(5));
|
| + base::WaitableEvent done(false, false);
|
| + syncable::ModelTypeBitSet types;
|
| + types[syncable::BOOKMARKS] = true;
|
| + syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
|
| + UseMockDelayProvider();
|
| +
|
| + EXPECT_CALL(*syncer(), SyncShare(_)).Times(2)
|
| + .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
|
| + RecordSyncShareAndPostSignal(&r, 2U, this, &done)));
|
| + EXPECT_CALL(*delay(), GetDelay(_))
|
| + .WillRepeatedly(Return(TimeDelta::FromDays(1)));
|
| +
|
| + syncer_thread()->Start(SyncerThread::NORMAL_MODE);
|
| + ASSERT_TRUE(done.TimedWait(timeout()));
|
| + done.Reset();
|
| +
|
| + Mock::VerifyAndClearExpectations(syncer());
|
| + EXPECT_EQ(2U, r.snapshots.size());
|
| + EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, r.snapshots[0]->source.first);
|
| + EXPECT_EQ(GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION,
|
| + r.snapshots[1]->source.first);
|
| +
|
| + EXPECT_CALL(*syncer(), SyncShare(_)).Times(1)
|
| + .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
|
| + RecordSyncShareAndPostSignal(&r, 1U, this, &done)));
|
| +
|
| + // We schedule a nudge with enough delay (10X poll interval) that at least
|
| + // one or two polls would have taken place. The nudge should succeed.
|
| + syncer_thread()->ScheduleNudge(poll * 10, NUDGE_SOURCE_LOCAL, types);
|
| + ASSERT_TRUE(done.TimedWait(timeout()));
|
| + done.Reset();
|
| +
|
| + Mock::VerifyAndClearExpectations(syncer());
|
| + Mock::VerifyAndClearExpectations(delay());
|
| + EXPECT_EQ(3U, r.snapshots.size());
|
| + EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[2]->source.first);
|
| +
|
| + EXPECT_CALL(*syncer(), SyncShare(_)).Times(0);
|
| + EXPECT_CALL(*delay(), GetDelay(_)).Times(0);
|
| +
|
| + syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
|
| + syncer_thread()->ScheduleConfig(zero(), types);
|
| + FlushLastTask(&done);
|
| +
|
| + syncer_thread()->Start(SyncerThread::NORMAL_MODE);
|
| + syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types);
|
| + syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types);
|
| + FlushLastTask(&done);
|
| +}
|
| +
|
| +// Test that backoff is shaping traffic properly with consecutive errors.
|
| +TEST_F(SyncerThread2Test, BackoffElevation) {
|
| + SyncShareRecords r;
|
| + const TimeDelta poll(TimeDelta::FromMilliseconds(10));
|
| + base::WaitableEvent done(false, false);
|
| + syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
|
| + UseMockDelayProvider();
|
| +
|
| + const TimeDelta first = TimeDelta::FromSeconds(1);
|
| + const TimeDelta second = TimeDelta::FromMilliseconds(10);
|
| + const TimeDelta third = TimeDelta::FromMilliseconds(20);
|
| + const TimeDelta fourth = TimeDelta::FromMilliseconds(30);
|
| + const TimeDelta fifth = TimeDelta::FromDays(1);
|
| +
|
| + EXPECT_CALL(*syncer(), SyncShare(_)).Times(kMinNumSamples)
|
| + .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
|
| + RecordSyncShareAndPostSignal(&r, kMinNumSamples, this, &done)));
|
| +
|
| + EXPECT_CALL(*delay(), GetDelay(Eq(first))).WillOnce(Return(second))
|
| + .RetiresOnSaturation();
|
| + EXPECT_CALL(*delay(), GetDelay(Eq(second))).WillOnce(Return(third))
|
| + .RetiresOnSaturation();
|
| + EXPECT_CALL(*delay(), GetDelay(Eq(third))).WillOnce(Return(fourth))
|
| + .RetiresOnSaturation();
|
| + EXPECT_CALL(*delay(), GetDelay(Eq(fourth))).WillOnce(Return(fifth));
|
| +
|
| + syncer_thread()->Start(SyncerThread::NORMAL_MODE);
|
| + ASSERT_TRUE(done.TimedWait(timeout()));
|
| +
|
| + EXPECT_GE(r.times[2] - r.times[1], second);
|
| + EXPECT_GE(r.times[3] - r.times[2], third);
|
| + EXPECT_GE(r.times[4] - r.times[3], fourth);
|
| +}
|
| +
|
| +// Test that things go back to normal once a canary task makes forward progress
|
| +// following a succession of failures.
|
| +TEST_F(SyncerThread2Test, BackoffRelief) {
|
| + SyncShareRecords r;
|
| + const TimeDelta poll(TimeDelta::FromMilliseconds(10));
|
| + base::WaitableEvent done(false, false);
|
| + syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
|
| + UseMockDelayProvider();
|
| +
|
| + const TimeDelta backoff = TimeDelta::FromMilliseconds(100);
|
| +
|
| + EXPECT_CALL(*syncer(), SyncShare(_))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed))
|
| + .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed))
|
| + .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
|
| + RecordSyncShareAndPostSignal(&r, kMinNumSamples, this, &done)));
|
| + EXPECT_CALL(*delay(), GetDelay(_)).WillOnce(Return(backoff));
|
| +
|
| + // Optimal start for the post-backoff poll party.
|
| + TimeTicks optimal_start = TimeTicks::Now() + poll + backoff;
|
| + syncer_thread()->Start(SyncerThread::NORMAL_MODE);
|
| + done.TimedWait(timeout());
|
| +
|
| + // Check for healthy polling after backoff is relieved.
|
| + // Can't use AnalyzePollRun because first sync is a continuation. Bleh.
|
| + for (size_t i = 0; i < r.times.size(); i++) {
|
| + SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
|
| + TimeTicks optimal_next_sync = optimal_start + poll * i;
|
| + EXPECT_GE(r.times[i], optimal_next_sync);
|
| + EXPECT_LT(r.times[i], optimal_next_sync + poll);
|
| + EXPECT_EQ(i == 0 ? GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION
|
| + : GetUpdatesCallerInfo::PERIODIC,
|
| + r.snapshots[i]->source.first);
|
| + }
|
| +}
|
| +
|
| +TEST_F(SyncerThread2Test, GetRecommendedDelay) {
|
| + EXPECT_LE(TimeDelta::FromSeconds(0),
|
| + SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(0)));
|
| + EXPECT_LE(TimeDelta::FromSeconds(1),
|
| + SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(1)));
|
| + EXPECT_LE(TimeDelta::FromSeconds(50),
|
| + SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(50)));
|
| + EXPECT_LE(TimeDelta::FromSeconds(10),
|
| + SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(10)));
|
| + EXPECT_EQ(TimeDelta::FromSeconds(kMaxBackoffSeconds),
|
| + SyncerThread::GetRecommendedDelay(
|
| + TimeDelta::FromSeconds(kMaxBackoffSeconds)));
|
| + EXPECT_EQ(TimeDelta::FromSeconds(kMaxBackoffSeconds),
|
| + SyncerThread::GetRecommendedDelay(
|
| + TimeDelta::FromSeconds(kMaxBackoffSeconds + 1)));
|
| +}
|
| +
|
| +// Test config tasks don't run during normal mode.
|
| +// TODO(tim): Implement this test and then the functionality!
|
| +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 config tasks don't run during normal mode.
|
| +// TODO(tim): Implement this test and then the functionality!
|
| +TEST_F(SyncerThread2Test, DISABLED_StartWhenNotConnected) {
|
| +
|
| +}
|
| +
|
| +} // namespace s3
|
| +} // namespace browser_sync
|
| +
|
| +// SyncerThread won't outlive the test!
|
| +DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::s3::SyncerThread2Test);
|
| +
|
|
|