summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/sync/engine/all_status.cc14
-rw-r--r--chrome/browser/sync/engine/syncapi.cc5
-rw-r--r--chrome/browser/sync/engine/syncapi.h6
-rw-r--r--chrome/browser/sync/engine/syncer_proto_util.cc7
-rw-r--r--chrome/browser/sync/engine/syncer_thread.cc27
-rw-r--r--chrome/browser/sync/engine/syncer_thread.h7
-rw-r--r--chrome/browser/sync/engine/syncer_thread_unittest.cc48
-rw-r--r--chrome/browser/sync/engine/syncer_types.h9
-rw-r--r--chrome/browser/sync/engine/syncer_unittest.cc2
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.cc9
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h7
-rw-r--r--chrome/browser/sync/profile_sync_service.cc14
-rw-r--r--chrome/browser/sync/profile_sync_service.h1
-rw-r--r--chrome/browser/sync/sessions/sync_session.h6
-rw-r--r--chrome/browser/sync/sessions/sync_session_unittest.cc3
-rw-r--r--chrome/test/sync/engine/mock_connection_manager.h4
-rw-r--r--chrome/test/sync/engine/syncer_command_test.h3
17 files changed, 147 insertions, 25 deletions
diff --git a/chrome/browser/sync/engine/all_status.cc b/chrome/browser/sync/engine/all_status.cc
index 6bf37d7..6c48905 100644
--- a/chrome/browser/sync/engine/all_status.cc
+++ b/chrome/browser/sync/engine/all_status.cc
@@ -207,20 +207,14 @@ void AllStatus::HandleChannelEvent(const SyncerEvent& event) {
lock.NotifyOverQuota();
break;
case SyncerEvent::REQUEST_SYNC_NUDGE:
- lock.set_notify_plan(DONT_NOTIFY);
- break;
case SyncerEvent::PAUSED:
- lock.set_notify_plan(DONT_NOTIFY);
- break;
case SyncerEvent::RESUMED:
- lock.set_notify_plan(DONT_NOTIFY);
- break;
case SyncerEvent::WAITING_FOR_CONNECTION:
- lock.set_notify_plan(DONT_NOTIFY);
- break;
case SyncerEvent::CONNECTED:
- lock.set_notify_plan(DONT_NOTIFY);
- break;
+ case SyncerEvent::STOP_SYNCING_PERMANENTLY:
+ case SyncerEvent::SYNCER_THREAD_EXITING:
+ lock.set_notify_plan(DONT_NOTIFY);
+ break;
default:
LOG(ERROR) << "Unrecognized Syncer Event: " << event.what_happened;
lock.set_notify_plan(DONT_NOTIFY);
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index e17d652..390d9f5 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -1869,6 +1869,11 @@ void SyncManager::SyncInternal::HandleChannelEvent(const SyncerEvent& event) {
observer_->OnResumed();
return;
}
+
+ if (event.what_happened == SyncerEvent::STOP_SYNCING_PERMANENTLY) {
+ observer_->OnStopSyncingPermanently();
+ return;
+ }
}
void SyncManager::SyncInternal::HandleAuthWatcherEvent(
diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h
index 7f84abe..4504364 100644
--- a/chrome/browser/sync/engine/syncapi.h
+++ b/chrome/browser/sync/engine/syncapi.h
@@ -662,6 +662,12 @@ class SyncManager {
// The syncer thread has been resumed.
virtual void OnResumed() = 0;
+ // We are no longer permitted to communicate with the server. Sync should
+ // be disabled and state cleaned up at once. This can happen for a number
+ // of reasons, e.g. swapping from a test instance to production, or a
+ // global stop syncing operation has wiped the store.
+ virtual void OnStopSyncingPermanently() = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(Observer);
};
diff --git a/chrome/browser/sync/engine/syncer_proto_util.cc b/chrome/browser/sync/engine/syncer_proto_util.cc
index 76e163a..ec95bc5 100644
--- a/chrome/browser/sync/engine/syncer_proto_util.cc
+++ b/chrome/browser/sync/engine/syncer_proto_util.cc
@@ -83,6 +83,9 @@ bool SyncerProtoUtil::VerifyResponseBirthday(syncable::Directory* dir,
std::string local_birthday = dir->store_birthday();
+ // TODO(tim): Bug 46807. Check for new response code denoting a clear store
+ // operation is in progress.
+
if (local_birthday.empty()) {
if (!response->has_store_birthday()) {
LOG(WARNING) << "Expected a birthday on first sync.";
@@ -193,6 +196,7 @@ bool SyncerProtoUtil::PostClientToServerMessage(
// TODO(ncarter): Add a unit test for the case where the syncer becomes
// stuck due to a bad birthday.
session->status_controller()->set_syncer_stuck(true);
+ session->delegate()->OnShouldStopSyncingPermanently();
return false;
}
@@ -200,9 +204,6 @@ bool SyncerProtoUtil::PostClientToServerMessage(
case ClientToServerResponse::SUCCESS:
LogResponseProfilingData(*response);
return true;
- case ClientToServerResponse::NOT_MY_BIRTHDAY:
- LOG(WARNING) << "Server thought we had wrong birthday.";
- return false;
case ClientToServerResponse::THROTTLED:
LOG(WARNING) << "Client silenced by server.";
session->delegate()->OnSilencedUntil(base::TimeTicks::Now() +
diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc
index 1437dfd..ffc7bcd 100644
--- a/chrome/browser/sync/engine/syncer_thread.cc
+++ b/chrome/browser/sync/engine/syncer_thread.cc
@@ -128,6 +128,14 @@ bool SyncerThread::Start() {
// Stop processing. A max wait of at least 2*server RTT time is recommended.
// Returns true if we stopped, false otherwise.
bool SyncerThread::Stop(int max_wait) {
+ RequestSyncerExitAndSetThreadStopConditions();
+
+ // This will join, and finish when ThreadMain terminates.
+ thread_.Stop();
+ return true;
+}
+
+void SyncerThread::RequestSyncerExitAndSetThreadStopConditions() {
{
AutoLock lock(lock_);
// If the thread has been started, then we either already have or are about
@@ -135,7 +143,7 @@ bool SyncerThread::Stop(int max_wait) {
// it to finish. If the thread has not been started --and we now own the
// lock-- then we can early out because the caller has not called Start().
if (!thread_.IsRunning())
- return true;
+ return;
LOG(INFO) << "SyncerThread::Stop - setting ThreadMain exit condition to "
<< "true (vault_.stop_syncer_thread_)";
@@ -155,10 +163,6 @@ bool SyncerThread::Stop(int max_wait) {
// end of ThreadMain.
vault_field_changed_.Broadcast();
}
-
- // This will join, and finish when ThreadMain terminates.
- thread_.Stop();
- return true;
}
bool SyncerThread::RequestPause() {
@@ -233,6 +237,13 @@ bool SyncerThread::IsSyncingCurrentlySilenced() {
return ret;
}
+void SyncerThread::OnShouldStopSyncingPermanently() {
+ RequestSyncerExitAndSetThreadStopConditions();
+
+ SyncerEvent event(SyncerEvent::STOP_SYNCING_PERMANENTLY);
+ relay_channel()->Notify(event);
+}
+
void SyncerThread::ThreadMainLoop() {
// This is called with lock_ acquired.
lock_.AssertAcquired();
@@ -386,14 +397,18 @@ void SyncerThread::PauseUntilResumedOrQuit() {
}
void SyncerThread::EnterPausedState() {
+ lock_.AssertAcquired();
vault_.pause_requested_ = false;
vault_.paused_ = true;
+ vault_field_changed_.Broadcast();
SyncerEvent event(SyncerEvent::PAUSED);
relay_channel()->Notify(event);
}
void SyncerThread::ExitPausedState() {
+ lock_.AssertAcquired();
vault_.paused_ = false;
+ vault_field_changed_.Broadcast();
SyncerEvent event(SyncerEvent::RESUMED);
relay_channel()->Notify(event);
}
@@ -488,6 +503,8 @@ void SyncerThread::ThreadMain() {
thread_main_started_.Signal();
ThreadMainLoop();
LOG(INFO) << "Syncer thread ThreadMain is done.";
+ SyncerEvent event(SyncerEvent::SYNCER_THREAD_EXITING);
+ relay_channel()->Notify(event);
}
void SyncerThread::SyncMain(Syncer* syncer) {
diff --git a/chrome/browser/sync/engine/syncer_thread.h b/chrome/browser/sync/engine/syncer_thread.h
index 29a2c13..a54ff7f 100644
--- a/chrome/browser/sync/engine/syncer_thread.h
+++ b/chrome/browser/sync/engine/syncer_thread.h
@@ -57,6 +57,7 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>,
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Pause);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StartWhenNotConnected);
FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, PauseWhenNotConnected);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StopSyncPermanently);
friend class SyncerThreadWithSyncerTest;
friend class SyncerThreadFactory;
public:
@@ -236,6 +237,7 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>,
const base::TimeDelta& new_interval);
virtual void OnReceivedLongPollIntervalUpdate(
const base::TimeDelta& new_interval);
+ virtual void OnShouldStopSyncingPermanently();
void HandleServerConnectionEvent(const ServerConnectionEvent& event);
@@ -288,6 +290,11 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>,
// For unit tests only.
virtual void DisableIdleDetection() { disable_idle_detection_ = true; }
+ // This sets all conditions for syncer thread termination but does not
+ // actually join threads. It is expected that Stop will be called at some
+ // time after to fully stop and clean up.
+ void RequestSyncerExitAndSetThreadStopConditions();
+
// State of the notification framework is tracked by these values.
bool p2p_authenticated_;
bool p2p_subscribed_;
diff --git a/chrome/browser/sync/engine/syncer_thread_unittest.cc b/chrome/browser/sync/engine/syncer_thread_unittest.cc
index 79e2665..a9798ad 100644
--- a/chrome/browser/sync/engine/syncer_thread_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_thread_unittest.cc
@@ -716,6 +716,43 @@ TEST_F(SyncerThreadWithSyncerTest, Throttling) {
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);
+ scoped_ptr<ChannelHookup<SyncerEvent> > hookup;
+ hookup.reset(syncer_thread()->relay_channel()->AddObserver(&listener));
+
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened, SyncerEvent::STATUS_CHANGED))).
+ Times(AnyNumber());
+
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))).
+ Times(AnyNumber()).
+ WillOnce(SignalEvent(&sync_cycle_ended_event));
+
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened,
+ SyncerEvent::STOP_SYNCING_PERMANENTLY)));
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING))).
+ WillOnce(SignalEvent(&syncer_thread_exiting_event));
+
+ EXPECT_TRUE(syncer_thread()->Start());
+ metadb()->Open();
+ 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);
@@ -789,6 +826,8 @@ TEST_F(SyncerThreadWithSyncerTest, MAYBE_Pause) {
EXPECT_CALL(listener, HandleChannelEvent(
Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))).
WillOnce(SignalEvent(&sync_cycle_ended_event));
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING)));
ASSERT_TRUE(syncer_thread()->Start());
metadb()->Open();
ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
@@ -829,6 +868,8 @@ TEST_F(SyncerThreadWithSyncerTest, StartWhenNotConnected) {
EXPECT_CALL(listener, HandleChannelEvent(
Field(&SyncerEvent::what_happened, SyncerEvent::STATUS_CHANGED))).
Times(AnyNumber());
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING)));
connection()->SetServerNotReachable();
metadb()->Open();
@@ -928,6 +969,9 @@ TEST_F(SyncerThreadWithSyncerTest, FLAKY_PauseWhenNotConnected) {
EXPECT_CALL(listener, HandleChannelEvent(
Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))).
WillOnce(SignalEvent(&sync_cycle_ended_event));
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING)));
+
syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
@@ -959,9 +1003,11 @@ TEST_F(SyncerThreadWithSyncerTest, PauseResumeWhenNotRunning) {
EXPECT_CALL(listener, HandleChannelEvent(
Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))).
WillOnce(SignalEvent(&sync_cycle_ended_event));
+ EXPECT_CALL(listener, HandleChannelEvent(
+ Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING)));
+
ASSERT_TRUE(Resume(&listener));
ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
-
EXPECT_TRUE(syncer_thread()->Stop(2000));
}
diff --git a/chrome/browser/sync/engine/syncer_types.h b/chrome/browser/sync/engine/syncer_types.h
index 5a3f68a..734f823 100644
--- a/chrome/browser/sync/engine/syncer_types.h
+++ b/chrome/browser/sync/engine/syncer_types.h
@@ -107,6 +107,15 @@ struct SyncerEvent {
// This event is sent when a connection has been established and
// the thread continues.
CONNECTED,
+
+ // This is sent after the Syncer (and SyncerThread) have initiated self
+ // halt due to no longer being permitted to communicate with the server.
+ // The listener should sever the sync / browser connections and delete sync
+ // data (i.e. as if the user clicked 'Stop Syncing' in the browser.
+ STOP_SYNCING_PERMANENTLY,
+
+ // Sent when the main syncer loop finishes.
+ SYNCER_THREAD_EXITING,
};
explicit SyncerEvent(EventCause cause) : what_happened(cause),
diff --git a/chrome/browser/sync/engine/syncer_unittest.cc b/chrome/browser/sync/engine/syncer_unittest.cc
index f0a8cb0..f2077e3 100644
--- a/chrome/browser/sync/engine/syncer_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_unittest.cc
@@ -115,6 +115,8 @@ class SyncerTest : public testing::Test,
const base::TimeDelta& new_interval) {
last_short_poll_interval_received_ = new_interval;
}
+ virtual void OnShouldStopSyncingPermanently() {
+ }
// ModelSafeWorkerRegistrar implementation.
virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 15d78cf..73fee4d 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -592,6 +592,15 @@ void SyncBackendHost::Core::OnResumed() {
NewRunnableMethod(this, &Core::NotifyResumed));
}
+void SyncBackendHost::Core::OnStopSyncingPermanently() {
+ host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &Core::HandleStopSyncingPermanentlyOnFrontendLoop));
+}
+
+void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() {
+ host_->frontend_->OnStopSyncingPermanently();
+}
+
void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop(
const GoogleServiceAuthError& new_auth_error) {
if (!host_ || !host_->frontend_)
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index a91d877..41a388a 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -58,6 +58,10 @@ class SyncFrontend {
// credentials to be provided. See SyncBackendHost::Authenticate for details.
virtual void OnAuthError() = 0;
+ // We are no longer permitted to communicate with the server. Sync should
+ // be disabled and state cleaned up at once.
+ virtual void OnStopSyncingPermanently() = 0;
+
protected:
// Don't delete through SyncFrontend interface.
virtual ~SyncFrontend() {
@@ -230,6 +234,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
virtual void OnPassphraseAccepted();
virtual void OnPaused();
virtual void OnResumed();
+ virtual void OnStopSyncingPermanently();
struct DoInitializeOptions {
DoInitializeOptions(
@@ -374,6 +379,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
void HandleSyncCycleCompletedOnFrontendLoop(
sessions::SyncSessionSnapshot* snapshot);
+ void HandleStopSyncingPermanentlyOnFrontendLoop();
+
// Called from Core::OnInitializationComplete to handle updating
// frontend thread components.
void HandleInitalizationCompletedOnFrontendLoop();
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 9695b84..055f039 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -322,11 +322,6 @@ void ProfileSyncService::EnableForUser() {
}
void ProfileSyncService::DisableForUser() {
- if (WizardIsVisible()) {
- // TODO(timsteele): Focus wizard.
- return;
- }
-
LOG(INFO) << "Clearing Sync DB.";
// Clear prefs (including SyncSetupHasCompleted) before shutting down so
@@ -403,7 +398,7 @@ void ProfileSyncService::OnUnrecoverableError(
from_here.Write(true, true, &location);
LOG(ERROR) << location;
- if (WizardIsVisible()) {
+ if (SetupInProgress()) {
// We've hit an error in the middle of a startup process- shutdown all the
// backend stuff, and then restart it, so we're in the same state as before.
MessageLoop::current()->PostTask(FROM_HERE,
@@ -468,6 +463,13 @@ void ProfileSyncService::OnAuthError() {
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}
+void ProfileSyncService::OnStopSyncingPermanently() {
+ if (SetupInProgress())
+ wizard_.Step(SyncSetupWizard::FATAL_ERROR);
+
+ DisableForUser();
+}
+
void ProfileSyncService::ShowLoginDialog() {
if (WizardIsVisible()) {
wizard_.Focus();
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index b995ffa..5b36e0b 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -156,6 +156,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
virtual void OnBackendInitialized();
virtual void OnSyncCycleCompleted();
virtual void OnAuthError();
+ virtual void OnStopSyncingPermanently();
// Called when a user enters credentials through UI.
virtual void OnUserSubmittedAuth(const std::string& username,
diff --git a/chrome/browser/sync/sessions/sync_session.h b/chrome/browser/sync/sessions/sync_session.h
index 6f29227..dd498fe 100644
--- a/chrome/browser/sync/sessions/sync_session.h
+++ b/chrome/browser/sync/sessions/sync_session.h
@@ -65,6 +65,12 @@ class SyncSession {
virtual void OnReceivedLongPollIntervalUpdate(
const base::TimeDelta& new_interval) = 0;
+ // The client needs to cease and desist syncing at once. This occurs when
+ // the Syncer detects that the backend store has fundamentally changed or
+ // is a different instance altogether (e.g. swapping from a test instance
+ // to production, or a global stop syncing operation has wiped the store).
+ virtual void OnShouldStopSyncingPermanently() = 0;
+
protected:
virtual ~Delegate() {}
};
diff --git a/chrome/browser/sync/sessions/sync_session_unittest.cc b/chrome/browser/sync/sessions/sync_session_unittest.cc
index ae85137..2bc4594 100644
--- a/chrome/browser/sync/sessions/sync_session_unittest.cc
+++ b/chrome/browser/sync/sessions/sync_session_unittest.cc
@@ -50,6 +50,9 @@ class SyncSessionTest : public testing::Test,
const base::TimeDelta& new_interval) {
FailControllerInvocationIfDisabled("OnReceivedShortPollIntervalUpdate");
}
+ virtual void OnShouldStopSyncingPermanently() {
+ FailControllerInvocationIfDisabled("OnShouldStopSyncingPermanently");
+ }
// ModelSafeWorkerRegistrar implementation.
virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {}
diff --git a/chrome/test/sync/engine/mock_connection_manager.h b/chrome/test/sync/engine/mock_connection_manager.h
index be2144f..dd2573d 100644
--- a/chrome/test/sync/engine/mock_connection_manager.h
+++ b/chrome/test/sync/engine/mock_connection_manager.h
@@ -177,6 +177,10 @@ class MockConnectionManager : public browser_sync::ServerConnectionManager {
use_legacy_bookmarks_protocol_ = value;
}
+ void set_store_birthday(string new_birthday) {
+ store_birthday_ = new_birthday;
+ }
+
// Retrieve the number of GetUpdates requests that the mock server has
// seen since the last time this function was called. Can be used to
// verify that a GetUpdates actually did or did not happen after running
diff --git a/chrome/test/sync/engine/syncer_command_test.h b/chrome/test/sync/engine/syncer_command_test.h
index 9a3c87c..719a22d 100644
--- a/chrome/test/sync/engine/syncer_command_test.h
+++ b/chrome/test/sync/engine/syncer_command_test.h
@@ -44,6 +44,9 @@ class SyncerCommandTestWithParam : public testing::TestWithParam<T>,
const base::TimeDelta& new_interval) {
FAIL() << "Should not get poll interval update.";
}
+ virtual void OnShouldStopSyncingPermanently() {
+ FAIL() << "Shouldn't be forced to stop syncing.";
+ }
// ModelSafeWorkerRegistrar implementation.
virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {