| // Copyright (c) 2009 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 <list> |
| #include <map> |
| |
| #include "base/lock.h" |
| #include "base/scoped_ptr.h" |
| #include "base/time.h" |
| #include "base/waitable_event.h" |
| #include "chrome/browser/sync/engine/model_safe_worker.h" |
| #include "chrome/browser/sync/engine/syncer_thread.h" |
| #include "chrome/browser/sync/engine/syncer_types.h" |
| #include "chrome/browser/sync/sessions/sync_session_context.h" |
| #include "chrome/browser/sync/util/channel.h" |
| #include "chrome/test/sync/engine/mock_connection_manager.h" |
| #include "chrome/test/sync/engine/test_directory_setter_upper.h" |
| #include "chrome/test/sync/sessions/test_scoped_session_event_listener.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using base::TimeTicks; |
| using base::TimeDelta; |
| using base::WaitableEvent; |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::Field; |
| |
| namespace browser_sync { |
| using sessions::ErrorCounters; |
| using sessions::TestScopedSessionEventListener; |
| using sessions::SyncSessionContext; |
| using sessions::SyncSessionSnapshot; |
| using sessions::SyncerStatus; |
| |
| typedef testing::Test SyncerThreadTest; |
| typedef SyncerThread::WaitInterval WaitInterval; |
| |
| ACTION_P(SignalEvent, event) { |
| event->Signal(); |
| } |
| |
| SyncSessionSnapshot SessionSnapshotForTest( |
| int64 num_server_changes_remaining, int64 max_local_timestamp, |
| int64 unsynced_count) { |
| return SyncSessionSnapshot(SyncerStatus(), ErrorCounters(), |
| num_server_changes_remaining, max_local_timestamp, false, |
| syncable::ModelTypeBitSet(), false, false, unsynced_count, 0, false); |
| } |
| |
| class ListenerMock : public SyncEngineEventListener { |
| public: |
| MOCK_METHOD1(OnSyncEngineEvent, void(const SyncEngineEvent&)); |
| }; |
| |
| class SyncerThreadWithSyncerTest : public testing::Test, |
| public ModelSafeWorkerRegistrar, |
| public SyncEngineEventListener { |
| public: |
| SyncerThreadWithSyncerTest() |
| : max_wait_time_(TimeDelta::FromSeconds(10)), |
| sync_cycle_ended_event_(false, false) {} |
| virtual void SetUp() { |
| metadb_.SetUp(); |
| connection_.reset(new MockConnectionManager(metadb_.manager(), |
| metadb_.name())); |
| worker_ = new ModelSafeWorker(); |
| std::vector<SyncEngineEventListener*> listeners; |
| listeners.push_back(this); |
| context_ = new SyncSessionContext(connection_.get(), metadb_.manager(), |
| this, listeners); |
| syncer_thread_ = new SyncerThread(context_); |
| syncer_thread_->SetConnected(true); |
| syncable::ModelTypeBitSet expected_types; |
| expected_types[syncable::BOOKMARKS] = true; |
| connection_->ExpectGetUpdatesRequestTypes(expected_types); |
| } |
| virtual void TearDown() { |
| context_ = NULL; |
| syncer_thread_ = NULL; |
| connection_.reset(); |
| metadb_.TearDown(); |
| } |
| |
| // ModelSafeWorkerRegistrar implementation. |
| virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) { |
| out->push_back(worker_.get()); |
| } |
| |
| virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) { |
| // We're just testing the sync engine here, so we shunt everything to |
| // the SyncerThread. |
| (*out)[syncable::BOOKMARKS] = GROUP_PASSIVE; |
| } |
| |
| ManuallyOpenedTestDirectorySetterUpper* metadb() { return &metadb_; } |
| MockConnectionManager* connection() { return connection_.get(); } |
| SyncerThread* syncer_thread() { return syncer_thread_; } |
| |
| // Waits an indefinite amount of sync cycles for the syncer thread to become |
| // throttled. Only call this if a throttle is supposed to occur! |
| bool WaitForThrottle() { |
| int max_cycles = 5; |
| while (max_cycles && !syncer_thread()->IsSyncingCurrentlySilenced()) { |
| sync_cycle_ended_event_.TimedWait(max_wait_time_); |
| max_cycles--; |
| } |
| |
| return syncer_thread()->IsSyncingCurrentlySilenced(); |
| } |
| |
| void WaitForDisconnect() { |
| // Wait for the SyncerThread to detect loss of connection, up to a max of |
| // 10 seconds to timeout the test. |
| AutoLock lock(syncer_thread()->lock_); |
| TimeTicks start = TimeTicks::Now(); |
| TimeDelta ten_seconds = TimeDelta::FromSeconds(10); |
| while (syncer_thread()->vault_.connected_) { |
| syncer_thread()->vault_field_changed_.TimedWait(ten_seconds); |
| if (TimeTicks::Now() - start > ten_seconds) |
| break; |
| } |
| EXPECT_FALSE(syncer_thread()->vault_.connected_); |
| } |
| |
| bool Pause(ListenerMock* listener) { |
| WaitableEvent event(false, false); |
| { |
| AutoLock lock(syncer_thread()->lock_); |
| EXPECT_CALL(*listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_PAUSED))). |
| WillOnce(SignalEvent(&event)); |
| } |
| if (!syncer_thread()->RequestPause()) |
| return false; |
| return event.TimedWait(max_wait_time_); |
| } |
| |
| bool Resume(ListenerMock* listener) { |
| WaitableEvent event(false, false); |
| { |
| AutoLock lock(syncer_thread()->lock_); |
| EXPECT_CALL(*listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_RESUMED))). |
| WillOnce(SignalEvent(&event)); |
| } |
| if (!syncer_thread()->RequestResume()) |
| return false; |
| return event.TimedWait(max_wait_time_); |
| } |
| |
| void PreventThreadFromPolling() { |
| const TimeDelta poll_interval = TimeDelta::FromMinutes(5); |
| syncer_thread()->SetSyncerShortPollInterval(poll_interval); |
| } |
| |
| private: |
| |
| virtual void OnSyncEngineEvent(const SyncEngineEvent& event) { |
| if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) |
| sync_cycle_ended_event_.Signal(); |
| } |
| |
| protected: |
| TimeDelta max_wait_time_; |
| SyncSessionContext* context_; |
| |
| private: |
| ManuallyOpenedTestDirectorySetterUpper metadb_; |
| scoped_ptr<MockConnectionManager> connection_; |
| scoped_refptr<SyncerThread> syncer_thread_; |
| scoped_refptr<ModelSafeWorker> worker_; |
| base::WaitableEvent sync_cycle_ended_event_; |
| DISALLOW_COPY_AND_ASSIGN(SyncerThreadWithSyncerTest); |
| }; |
| |
| class SyncShareIntercept |
| : public MockConnectionManager::ResponseCodeOverrideRequestor, |
| public MockConnectionManager::MidCommitObserver { |
| public: |
| SyncShareIntercept() : sync_occured_(false, false), |
| allow_multiple_interceptions_(true) {} |
| virtual ~SyncShareIntercept() {} |
| virtual void Observe() { |
| if (!allow_multiple_interceptions_ && !times_sync_occured_.empty()) |
| FAIL() << "Multiple sync shares occured."; |
| times_sync_occured_.push_back(TimeTicks::Now()); |
| sync_occured_.Signal(); |
| } |
| |
| // ResponseCodeOverrideRequestor implementation. This assumes any override |
| // requested is intended to silence the SyncerThread. |
| virtual void OnOverrideComplete() { |
| // We should not see any syncing. |
| allow_multiple_interceptions_ = false; |
| times_sync_occured_.clear(); |
| } |
| |
| void WaitForSyncShare(int at_least_this_many, TimeDelta max_wait) { |
| while (at_least_this_many-- > 0) |
| sync_occured_.TimedWait(max_wait); |
| } |
| std::vector<TimeTicks> times_sync_occured() const { |
| return times_sync_occured_; |
| } |
| |
| void Reset() { |
| allow_multiple_interceptions_ = true; |
| times_sync_occured_.clear(); |
| sync_occured_.Reset(); |
| } |
| private: |
| std::vector<TimeTicks> times_sync_occured_; |
| base::WaitableEvent sync_occured_; |
| bool allow_multiple_interceptions_; |
| DISALLOW_COPY_AND_ASSIGN(SyncShareIntercept); |
| }; |
| |
| TEST_F(SyncerThreadTest, Construction) { |
| SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, |
| std::vector<SyncEngineEventListener*>()); |
| scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); |
| } |
| |
| TEST_F(SyncerThreadTest, StartStop) { |
| SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, |
| std::vector<SyncEngineEventListener*>()); |
| scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); |
| EXPECT_TRUE(syncer_thread->Start()); |
| EXPECT_TRUE(syncer_thread->Stop(2000)); |
| |
| // Do it again for good measure. I caught some bugs by adding this so |
| // I would recommend keeping it. |
| EXPECT_TRUE(syncer_thread->Start()); |
| EXPECT_TRUE(syncer_thread->Stop(2000)); |
| } |
| |
| TEST(SyncerThread, GetRecommendedDelay) { |
| EXPECT_LE(0, SyncerThread::GetRecommendedDelaySeconds(0)); |
| EXPECT_LE(1, SyncerThread::GetRecommendedDelaySeconds(1)); |
| EXPECT_LE(50, SyncerThread::GetRecommendedDelaySeconds(50)); |
| EXPECT_LE(10, SyncerThread::GetRecommendedDelaySeconds(10)); |
| EXPECT_EQ(SyncerThread::kMaxBackoffSeconds, |
| SyncerThread::GetRecommendedDelaySeconds( |
| SyncerThread::kMaxBackoffSeconds)); |
| EXPECT_EQ(SyncerThread::kMaxBackoffSeconds, |
| SyncerThread::GetRecommendedDelaySeconds( |
| SyncerThread::kMaxBackoffSeconds+1)); |
| } |
| |
| TEST_F(SyncerThreadTest, CalculateSyncWaitTime) { |
| SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, |
| std::vector<SyncEngineEventListener*>()); |
| scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); |
| syncer_thread->DisableIdleDetection(); |
| |
| // Syncer_polling_interval_ is less than max poll interval. |
| TimeDelta syncer_polling_interval = TimeDelta::FromSeconds(1); |
| |
| syncer_thread->SetSyncerPollingInterval(syncer_polling_interval); |
| |
| // user_idle_ms is less than 10 * (syncer_polling_interval*1000). |
| ASSERT_EQ(syncer_polling_interval.InMilliseconds(), |
| syncer_thread->CalculateSyncWaitTime(1000, 0)); |
| ASSERT_EQ(syncer_polling_interval.InMilliseconds(), |
| syncer_thread->CalculateSyncWaitTime(1000, 1)); |
| |
| // user_idle_ms is ge than 10 * (syncer_polling_interval*1000). |
| int last_poll_time = 2000; |
| ASSERT_TRUE(last_poll_time <= |
| syncer_thread->CalculateSyncWaitTime(last_poll_time, 10000)); |
| ASSERT_TRUE(last_poll_time * 3 >= |
| syncer_thread->CalculateSyncWaitTime(last_poll_time, 10000)); |
| ASSERT_TRUE(last_poll_time <= |
| syncer_thread->CalculateSyncWaitTime(last_poll_time, 100000)); |
| ASSERT_TRUE(last_poll_time * 3 >= |
| syncer_thread->CalculateSyncWaitTime(last_poll_time, 100000)); |
| |
| // Maximum backoff time should be syncer_max_interval. |
| int near_threshold = SyncerThread::kDefaultMaxPollIntervalMs / 2 - 1; |
| int threshold = SyncerThread::kDefaultMaxPollIntervalMs; |
| int over_threshold = SyncerThread::kDefaultMaxPollIntervalMs + 1; |
| ASSERT_TRUE(near_threshold <= |
| syncer_thread->CalculateSyncWaitTime(near_threshold, 10000)); |
| ASSERT_TRUE(SyncerThread::kDefaultMaxPollIntervalMs >= |
| syncer_thread->CalculateSyncWaitTime(near_threshold, 10000)); |
| ASSERT_TRUE(SyncerThread::kDefaultMaxPollIntervalMs == |
| syncer_thread->CalculateSyncWaitTime(threshold, 10000)); |
| ASSERT_TRUE(SyncerThread::kDefaultMaxPollIntervalMs == |
| syncer_thread->CalculateSyncWaitTime(over_threshold, 10000)); |
| |
| // Possible idle time must be capped by syncer_max_interval. |
| int over_sync_max_interval = |
| SyncerThread::kDefaultMaxPollIntervalMs + 1; |
| syncer_polling_interval = TimeDelta::FromSeconds( |
| over_sync_max_interval / 100); // so 1000* is right |
| syncer_thread->SetSyncerPollingInterval(syncer_polling_interval); |
| ASSERT_EQ(syncer_polling_interval.InSeconds() * 1000, |
| syncer_thread->CalculateSyncWaitTime(1000, over_sync_max_interval)); |
| syncer_polling_interval = TimeDelta::FromSeconds(1); |
| syncer_thread->SetSyncerPollingInterval(syncer_polling_interval); |
| ASSERT_TRUE(last_poll_time <= |
| syncer_thread->CalculateSyncWaitTime(last_poll_time, |
| over_sync_max_interval)); |
| ASSERT_TRUE(last_poll_time * 3 >= |
| syncer_thread->CalculateSyncWaitTime(last_poll_time, |
| over_sync_max_interval)); |
| } |
| |
| TEST_F(SyncerThreadTest, CalculatePollingWaitTime) { |
| // Set up the environment. |
| int user_idle_milliseconds_param = 0; |
| SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL, |
| std::vector<SyncEngineEventListener*>()); |
| scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context)); |
| syncer_thread->DisableIdleDetection(); |
| // Hold the lock to appease asserts in code. |
| AutoLock lock(syncer_thread->lock_); |
| |
| // Notifications disabled should result in a polling interval of |
| // kDefaultShortPollInterval. |
| { |
| context->set_notifications_enabled(false); |
| bool continue_sync_cycle_param = false; |
| |
| // No work and no backoff. |
| WaitInterval interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_EQ(SyncerThread::kDefaultShortPollIntervalSeconds, |
| interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_FALSE(continue_sync_cycle_param); |
| |
| // In this case the continue_sync_cycle is turned off. |
| continue_sync_cycle_param = true; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_EQ(SyncerThread::kDefaultShortPollIntervalSeconds, |
| interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_FALSE(continue_sync_cycle_param); |
| } |
| |
| // Notifications enabled should result in a polling interval of |
| // SyncerThread::kDefaultLongPollIntervalSeconds. |
| { |
| context->set_notifications_enabled(true); |
| bool continue_sync_cycle_param = false; |
| |
| // No work and no backoff. |
| WaitInterval interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds, |
| interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_FALSE(continue_sync_cycle_param); |
| |
| // In this case the continue_sync_cycle is turned off. |
| continue_sync_cycle_param = true; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds, |
| interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_FALSE(continue_sync_cycle_param); |
| } |
| |
| // There are two states which can cause a continuation, either the updates |
| // available do not match the updates received, or the unsynced count is |
| // non-zero. |
| { |
| // More server changes remaining to download. |
| context->set_last_snapshot(SessionSnapshotForTest(1, 0, 0)); |
| bool continue_sync_cycle_param = false; |
| |
| WaitInterval interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_LE(0, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| continue_sync_cycle_param = false; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_GE(3, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_LE(0, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_GE(2, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| // Now simulate no more server changes remaining. |
| context->set_last_snapshot(SessionSnapshotForTest(1, 1, 0)); |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds, |
| interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_FALSE(continue_sync_cycle_param); |
| } |
| |
| { |
| |
| // Now try with unsynced local items. |
| context->set_last_snapshot(SessionSnapshotForTest(0, 0, 1)); |
| bool continue_sync_cycle_param = false; |
| |
| WaitInterval interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_LE(0, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| continue_sync_cycle_param = false; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 0, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_GE(2, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| context->set_last_snapshot(SessionSnapshotForTest(0, 0, 0)); |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 4, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds, |
| interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_FALSE(continue_sync_cycle_param); |
| } |
| |
| // Regression for exponential backoff reset when the syncer is nudged. |
| { |
| |
| context->set_last_snapshot(SessionSnapshotForTest(0, 0, 1)); |
| bool continue_sync_cycle_param = false; |
| |
| // Expect move from default polling interval to exponential backoff due to |
| // unsynced_count != 0. |
| WaitInterval interval = syncer_thread->CalculatePollingWaitTime( |
| 3600, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_LE(0, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| continue_sync_cycle_param = false; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 3600, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_GE(2, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| // Expect exponential backoff. |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 2, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_LE(2, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 2, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_GE(6, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| syncer_thread->vault_.current_wait_interval_ = interval; |
| |
| interval = syncer_thread->CalculatePollingWaitTime( |
| static_cast<int>(interval.poll_delta.InSeconds()), |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| true); |
| |
| // Don't change poll on a failed nudge during backoff. |
| ASSERT_TRUE(syncer_thread->vault_.current_wait_interval_.poll_delta == |
| interval.poll_delta); |
| ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode); |
| ASSERT_TRUE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| // If we got a nudge and we weren't in backoff mode, we see exponential |
| // backoff. |
| syncer_thread->vault_.current_wait_interval_.mode = WaitInterval::NORMAL; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 2, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| true); |
| |
| // 5 and 3 are bounds on the backoff randomization formula given input of 2. |
| ASSERT_GE(5, interval.poll_delta.InSeconds()); |
| ASSERT_LE(3, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| // And if another interval expires, we get a bigger backoff. |
| WaitInterval new_interval = syncer_thread->CalculatePollingWaitTime( |
| static_cast<int>(interval.poll_delta.InSeconds()), |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| false); |
| |
| ASSERT_GE(12, new_interval.poll_delta.InSeconds()); |
| ASSERT_LE(5, new_interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode); |
| ASSERT_FALSE(new_interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| // A nudge resets the continue_sync_cycle_param value, so our backoff |
| // should return to the minimum. |
| continue_sync_cycle_param = false; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 3600, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| true); |
| |
| ASSERT_LE(0, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| continue_sync_cycle_param = false; |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 3600, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| true); |
| |
| ASSERT_GE(2, interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_TRUE(continue_sync_cycle_param); |
| |
| // Setting unsynced_count = 0 returns us to the default polling interval. |
| context->set_last_snapshot(SessionSnapshotForTest(0, 0, 0)); |
| interval = syncer_thread->CalculatePollingWaitTime( |
| 4, |
| &user_idle_milliseconds_param, |
| &continue_sync_cycle_param, |
| true); |
| |
| ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds, |
| interval.poll_delta.InSeconds()); |
| ASSERT_EQ(WaitInterval::NORMAL, interval.mode); |
| ASSERT_FALSE(interval.had_nudge_during_backoff); |
| ASSERT_FALSE(continue_sync_cycle_param); |
| } |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, Polling) { |
| SyncShareIntercept interceptor; |
| connection()->SetMidCommitObserver(&interceptor); |
| |
| const TimeDelta poll_interval = TimeDelta::FromSeconds(1); |
| syncer_thread()->SetSyncerShortPollInterval(poll_interval); |
| EXPECT_TRUE(syncer_thread()->Start()); |
| |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| |
| TimeDelta two_polls = poll_interval + poll_interval; |
| // We could theoretically return immediately from the wait if the interceptor |
| // was already signaled for a SyncShare (the first one comes quick). |
| interceptor.WaitForSyncShare(1, two_polls); |
| EXPECT_FALSE(interceptor.times_sync_occured().empty()); |
| |
| // Wait for at least 2 more SyncShare operations. |
| interceptor.WaitForSyncShare(2, two_polls); |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| |
| // Now analyze the run. |
| std::vector<TimeTicks> data = interceptor.times_sync_occured(); |
| |
| EXPECT_GE(data.size(), static_cast<unsigned int>(3)); |
| for (unsigned int i = 0; i < data.size() - 1; i++) { |
| TimeTicks optimal_next_sync = data[i] + poll_interval; |
| EXPECT_TRUE(data[i + 1] >= optimal_next_sync); |
| // This should be reliable, as there are no blocking or I/O operations |
| // except the explicit 2 second wait, so if it takes longer than this |
| // there is a problem. |
| EXPECT_TRUE(data[i + 1] < optimal_next_sync + poll_interval); |
| } |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, Nudge) { |
| SyncShareIntercept interceptor; |
| connection()->SetMidCommitObserver(&interceptor); |
| // We don't want a poll to happen during this test (except the first one). |
| PreventThreadFromPolling(); |
| EXPECT_TRUE(syncer_thread()->Start()); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| const TimeDelta poll_interval = TimeDelta::FromMinutes(5); |
| interceptor.WaitForSyncShare(1, poll_interval + poll_interval); |
| |
| EXPECT_EQ(static_cast<unsigned int>(1), |
| interceptor.times_sync_occured().size()); |
| // The SyncerThread should be waiting for the poll now. Nudge it to sync |
| // immediately (5ms). |
| syncer_thread()->NudgeSyncer(5, SyncerThread::kUnknown); |
| interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1)); |
| EXPECT_EQ(static_cast<unsigned int>(2), |
| interceptor.times_sync_occured().size()); |
| |
| // SyncerThread should be waiting again. Signal it to stop. |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, NudgeWithDataTypes) { |
| SyncShareIntercept interceptor; |
| connection()->SetMidCommitObserver(&interceptor); |
| // We don't want a poll to happen during this test (except the first one). |
| PreventThreadFromPolling(); |
| EXPECT_TRUE(syncer_thread()->Start()); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| const TimeDelta poll_interval = TimeDelta::FromMinutes(5); |
| interceptor.WaitForSyncShare(1, poll_interval + poll_interval); |
| EXPECT_EQ(static_cast<unsigned int>(1), |
| interceptor.times_sync_occured().size()); |
| |
| // The SyncerThread should be waiting for the poll now. Nudge it to sync |
| // immediately (5ms). |
| syncable::ModelTypeBitSet model_types; |
| model_types[syncable::BOOKMARKS] = true; |
| |
| // Paused so we can verify the nudge types safely. |
| syncer_thread()->RequestPause(); |
| syncer_thread()->NudgeSyncerWithDataTypes(5, |
| SyncerThread::kUnknown, |
| model_types); |
| EXPECT_EQ(model_types, syncer_thread()->vault_.pending_nudge_types_); |
| syncer_thread()->RequestResume(); |
| |
| interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1)); |
| EXPECT_EQ(static_cast<unsigned int>(2), |
| interceptor.times_sync_occured().size()); |
| |
| // SyncerThread should be waiting again. Signal it to stop. |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| EXPECT_TRUE(syncer_thread()->vault_.pending_nudge_types_.none()); |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, NudgeWithDataTypesCoalesced) { |
| SyncShareIntercept interceptor; |
| connection()->SetMidCommitObserver(&interceptor); |
| // We don't want a poll to happen during this test (except the first one). |
| PreventThreadFromPolling(); |
| EXPECT_TRUE(syncer_thread()->Start()); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| const TimeDelta poll_interval = TimeDelta::FromMinutes(5); |
| interceptor.WaitForSyncShare(1, poll_interval + poll_interval); |
| EXPECT_EQ(static_cast<unsigned int>(1), |
| interceptor.times_sync_occured().size()); |
| |
| // The SyncerThread should be waiting for the poll now. Nudge it to sync |
| // immediately (5ms). |
| syncable::ModelTypeBitSet model_types; |
| model_types[syncable::BOOKMARKS] = true; |
| |
| // Paused so we can verify the nudge types safely. |
| syncer_thread()->RequestPause(); |
| syncer_thread()->NudgeSyncerWithDataTypes(100, |
| SyncerThread::kUnknown, |
| model_types); |
| EXPECT_EQ(model_types, syncer_thread()->vault_.pending_nudge_types_); |
| |
| model_types[syncable::BOOKMARKS] = false; |
| model_types[syncable::AUTOFILL] = true; |
| syncer_thread()->NudgeSyncerWithDataTypes(0, |
| SyncerThread::kUnknown, |
| model_types); |
| |
| // Reset BOOKMARKS for expectations. |
| model_types[syncable::BOOKMARKS] = true; |
| EXPECT_EQ(model_types, syncer_thread()->vault_.pending_nudge_types_); |
| |
| syncer_thread()->RequestResume(); |
| |
| interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1)); |
| EXPECT_EQ(static_cast<unsigned int>(2), |
| interceptor.times_sync_occured().size()); |
| |
| // SyncerThread should be waiting again. Signal it to stop. |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| EXPECT_TRUE(syncer_thread()->vault_.pending_nudge_types_.none()); |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, Throttling) { |
| SyncShareIntercept interceptor; |
| connection()->SetMidCommitObserver(&interceptor); |
| const TimeDelta poll_interval = TimeDelta::FromMilliseconds(10); |
| syncer_thread()->SetSyncerShortPollInterval(poll_interval); |
| |
| EXPECT_TRUE(syncer_thread()->Start()); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| |
| // Wait for some healthy syncing. |
| interceptor.WaitForSyncShare(4, poll_interval + poll_interval); |
| |
| // Tell the server to throttle a single request, which should be all it takes |
| // to silence our syncer (for 2 hours, so we shouldn't hit that in this test). |
| // This will atomically visit the interceptor so it can switch to throttled |
| // mode and fail on multiple requests. |
| connection()->ThrottleNextRequest(&interceptor); |
| |
| // Try to trigger a sync (we have a really short poll interval already). |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| |
| // Wait until the syncer thread reports that it is throttled. Any further |
| // sync share interceptions will result in failure. If things are broken, |
| // we may never halt. |
| ASSERT_TRUE(WaitForThrottle()); |
| EXPECT_TRUE(syncer_thread()->IsSyncingCurrentlySilenced()); |
| |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, StopSyncPermanently) { |
| // The SyncerThread should request an exit from the Syncer and set |
| // conditions for termination. |
| const TimeDelta poll_interval = TimeDelta::FromMilliseconds(10); |
| syncer_thread()->SetSyncerShortPollInterval(poll_interval); |
| |
| ListenerMock listener; |
| WaitableEvent sync_cycle_ended_event(false, false); |
| WaitableEvent syncer_thread_exiting_event(false, false); |
| TestScopedSessionEventListener reg(context_, &listener); |
| |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::STATUS_CHANGED))). |
| Times(AnyNumber()); |
| |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| Times(AnyNumber()). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::STOP_SYNCING_PERMANENTLY))); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_EXITING))). |
| WillOnce(SignalEvent(&syncer_thread_exiting_event)); |
| |
| EXPECT_TRUE(syncer_thread()->Start()); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| |
| connection()->set_store_birthday("NotYourLuckyDay"); |
| ASSERT_TRUE(syncer_thread_exiting_event.TimedWait(max_wait_time_)); |
| EXPECT_TRUE(syncer_thread()->Stop(0)); |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, AuthInvalid) { |
| SyncShareIntercept interceptor; |
| connection()->SetMidCommitObserver(&interceptor); |
| const TimeDelta poll_interval = TimeDelta::FromMilliseconds(1); |
| |
| syncer_thread()->SetSyncerShortPollInterval(poll_interval); |
| EXPECT_TRUE(syncer_thread()->Start()); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| |
| // Wait for some healthy syncing. |
| interceptor.WaitForSyncShare(2, TimeDelta::FromSeconds(10)); |
| EXPECT_GE(interceptor.times_sync_occured().size(), 2U); |
| |
| // Atomically start returning auth invalid and set the interceptor to fail |
| // on any sync. |
| connection()->FailWithAuthInvalid(&interceptor); |
| WaitForDisconnect(); |
| |
| // Try to trigger a sync (the interceptor will assert if one occurs). |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| |
| // Wait several poll intervals but don't expect any syncing besides the cycle |
| // that lost the connection. |
| interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1)); |
| EXPECT_EQ(1U, interceptor.times_sync_occured().size()); |
| |
| // Simulate a valid re-authentication and expect resumption of syncing. |
| interceptor.Reset(); |
| ASSERT_TRUE(interceptor.times_sync_occured().empty()); |
| connection()->StopFailingWithAuthInvalid(NULL); |
| ServerConnectionEvent e = {ServerConnectionEvent::STATUS_CHANGED, |
| HttpResponse::SERVER_CONNECTION_OK, |
| true}; |
| connection()->channel()->NotifyListeners(e); |
| |
| interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(10)); |
| EXPECT_FALSE(interceptor.times_sync_occured().empty()); |
| |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| } |
| |
| // TODO(zea): Disabled, along with PauseWhenNotConnected, due to stalling on |
| // windows, preventing further sync unit tests from running. See crbug/39070. |
| TEST_F(SyncerThreadWithSyncerTest, DISABLED_Pause) { |
| WaitableEvent sync_cycle_ended_event(false, false); |
| WaitableEvent paused_event(false, false); |
| WaitableEvent resumed_event(false, false); |
| PreventThreadFromPolling(); |
| |
| ListenerMock listener; |
| TestScopedSessionEventListener reg(context_, &listener); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::STATUS_CHANGED))). |
| Times(AnyNumber()); |
| |
| // Wait for the initial sync to complete. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_EXITING))); |
| |
| ASSERT_TRUE(syncer_thread()->Start()); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| |
| // Request a pause. |
| ASSERT_TRUE(Pause(&listener)); |
| |
| // Resuming the pause. |
| ASSERT_TRUE(Resume(&listener)); |
| |
| // Not paused, should fail. |
| EXPECT_FALSE(syncer_thread()->RequestResume()); |
| |
| // Request a pause. |
| ASSERT_TRUE(Pause(&listener)); |
| |
| // Nudge the syncer, this should do nothing while we are paused. |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| |
| // Resuming will cause the nudge to be processed and a sync cycle to run. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| ASSERT_TRUE(Resume(&listener)); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, StartWhenNotConnected) { |
| WaitableEvent sync_cycle_ended_event(false, false); |
| WaitableEvent event(false, false); |
| ListenerMock listener; |
| TestScopedSessionEventListener reg(context_, &listener); |
| PreventThreadFromPolling(); |
| |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::STATUS_CHANGED))). |
| Times(AnyNumber()); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_EXITING))); |
| |
| connection()->SetServerNotReachable(); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| |
| // Syncer thread will always go through once cycle at the start, |
| // then it will wait for a connection. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION))). |
| WillOnce(SignalEvent(&event)); |
| ASSERT_TRUE(syncer_thread()->Start()); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| ASSERT_TRUE(event.TimedWait(max_wait_time_)); |
| |
| // Connect, will put the syncer thread into its usually poll wait. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_CONNECTED))). |
| WillOnce(SignalEvent(&event)); |
| connection()->SetServerReachable(); |
| ASSERT_TRUE(event.TimedWait(max_wait_time_)); |
| |
| // Nudge the syncer to complete a cycle. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| } |
| |
| // See TODO comment on the "Pause" test above. |
| TEST_F(SyncerThreadWithSyncerTest, DISABLED_PauseWhenNotConnected) { |
| WaitableEvent sync_cycle_ended_event(false, false); |
| WaitableEvent event(false, false); |
| ListenerMock listener; |
| TestScopedSessionEventListener reg(context_, &listener); |
| PreventThreadFromPolling(); |
| |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::STATUS_CHANGED))). |
| Times(AnyNumber()); |
| |
| // Put the thread into a "waiting for connection" state. |
| connection()->SetServerNotReachable(); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION))). |
| WillOnce(SignalEvent(&event)); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| |
| ASSERT_TRUE(syncer_thread()->Start()); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| ASSERT_TRUE(event.TimedWait(max_wait_time_)); |
| |
| // Pause and resume the thread while waiting for a connection. |
| ASSERT_TRUE(Pause(&listener)); |
| ASSERT_TRUE(Resume(&listener)); |
| |
| // Make a connection and let the syncer cycle. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_CONNECTED))). |
| WillOnce(SignalEvent(&event)); |
| connection()->SetServerReachable(); |
| ASSERT_TRUE(event.TimedWait(max_wait_time_)); |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| |
| // Disconnect and get into the waiting for a connection state. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION))). |
| WillOnce(SignalEvent(&event)); |
| connection()->SetServerNotReachable(); |
| ASSERT_TRUE(event.TimedWait(max_wait_time_)); |
| |
| // Pause so we can test getting a connection while paused. |
| ASSERT_TRUE(Pause(&listener)); |
| |
| // Get a connection then resume. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_CONNECTED))). |
| WillOnce(SignalEvent(&event)); |
| connection()->SetServerReachable(); |
| ASSERT_TRUE(event.TimedWait(max_wait_time_)); |
| |
| ASSERT_TRUE(Resume(&listener)); |
| |
| // Cycle the syncer to show we are not longer paused. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_EXITING))); |
| |
| syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| } |
| |
| TEST_F(SyncerThreadWithSyncerTest, PauseResumeWhenNotRunning) { |
| WaitableEvent sync_cycle_ended_event(false, false); |
| WaitableEvent event(false, false); |
| ListenerMock listener; |
| TestScopedSessionEventListener reg(context_, &listener); |
| PreventThreadFromPolling(); |
| |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::STATUS_CHANGED))). |
| Times(AnyNumber()); |
| |
| // Pause and resume the syncer while not running |
| ASSERT_TRUE(Pause(&listener)); |
| ASSERT_TRUE(Resume(&listener)); |
| |
| // Pause the thread then start the syncer. |
| ASSERT_TRUE(Pause(&listener)); |
| metadb()->Open(); |
| syncer_thread()->CreateSyncer(metadb()->name()); |
| ASSERT_TRUE(syncer_thread()->Start()); |
| |
| // Resume and let the syncer cycle. |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNC_CYCLE_ENDED))). |
| WillOnce(SignalEvent(&sync_cycle_ended_event)); |
| EXPECT_CALL(listener, OnSyncEngineEvent( |
| Field(&SyncEngineEvent::what_happened, |
| SyncEngineEvent::SYNCER_THREAD_EXITING))); |
| |
| ASSERT_TRUE(Resume(&listener)); |
| ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); |
| EXPECT_TRUE(syncer_thread()->Stop(2000)); |
| } |
| |
| } // namespace browser_sync |