summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync
diff options
context:
space:
mode:
authorskrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-23 21:47:04 +0000
committerskrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-23 21:47:04 +0000
commitc4c672aea10f13b53c4271b42309dd5a2cdd4cb9 (patch)
tree391dd8fcab139449970ea119e373120b60914569 /chrome/browser/sync
parentf4481633ac082f136453be1926c77d0c079b10c4 (diff)
downloadchromium_src-c4c672aea10f13b53c4271b42309dd5a2cdd4cb9.zip
chromium_src-c4c672aea10f13b53c4271b42309dd5a2cdd4cb9.tar.gz
chromium_src-c4c672aea10f13b53c4271b42309dd5a2cdd4cb9.tar.bz2
This is basically a rewrite of the DataTypeManager to support dynamic data type configuration. DTM::Start() has been replaced with DTM::Configure(). Note that the callback is also gone, replaced with notification service style notifications to tell listeners that configuration has started or finished.
I also added some stuff to prepare for adding the "initial download" step for new datatype into the configure process. Review URL: http://codereview.chromium.org/1128012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42384 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r--chrome/browser/sync/glue/data_type_manager.h68
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl.cc315
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl.h23
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl_unittest.cc372
-rw-r--r--chrome/browser/sync/glue/data_type_manager_mock.h29
-rw-r--r--chrome/browser/sync/profile_sync_service.cc66
-rw-r--r--chrome/browser/sync/profile_sync_service.h24
-rw-r--r--chrome/browser/sync/profile_sync_service_startup_unittest.cc31
8 files changed, 645 insertions, 283 deletions
diff --git a/chrome/browser/sync/glue/data_type_manager.h b/chrome/browser/sync/glue/data_type_manager.h
index 401af56..0a0b1f1 100644
--- a/chrome/browser/sync/glue/data_type_manager.h
+++ b/chrome/browser/sync/glue/data_type_manager.h
@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_SYNC_GLUE_DATA_TYPE_MANAGER_H__
#define CHROME_BROWSER_SYNC_GLUE_DATA_TYPE_MANAGER_H__
+#include <set>
+
#include "base/task.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/syncable/model_type.h"
@@ -16,49 +18,51 @@ namespace browser_sync {
class DataTypeManager {
public:
enum State {
- STOPPED, // No data types are currently running.
- PAUSE_PENDING, // Waiting for the sync backend to pause.
- STARTING, // Data types are being started.
- RESUME_PENDING, // Waiting for the sync backend to resume.
- STARTED, // All enabled data types are running.
- STOPPING // Data types are being stopped.
+ STOPPED, // No data types are currently running.
+ RESTARTING, // Configuration has been changed...
+ DOWNLOAD_PENDING, // Not implemented yet: Waiting for the syncer to
+ // complete the initial download of new data
+ // types.
+ PAUSE_PENDING, // Waiting for the sync backend to pause.
+ CONFIGURING, // Data types are being started.
+ RESUME_PENDING, // Waiting for the sync backend to resume.
+ CONFIGURED, // All enabled data types are running.
+ STOPPING // Data types are being stopped.
};
- enum StartResult {
- OK, // All enabled data types were started normally.
- BUSY, // Start() was called while start is already
- // in progress.
- ASSOCIATION_FAILED, // An error occurred during model association.
- ABORTED, // Start was aborted by calling Stop() before
- // all types were started.
- UNRECOVERABLE_ERROR // A data type experienced an unrecoverable error
- // during startup.
+ enum ConfigureResult {
+ OK, // Configuration finished without error.
+ ASSOCIATION_FAILED, // An error occurred during model association.
+ ABORTED, // Start was aborted by calling Stop() before
+ // all types were started.
+ UNRECOVERABLE_ERROR // A data type experienced an unrecoverable error
+ // during startup.
};
- typedef Callback1<StartResult>::Type StartCallback;
+ typedef std::set<syncable::ModelType> TypeSet;
virtual ~DataTypeManager() {}
- // Begins asynchronous start up of all registered data types. Upon
- // successful completion or failure, the start_callback will be
- // invoked with the corresponding StartResult. This is an all or
- // nothing process -- if any of the registered data types fail to
- // start, the startup process will halt and any data types that have
- // been started will be stopped.
- virtual void Start(StartCallback* start_callback) = 0;
+ // Begins asynchronous configuration of data types. Any currently
+ // running data types that are not in the desired_types set will be
+ // stopped. Any stopped data types that are in the desired_types
+ // set will be started. All other data types are left in their
+ // current state. A SYNC_CONFIGURE_START notification will be sent
+ // to the UI thread when configuration is started and a
+ // SYNC_CONFIGURE_DONE notification will be sent (with a
+ // ConfigurationResult detail) when configuration is complete.
+ //
+ // Note that you may call Configure() while configuration is in
+ // progress. Configuration will be complete only when the
+ // desired_types supplied in the last call to Configure is achieved.
+ virtual void Configure(const TypeSet& desired_types) = 0;
// Synchronously stops all registered data types. If called after
- // Start() is called but before it finishes, it will abort the start
- // and any data types that have been started will be stopped.
+ // Configure() is called but before it finishes, it will abort the
+ // configure and any data types that have been started will be
+ // stopped.
virtual void Stop() = 0;
- // Returns true if the given data type has been registered.
- virtual bool IsRegistered(syncable::ModelType type) = 0;
-
- // Returns true if the given data type has been registered and is
- // enabled.
- virtual bool IsEnabled(syncable::ModelType type) = 0;
-
// Reference to map of data type controllers.
virtual const DataTypeController::TypeMap& controllers() = 0;
diff --git a/chrome/browser/sync/glue/data_type_manager_impl.cc b/chrome/browser/sync/glue/data_type_manager_impl.cc
index 12cd4d2..a166d11 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl.cc
+++ b/chrome/browser/sync/glue/data_type_manager_impl.cc
@@ -2,16 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
+#include <functional>
+
#include "base/logging.h"
#include "base/task.h"
#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/glue/data_type_manager_impl.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/common/notification_details.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_source.h"
-static const int kStartOrderStart = -1;
+namespace browser_sync {
namespace {
@@ -22,9 +26,24 @@ static const syncable::ModelType kStartOrder[] = {
syncable::TYPED_URLS,
};
-} // namespace
+// Comparator used when sorting data type controllers.
+class SortComparator : public std::binary_function<DataTypeController*,
+ DataTypeController*,
+ bool> {
+ public:
+ explicit SortComparator(std::map<syncable::ModelType, int>* order)
+ : order_(order) { }
-namespace browser_sync {
+ // Returns true if lhs preceeds rhs.
+ bool operator() (DataTypeController* lhs, DataTypeController* rhs) {
+ return (*order_)[lhs->type()] < (*order_)[rhs->type()];
+ }
+
+ private:
+ std::map<syncable::ModelType, int>* order_;
+};
+
+} // namespace
DataTypeManagerImpl::DataTypeManagerImpl(
SyncBackendHost* backend,
@@ -32,68 +51,125 @@ DataTypeManagerImpl::DataTypeManagerImpl(
: backend_(backend),
controllers_(controllers),
state_(DataTypeManager::STOPPED),
- current_type_(kStartOrderStart) {
+ current_dtc_(NULL) {
DCHECK(backend_);
- DCHECK(arraysize(kStartOrder) > 0);
+ DCHECK_GT(arraysize(kStartOrder), 0U);
// Ensure all data type controllers are stopped.
for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
it != controllers_.end(); ++it) {
DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state());
}
+
+ // Build a ModelType -> order map for sorting.
+ for (int i = 0; i < static_cast<int>(arraysize(kStartOrder)); i++)
+ start_order_[kStartOrder[i]] = i;
}
-void DataTypeManagerImpl::Start(StartCallback* start_callback) {
+void DataTypeManagerImpl::Configure(const TypeSet& desired_types) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
- if (state_ != STOPPED) {
- start_callback->Run(BUSY);
- delete start_callback;
+ if (state_ == STOPPING) {
+ // You can not set a configuration while stopping.
+ LOG(ERROR) << "Configuration set while stopping.";
return;
}
- state_ = PAUSE_PENDING;
- start_callback_.reset(start_callback);
- current_type_ = kStartOrderStart;
+ // Add any data type controllers into the needs_start_ list that are
+ // currently NOT_RUNNING or STOPPING.
+ needs_start_.clear();
+ for (TypeSet::const_iterator it = desired_types.begin();
+ it != desired_types.end(); ++it) {
+ DataTypeController* dtc = controllers_[*it];
+ if (dtc && (dtc->state() == DataTypeController::NOT_RUNNING ||
+ dtc->state() == DataTypeController::STOPPING)) {
+ needs_start_.push_back(dtc);
+ LOG(INFO) << "Will start " << dtc->name();
+ }
+ }
+ // Sort these according to kStartOrder.
+ std::sort(needs_start_.begin(),
+ needs_start_.end(),
+ SortComparator(&start_order_));
- // Pause the sync backend before starting the data types.
- AddObserver(NotificationType::SYNC_PAUSED);
- if (!backend_->RequestPause()) {
- RemoveObserver(NotificationType::SYNC_PAUSED);
- state_ = STOPPED;
- start_callback_->Run(UNRECOVERABLE_ERROR);
- start_callback_.reset();
+ // Add any data type controllers into that needs_stop_ list that are
+ // currently MODEL_STARTING, ASSOCIATING, or RUNNING.
+ needs_stop_.clear();
+ for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
+ it != controllers_.end(); ++it) {
+ DataTypeController* dtc = (*it).second;
+ if (desired_types.count(dtc->type()) == 0 && (
+ dtc->state() == DataTypeController::MODEL_STARTING ||
+ dtc->state() == DataTypeController::ASSOCIATING ||
+ dtc->state() == DataTypeController::RUNNING)) {
+ needs_stop_.push_back(dtc);
+ LOG(INFO) << "Will stop " << dtc->name();
+ }
}
+ // Sort these according to kStartOrder.
+ std::sort(needs_stop_.begin(),
+ needs_stop_.end(),
+ SortComparator(&start_order_));
+
+ // If nothing changed, we're done.
+ if (needs_start_.size() == 0 && needs_stop_.size() == 0) {
+ state_ = CONFIGURED;
+ NotifyStart();
+ NotifyDone(OK);
+ return;
+ }
+
+ Restart();
+}
+
+void DataTypeManagerImpl::Restart() {
+ LOG(INFO) << "Restarting...";
+ // If we are currently waiting for an asynchronous process to
+ // complete, change our state to RESTARTING so those processes know
+ // that we want to start over when they finish.
+ if (state_ == DOWNLOAD_PENDING || state_ == PAUSE_PENDING ||
+ state_ == CONFIGURING || state_ == RESUME_PENDING) {
+ state_ = RESTARTING;
+ return;
+ }
+
+ DCHECK(state_ == STOPPED || state_ == RESTARTING || state_ == CONFIGURED);
+ current_dtc_ = NULL;
+
+ // Starting from a "steady state" (stopped or configured) state
+ // should send a start notification.
+ if (state_ == STOPPED || state_ == CONFIGURED)
+ NotifyStart();
+
+ // Stop requested data types.
+ for (size_t i = 0; i < needs_stop_.size(); ++i) {
+ LOG(INFO) << "Stopping " << needs_stop_[i]->name();
+ needs_stop_[i]->Stop();
+ }
+ needs_stop_.clear();
+
+ // TODO(sync): Get updates for new data types here.
+
+ // Pause the sync backend before starting the data types.
+ state_ = PAUSE_PENDING;
+ PauseSyncer();
}
void DataTypeManagerImpl::StartNextType() {
- // Ensure that the current type has indeed started.
- DCHECK(current_type_ == kStartOrderStart ||
- controllers_[kStartOrder[current_type_]]->state() ==
- DataTypeController::RUNNING);
-
- // Find the next startable type.
- while (current_type_ < static_cast<int>(arraysize(kStartOrder)) - 1) {
- current_type_++;
- syncable::ModelType type = kStartOrder[current_type_];
- if (IsEnabled(type)) {
- LOG(INFO) << "Starting " << controllers_[type]->name();
- controllers_[type]->Start(
- true,
- NewCallback(this, &DataTypeManagerImpl::TypeStartCallback));
- return;
- }
+ // If there are any data types left to start, start the one at the
+ // front of the list.
+ if (needs_start_.size() > 0) {
+ current_dtc_ = needs_start_[0];
+ LOG(INFO) << "Starting " << current_dtc_->name();
+ current_dtc_->Start(
+ true,
+ NewCallback(this, &DataTypeManagerImpl::TypeStartCallback));
+ return;
}
- // No more startable types found, we must be done. Resume the sync
+ // If no more data types need starting, we're done. Resume the sync
// backend to finish.
- DCHECK_EQ(state_, STARTING);
- AddObserver(NotificationType::SYNC_RESUMED);
+ DCHECK_EQ(state_, CONFIGURING);
state_ = RESUME_PENDING;
- if (!backend_->RequestResume()) {
- RemoveObserver(NotificationType::SYNC_RESUMED);
- FinishStop();
- start_callback_->Run(UNRECOVERABLE_ERROR);
- start_callback_.reset();
- }
+ ResumeSyncer();
}
void DataTypeManagerImpl::TypeStartCallback(
@@ -101,22 +177,43 @@ void DataTypeManagerImpl::TypeStartCallback(
// When the data type controller invokes this callback, it must be
// on the UI thread.
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ DCHECK(current_dtc_);
+
+ // If configuration changed while this data type was starting, we
+ // need to reset. Resume the syncer.
+ if (state_ == RESTARTING) {
+ ResumeSyncer();
+ return;
+ }
- // If we reach this callback while stopping, this means that the
- // current data type was stopped while still starting up. Now that
- // the data type is aborted, we can finish stop.
+ // We're done with the data type at the head of the list -- remove it.
+ DataTypeController* started_dtc = current_dtc_;
+ DCHECK(needs_start_.size());
+ DCHECK_EQ(needs_start_[0], started_dtc);
+ needs_start_.erase(needs_start_.begin());
+ current_dtc_ = NULL;
+
+ // If we reach this callback while stopping, this means that
+ // DataTypeManager::Stop() was called while the current data type
+ // was starting. Now that it has finished starting, we can finish
+ // stopping the DataTypeManager. This is considered an ABORT.
if (state_ == STOPPING) {
FinishStop();
- start_callback_->Run(DataTypeManager::ABORTED);
- start_callback_.reset();
+ NotifyDone(ABORTED);
+ return;
+ }
+
+ // If our state_ is STOPPED, we have already stopped all of the data
+ // types. We should not be getting callbacks from stopped data types.
+ if (state_ == STOPPED) {
+ LOG(ERROR) << "Start callback called by stopped data type!";
return;
}
// If the type started normally, continue to the next type.
- syncable::ModelType type = kStartOrder[current_type_];
if (result == DataTypeController::OK ||
result == DataTypeController::OK_FIRST_RUN) {
- LOG(INFO) << "Started " << controllers_[type]->name();
+ LOG(INFO) << "Started " << started_dtc->name();
StartNextType();
return;
}
@@ -124,25 +221,24 @@ void DataTypeManagerImpl::TypeStartCallback(
// Any other result is a fatal error. Shut down any types we've
// managed to start up to this point and pass the result to the
// callback.
- LOG(INFO) << "Failed " << controllers_[type]->name();
+ LOG(INFO) << "Failed " << started_dtc->name();
FinishStop();
- StartResult start_result = DataTypeManager::ABORTED;
+ ConfigureResult configure_result = DataTypeManager::ABORTED;
switch (result) {
case DataTypeController::ABORTED:
- start_result = DataTypeManager::ABORTED;
+ configure_result = DataTypeManager::ABORTED;
break;
case DataTypeController::ASSOCIATION_FAILED:
- start_result = DataTypeManager::ASSOCIATION_FAILED;
+ configure_result = DataTypeManager::ASSOCIATION_FAILED;
break;
case DataTypeController::UNRECOVERABLE_ERROR:
- start_result = DataTypeManager::UNRECOVERABLE_ERROR;
+ configure_result = DataTypeManager::UNRECOVERABLE_ERROR;
break;
default:
NOTREACHED();
break;
}
- start_callback_->Run(start_result);
- start_callback_.reset();
+ NotifyDone(configure_result);
}
void DataTypeManagerImpl::Stop() {
@@ -150,58 +246,73 @@ void DataTypeManagerImpl::Stop() {
if (state_ == STOPPED)
return;
- // If we are currently starting, then the current type is in a
- // partially started state. Abort the startup of the current type
- // and continue shutdown when the abort completes.
- if (state_ == STARTING) {
+ // If we are currently configuring, then the current type is in a
+ // partially started state. Abort the startup of the current type,
+ // which will synchronously invoke the start callback.
+ if (state_ == CONFIGURING) {
state_ = STOPPING;
- syncable::ModelType type = kStartOrder[current_type_];
- controllers_[type]->Stop();
+ current_dtc_->Stop();
return;
}
+ // If Stop() is called while waiting for pause or resume, we no
+ // longer care about this.
+ if (state_ == PAUSE_PENDING)
+ RemoveObserver(NotificationType::SYNC_PAUSED);
+ if (state_ == RESUME_PENDING)
+ RemoveObserver(NotificationType::SYNC_RESUMED);
+
state_ = STOPPING;
FinishStop();
}
void DataTypeManagerImpl::FinishStop() {
- DCHECK(state_== STARTING || state_ == STOPPING || state_ == RESUME_PENDING);
+ DCHECK(state_== CONFIGURING ||
+ state_ == STOPPING ||
+ state_ == PAUSE_PENDING ||
+ state_ == RESUME_PENDING);
// Simply call the Stop() method on all running data types.
- for (unsigned int i = 0; i < arraysize(kStartOrder); ++i) {
- syncable::ModelType type = kStartOrder[i];
- if (IsRegistered(type) &&
- controllers_[type]->state() == DataTypeController::RUNNING) {
- controllers_[type]->Stop();
- LOG(INFO) << "Stopped " << controllers_[type]->name();
+ for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
+ it != controllers_.end(); ++it) {
+ DataTypeController* dtc = (*it).second;
+ if (dtc->state() == DataTypeController::RUNNING) {
+ dtc->Stop();
+ LOG(INFO) << "Stopped " << dtc->name();
}
}
state_ = STOPPED;
}
-bool DataTypeManagerImpl::IsRegistered(syncable::ModelType type) {
- return controllers_.count(type) == 1;
-}
-
-bool DataTypeManagerImpl::IsEnabled(syncable::ModelType type) {
- return IsRegistered(type) && controllers_[type]->enabled();
-}
-
void DataTypeManagerImpl::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
- switch(type.value) {
+ switch (type.value) {
case NotificationType::SYNC_PAUSED:
- DCHECK_EQ(state_, PAUSE_PENDING);
- state_ = STARTING;
+ DCHECK(state_ == PAUSE_PENDING || state_ == RESTARTING);
RemoveObserver(NotificationType::SYNC_PAUSED);
+
+ // If the state changed to RESTARTING while waiting to be
+ // paused, resume the syncer so we can restart.
+ if (state_ == RESTARTING) {
+ ResumeSyncer();
+ return;
+ }
+
+ state_ = CONFIGURING;
StartNextType();
break;
case NotificationType::SYNC_RESUMED:
- DCHECK_EQ(state_, RESUME_PENDING);
- state_ = STARTED;
+ DCHECK(state_ == RESUME_PENDING || state_ == RESTARTING);
RemoveObserver(NotificationType::SYNC_RESUMED);
- start_callback_->Run(OK);
- start_callback_.reset();
+
+ // If we are resuming because of a restart, continue the restart.
+ if (state_ == RESTARTING) {
+ Restart();
+ return;
+ }
+
+ state_ = CONFIGURED;
+ NotifyDone(OK);
break;
default:
NOTREACHED();
@@ -220,4 +331,36 @@ void DataTypeManagerImpl::RemoveObserver(NotificationType type) {
NotificationService::AllSources());
}
+void DataTypeManagerImpl::NotifyStart() {
+ NotificationService::current()->Notify(
+ NotificationType::SYNC_CONFIGURE_START,
+ NotificationService::AllSources(),
+ NotificationService::NoDetails());
+}
+
+void DataTypeManagerImpl::NotifyDone(ConfigureResult result) {
+ NotificationService::current()->Notify(
+ NotificationType::SYNC_CONFIGURE_DONE,
+ NotificationService::AllSources(),
+ Details<ConfigureResult>(&result));
+}
+
+void DataTypeManagerImpl::ResumeSyncer() {
+ AddObserver(NotificationType::SYNC_RESUMED);
+ if (!backend_->RequestResume()) {
+ RemoveObserver(NotificationType::SYNC_RESUMED);
+ FinishStop();
+ NotifyDone(UNRECOVERABLE_ERROR);
+ }
+}
+
+void DataTypeManagerImpl::PauseSyncer() {
+ AddObserver(NotificationType::SYNC_PAUSED);
+ if (!backend_->RequestPause()) {
+ RemoveObserver(NotificationType::SYNC_PAUSED);
+ FinishStop();
+ NotifyDone(UNRECOVERABLE_ERROR);
+ }
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/data_type_manager_impl.h b/chrome/browser/sync/glue/data_type_manager_impl.h
index 623c8f5..b8be7715 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl.h
+++ b/chrome/browser/sync/glue/data_type_manager_impl.h
@@ -7,6 +7,9 @@
#include "chrome/browser/sync/glue/data_type_manager.h"
+#include <map>
+#include <vector>
+
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "chrome/common/notification_observer.h"
@@ -18,6 +21,7 @@ class NotificationDetails;
namespace browser_sync {
+class DataTypeController;
class SyncBackendHost;
class DataTypeManagerImpl : public DataTypeManager,
@@ -28,14 +32,10 @@ class DataTypeManagerImpl : public DataTypeManager,
virtual ~DataTypeManagerImpl() {}
// DataTypeManager interface.
- virtual void Start(StartCallback* start_callback);
+ virtual void Configure(const TypeSet& desired_types);
virtual void Stop();
- virtual bool IsRegistered(syncable::ModelType type);
-
- virtual bool IsEnabled(syncable::ModelType type);
-
virtual const DataTypeController::TypeMap& controllers() {
return controllers_;
};
@@ -61,15 +61,24 @@ class DataTypeManagerImpl : public DataTypeManager,
// Stops all data types.
void FinishStop();
+ void Restart();
void AddObserver(NotificationType type);
void RemoveObserver(NotificationType type);
+ void NotifyStart();
+ void NotifyDone(ConfigureResult result);
+ void ResumeSyncer();
+ void PauseSyncer();
SyncBackendHost* backend_;
+ // Map of all data type controllers that are available for sync.
+ // This list is determined at startup by various command line flags.
DataTypeController::TypeMap controllers_;
State state_;
- int current_type_;
+ DataTypeController* current_dtc_;
+ std::map<syncable::ModelType, int> start_order_;
+ std::vector<DataTypeController*> needs_start_;
+ std::vector<DataTypeController*> needs_stop_;
- scoped_ptr<StartCallback> start_callback_;
NotificationRegistrar notification_registrar_;
DISALLOW_COPY_AND_ASSIGN(DataTypeManagerImpl);
diff --git a/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc b/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc
index a7843f1..d17025b 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc
+++ b/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "testing/gtest/include/gtest/gtest.h"
+#include <set>
+
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/stl_util-inl.h"
@@ -13,8 +14,14 @@
#include "chrome/browser/sync/glue/data_type_manager_impl.h"
#include "chrome/browser/sync/glue/sync_backend_host_mock.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_observer_mock.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
using browser_sync::DataTypeManager;
using browser_sync::DataTypeManagerImpl;
@@ -23,6 +30,10 @@ using browser_sync::DataTypeControllerMock;
using browser_sync::SyncBackendHostMock;
using testing::_;
using testing::DoAll;
+using testing::DoDefault;
+using testing::InSequence;
+using testing::Property;
+using testing::Pointee;
using testing::Return;
using testing::SaveArg;
@@ -31,11 +42,6 @@ ACTION_P(InvokeCallback, callback_result) {
delete arg1;
}
-class StartCallback {
- public:
- MOCK_METHOD1(Run, void(DataTypeManager::StartResult result));
-};
-
class DataTypeManagerImplTest : public testing::Test {
public:
DataTypeManagerImplTest()
@@ -45,6 +51,15 @@ class DataTypeManagerImplTest : public testing::Test {
}
protected:
+ virtual void SetUp() {
+ registrar_.Add(&observer_,
+ NotificationType::SYNC_CONFIGURE_START,
+ NotificationService::AllSources());
+ registrar_.Add(&observer_,
+ NotificationType::SYNC_CONFIGURE_DONE,
+ NotificationService::AllSources());
+ }
+
DataTypeControllerMock* MakeBookmarkDTC() {
DataTypeControllerMock* dtc = new DataTypeControllerMock();
EXPECT_CALL(*dtc, enabled()).WillRepeatedly(Return(true));
@@ -62,107 +77,267 @@ class DataTypeManagerImplTest : public testing::Test {
}
void SetStartStopExpectations(DataTypeControllerMock* mock_dtc) {
+ InSequence seq;
+ EXPECT_CALL(*mock_dtc, state()).
+ WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
EXPECT_CALL(*mock_dtc, Start(true, _)).
WillOnce(InvokeCallback((DataTypeController::OK)));
- EXPECT_CALL(*mock_dtc, Stop()).Times(1);
-
- // The state() getter is used a few times in DCHECK code and needs
- // different mock programming in debug vs. release mode.
-#ifdef NDEBUG
EXPECT_CALL(*mock_dtc, state()).
- WillOnce(Return(DataTypeController::RUNNING));
-#else
+ WillRepeatedly(Return(DataTypeController::RUNNING));
+ EXPECT_CALL(*mock_dtc, Stop()).Times(1);
EXPECT_CALL(*mock_dtc, state()).
- WillOnce(Return(DataTypeController::NOT_RUNNING)).
- WillOnce(Return(DataTypeController::RUNNING)).
- WillOnce(Return(DataTypeController::RUNNING));
-#endif
+ WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
+ }
+
+ void SetConfigureStartExpectation() {
+ EXPECT_CALL(
+ observer_,
+ Observe(NotificationType(NotificationType::SYNC_CONFIGURE_START),
+ _, _));
+ }
+
+ void SetConfigureDoneExpectation(DataTypeManager::ConfigureResult result) {
+ EXPECT_CALL(
+ observer_,
+ Observe(NotificationType(NotificationType::SYNC_CONFIGURE_DONE), _,
+ Property(&Details<DataTypeManager::ConfigureResult>::ptr,
+ Pointee(result))));
}
MessageLoopForUI message_loop_;
ChromeThread ui_thread_;
DataTypeController::TypeMap controllers_;
- StartCallback callback_;
SyncBackendHostMock backend_;
+ NotificationObserverMock observer_;
+ NotificationRegistrar registrar_;
+ std::set<syncable::ModelType> types_;
};
TEST_F(DataTypeManagerImplTest, NoControllers) {
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(callback_, Run(DataTypeManager::OK));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
- EXPECT_EQ(DataTypeManager::STARTED, dtm.state());
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
dtm.Stop();
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
-TEST_F(DataTypeManagerImplTest, OneDisabledController) {
+TEST_F(DataTypeManagerImplTest, ConfigureOne) {
DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- EXPECT_CALL(*bookmark_dtc, enabled()).WillRepeatedly(Return(false));
- EXPECT_CALL(*bookmark_dtc, Start(_, _)).Times(0);
- EXPECT_CALL(*bookmark_dtc, Stop()).Times(0);
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
+ SetStartStopExpectations(bookmark_dtc);
+ controllers_[syncable::BOOKMARKS] = bookmark_dtc;
+ EXPECT_CALL(backend_, RequestPause()).Times(1);
+ EXPECT_CALL(backend_, RequestResume()).Times(1);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
+ types_.insert(syncable::BOOKMARKS);
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+ dtm.Stop();
+ EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
+}
+
+TEST_F(DataTypeManagerImplTest, ConfigureOneThenAnother) {
+ DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
+ SetStartStopExpectations(bookmark_dtc);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
+ DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
+ SetStartStopExpectations(preference_dtc);
+ controllers_[syncable::PREFERENCES] = preference_dtc;
+ EXPECT_CALL(backend_, RequestPause()).Times(2);
+ EXPECT_CALL(backend_, RequestResume()).Times(2);
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(callback_, Run(DataTypeManager::OK));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
- EXPECT_EQ(DataTypeManager::STARTED, dtm.state());
+ types_.insert(syncable::BOOKMARKS);
+
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+
+ types_.insert(syncable::PREFERENCES);
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+
dtm.Stop();
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
-TEST_F(DataTypeManagerImplTest, OneEnabledController) {
+TEST_F(DataTypeManagerImplTest, ConfigureOneThenSwitch) {
DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
SetStartStopExpectations(bookmark_dtc);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
+ DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
+ SetStartStopExpectations(preference_dtc);
+ controllers_[syncable::PREFERENCES] = preference_dtc;
+ EXPECT_CALL(backend_, RequestPause()).Times(2);
+ EXPECT_CALL(backend_, RequestResume()).Times(2);
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(callback_, Run(DataTypeManager::OK));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
- EXPECT_EQ(DataTypeManager::STARTED, dtm.state());
+ types_.insert(syncable::BOOKMARKS);
+
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+
+ types_.clear();
+ types_.insert(syncable::PREFERENCES);
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+
dtm.Stop();
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
-TEST_F(DataTypeManagerImplTest, OneFailingController) {
+TEST_F(DataTypeManagerImplTest, ConfigureWhileOneInFlight) {
DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- EXPECT_CALL(*bookmark_dtc, Start(true, _)).
- WillOnce(InvokeCallback((DataTypeController::ASSOCIATION_FAILED)));
- EXPECT_CALL(*bookmark_dtc, Stop()).Times(0);
- // The state() getter is used a few times in DCHECK code and needs
- // different mock programming in debug vs. release mode.
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
+ // Save the callback here so we can interrupt startup.
+ DataTypeController::StartCallback* callback;
+ {
+ InSequence seq;
+ EXPECT_CALL(*bookmark_dtc, state()).
+ WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
+ EXPECT_CALL(*bookmark_dtc, Start(true, _)).
+ WillOnce(SaveArg<1>(&callback));
+ EXPECT_CALL(*bookmark_dtc, state()).
+ WillRepeatedly(Return(DataTypeController::RUNNING));
+ EXPECT_CALL(*bookmark_dtc, Stop()).Times(1);
+ EXPECT_CALL(*bookmark_dtc, state()).
+ WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
+ }
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
+ DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
+ SetStartStopExpectations(preference_dtc);
+ controllers_[syncable::PREFERENCES] = preference_dtc;
+
+ EXPECT_CALL(backend_, RequestPause()).Times(2);
+ EXPECT_CALL(backend_, RequestResume()).Times(2);
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(backend_, RequestPause()).
- WillOnce(DoAll(Notify(NotificationType::SYNC_PAUSED), Return(true)));
- EXPECT_CALL(callback_, Run(DataTypeManager::ASSOCIATION_FAILED));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
+ types_.insert(syncable::BOOKMARKS);
+
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+
+ // At this point, the bookmarks dtc should be in flight. Add
+ // preferences and continue starting bookmarks.
+ types_.insert(syncable::PREFERENCES);
+ dtm.Configure(types_);
+ callback->Run(DataTypeController::OK);
+ delete callback;
+
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+
+ dtm.Stop();
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
-TEST_F(DataTypeManagerImplTest, TwoEnabledControllers) {
+TEST_F(DataTypeManagerImplTest, ConfigureWhilePausePending) {
DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
SetStartStopExpectations(bookmark_dtc);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
+ DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
+ SetStartStopExpectations(preference_dtc);
+ controllers_[syncable::PREFERENCES] = preference_dtc;
+
+ // Don't notify the first time pause is called.
+ EXPECT_CALL(backend_, RequestPause()).
+ WillOnce(Return(true)).
+ WillOnce(DoDefault());
+ EXPECT_CALL(backend_, RequestResume()).Times(2);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
+ types_.insert(syncable::BOOKMARKS);
+
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::PAUSE_PENDING, dtm.state());
+
+ // Configure while pause pending.
+ types_.insert(syncable::PREFERENCES);
+ dtm.Configure(types_);
+
+ // Should still be PAUSE_PENDING.
+ EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
+
+ // Send the SYNC_PAUSED notification. This will allow the DTM to
+ // wake up and restart itself with the new configuration.
+ NotificationService::current()->Notify(NotificationType::SYNC_PAUSED,
+ NotificationService::AllSources(),
+ NotificationService::NoDetails());
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+ dtm.Stop();
+ EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
+}
+TEST_F(DataTypeManagerImplTest, ConfigureWhileResumePending) {
+ DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
+ SetStartStopExpectations(bookmark_dtc);
+ controllers_[syncable::BOOKMARKS] = bookmark_dtc;
DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
SetStartStopExpectations(preference_dtc);
controllers_[syncable::PREFERENCES] = preference_dtc;
+ EXPECT_CALL(backend_, RequestPause()).Times(2);
+ // Don't notify the first time resume is called.
+ EXPECT_CALL(backend_, RequestResume()).
+ WillOnce(Return(true)).
+ WillOnce(DoDefault());
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(callback_, Run(DataTypeManager::OK));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
- EXPECT_EQ(DataTypeManager::STARTED, dtm.state());
+ types_.insert(syncable::BOOKMARKS);
+
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::OK);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::RESUME_PENDING, dtm.state());
+
+ // Configure while resume pending.
+ types_.insert(syncable::PREFERENCES);
+ dtm.Configure(types_);
+
+ // Should still be PAUSE_PENDING.
+ EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
+
+ // Send the SYNC_PAUSED notification. This will allow the DTM to
+ // wake up and restart itself with the new configuration.
+ NotificationService::current()->Notify(NotificationType::SYNC_RESUMED,
+ NotificationService::AllSources(),
+ NotificationService::NoDetails());
+ EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
dtm.Stop();
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
+TEST_F(DataTypeManagerImplTest, OneFailingController) {
+ DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
+ EXPECT_CALL(*bookmark_dtc, Start(true, _)).
+ WillOnce(InvokeCallback((DataTypeController::ASSOCIATION_FAILED)));
+ EXPECT_CALL(*bookmark_dtc, Stop()).Times(0);
+ EXPECT_CALL(*bookmark_dtc, state()).
+ WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
+ controllers_[syncable::BOOKMARKS] = bookmark_dtc;
+
+ DataTypeManagerImpl dtm(&backend_, controllers_);
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::ASSOCIATION_FAILED);
+ EXPECT_CALL(backend_, RequestPause()).Times(1);
+ EXPECT_CALL(backend_, RequestResume()).Times(0);
+
+ types_.insert(syncable::BOOKMARKS);
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
+}
+
TEST_F(DataTypeManagerImplTest, InterruptedStart) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
+ DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
SetStartStopExpectations(bookmark_dtc);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
@@ -177,11 +352,17 @@ TEST_F(DataTypeManagerImplTest, InterruptedStart) {
controllers_[syncable::PREFERENCES] = preference_dtc;
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(backend_, RequestPause()).
- WillOnce(DoAll(Notify(NotificationType::SYNC_PAUSED), Return(true)));
- EXPECT_CALL(callback_, Run(DataTypeManager::ABORTED));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
- EXPECT_EQ(DataTypeManager::STARTING, dtm.state());
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::ABORTED);
+ EXPECT_CALL(backend_, RequestPause()).Times(1);
+ EXPECT_CALL(backend_, RequestResume()).Times(0);
+
+ types_.insert(syncable::BOOKMARKS);
+ types_.insert(syncable::PREFERENCES);
+ dtm.Configure(types_);
+ // Configure should stop in the CONFIGURING state because we are
+ // waiting for the preferences callback to be invoked.
+ EXPECT_EQ(DataTypeManager::CONFIGURING, dtm.state());
// Call stop before the preference callback is invoked.
dtm.Stop();
@@ -204,58 +385,17 @@ TEST_F(DataTypeManagerImplTest, SecondControllerFails) {
controllers_[syncable::PREFERENCES] = preference_dtc;
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(backend_, RequestPause()).
- WillOnce(DoAll(Notify(NotificationType::SYNC_PAUSED), Return(true)));
- EXPECT_CALL(callback_, Run(DataTypeManager::ASSOCIATION_FAILED));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::ASSOCIATION_FAILED);
+ EXPECT_CALL(backend_, RequestPause()).Times(1);
+ EXPECT_CALL(backend_, RequestResume()).Times(0);
+
+ types_.insert(syncable::BOOKMARKS);
+ types_.insert(syncable::PREFERENCES);
+ dtm.Configure(types_);
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
-TEST_F(DataTypeManagerImplTest, IsRegisteredNone) {
- DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_FALSE(dtm.IsRegistered(syncable::BOOKMARKS));
-}
-
-TEST_F(DataTypeManagerImplTest, IsRegisteredButNoMatch) {
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- EXPECT_CALL(*preference_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- controllers_[syncable::PREFERENCES] = preference_dtc;
- DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_FALSE(dtm.IsRegistered(syncable::BOOKMARKS));
-}
-
-TEST_F(DataTypeManagerImplTest, IsRegisteredMatch) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_TRUE(dtm.IsRegistered(syncable::BOOKMARKS));
-}
-
-TEST_F(DataTypeManagerImplTest, IsNotEnabled) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- EXPECT_CALL(*bookmark_dtc, enabled()).
- WillRepeatedly(Return(false));
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_FALSE(dtm.IsEnabled(syncable::BOOKMARKS));
-}
-
-TEST_F(DataTypeManagerImplTest, IsEnabled) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- EXPECT_CALL(*bookmark_dtc, enabled()).
- WillRepeatedly(Return(true));
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_TRUE(dtm.IsEnabled(syncable::BOOKMARKS));
-}
-
TEST_F(DataTypeManagerImplTest, PauseFailed) {
DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
EXPECT_CALL(*bookmark_dtc, Start(_, _)).Times(0);
@@ -264,11 +404,13 @@ TEST_F(DataTypeManagerImplTest, PauseFailed) {
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(backend_, RequestPause()).
- WillOnce(Return(false));
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::UNRECOVERABLE_ERROR);
+ EXPECT_CALL(backend_, RequestPause()).WillOnce(Return(false));
+ EXPECT_CALL(backend_, RequestResume()).Times(0);
- EXPECT_CALL(callback_, Run(DataTypeManager::UNRECOVERABLE_ERROR));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
+ types_.insert(syncable::BOOKMARKS);
+ dtm.Configure(types_);
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
@@ -278,12 +420,12 @@ TEST_F(DataTypeManagerImplTest, ResumeFailed) {
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
DataTypeManagerImpl dtm(&backend_, controllers_);
- EXPECT_CALL(backend_, RequestPause()).
- WillOnce(DoAll(Notify(NotificationType::SYNC_PAUSED), Return(true)));
- EXPECT_CALL(backend_, RequestResume()).
- WillOnce(Return(false));
+ SetConfigureStartExpectation();
+ SetConfigureDoneExpectation(DataTypeManager::UNRECOVERABLE_ERROR);
+ EXPECT_CALL(backend_, RequestPause()).Times(1);
+ EXPECT_CALL(backend_, RequestResume()).WillOnce(Return(false));
- EXPECT_CALL(callback_, Run(DataTypeManager::UNRECOVERABLE_ERROR));
- dtm.Start(NewCallback(&callback_, &StartCallback::Run));
+ types_.insert(syncable::BOOKMARKS);
+ dtm.Configure(types_);
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
diff --git a/chrome/browser/sync/glue/data_type_manager_mock.h b/chrome/browser/sync/glue/data_type_manager_mock.h
index 540553b..1d97dac 100644
--- a/chrome/browser/sync/glue/data_type_manager_mock.h
+++ b/chrome/browser/sync/glue/data_type_manager_mock.h
@@ -6,18 +6,41 @@
#define CHROME_BROWSER_SYNC_GLUE_DATA_TYPE_MANAGER_MOCK_H__
#include "chrome/browser/sync/glue/data_type_manager.h"
+#include "chrome/browser/sync/profile_sync_test_util.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
+ACTION_P2(NotifyWithResult, type, result) {
+ NotificationService::current()->Notify(
+ type,
+ NotificationService::AllSources(),
+ Details<browser_sync::DataTypeManager::ConfigureResult>(result));
+}
+
namespace browser_sync {
class DataTypeManagerMock : public DataTypeManager {
public:
- MOCK_METHOD1(Start, void(StartCallback* start_callback));
+ DataTypeManagerMock() : result_(OK) {
+ // By default, calling Configure will send a SYNC_CONFIGURE_START
+ // and SYNC_CONFIGURE_DONE notification with a DataTypeManager::OK
+ // detail.
+ ON_CALL(*this, Configure(testing::_)).
+ WillByDefault(testing::DoAll(
+ Notify(NotificationType::SYNC_CONFIGURE_START),
+ NotifyWithResult(NotificationType::SYNC_CONFIGURE_DONE,
+ &result_)));
+ }
+
+ MOCK_METHOD1(Configure, void(const TypeSet&));
MOCK_METHOD0(Stop, void());
- MOCK_METHOD1(IsRegistered, bool(syncable::ModelType type));
- MOCK_METHOD1(IsEnabled, bool(syncable::ModelType type));
MOCK_METHOD0(controllers, const DataTypeController::TypeMap&());
MOCK_METHOD0(state, State());
+
+ private:
+ DataTypeManager::ConfigureResult result_;
};
} // namespace browser_sync
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index bb184cd..a6d388c 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/sync/profile_sync_service.h"
+#include <set>
+
#include "app/l10n_util.h"
#include "base/callback.h"
#include "base/command_line.h"
@@ -21,6 +23,10 @@
#include "chrome/browser/sync/glue/data_type_manager.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/time_format.h"
#include "grit/generated_resources.h"
@@ -50,8 +56,13 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
is_auth_in_progress_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)),
unrecoverable_error_detected_(false),
- startup_had_first_time_(false),
notification_method_(browser_sync::kDefaultNotificationMethod) {
+ registrar_.Add(this,
+ NotificationType::SYNC_CONFIGURE_START,
+ NotificationService::AllSources());
+ registrar_.Add(this,
+ NotificationType::SYNC_CONFIGURE_DONE,
+ NotificationService::AllSources());
}
ProfileSyncService::~ProfileSyncService() {
@@ -82,7 +93,7 @@ void ProfileSyncService::Initialize() {
void ProfileSyncService::RegisterDataTypeController(
DataTypeController* data_type_controller) {
- DCHECK(data_type_controllers_.count(data_type_controller->type()) == 0);
+ DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U);
data_type_controllers_[data_type_controller->type()] =
data_type_controller;
}
@@ -403,7 +414,6 @@ void ProfileSyncService::OnUserCancelledDialog() {
void ProfileSyncService::StartProcessingChangesIfReady() {
DCHECK(backend_initialized_);
- startup_had_first_time_ = false;
// If we're running inside Chromium OS, always allow merges and
// consider the sync setup complete.
@@ -411,21 +421,16 @@ void ProfileSyncService::StartProcessingChangesIfReady() {
SetSyncSetupCompleted();
}
- data_type_manager_.reset(
- factory_->CreateDataTypeManager(backend_.get(), data_type_controllers_));
- data_type_manager_->Start(
- NewCallback(this, &ProfileSyncService::DataTypeManagerStartCallback));
-}
-
-void ProfileSyncService::DataTypeManagerStartCallback(
- DataTypeManager::StartResult result) {
- if (result != DataTypeManager::OK) {
- OnUnrecoverableError();
- return;
+ std::set<syncable::ModelType> types;
+ for (DataTypeController::TypeMap::const_iterator it =
+ data_type_controllers_.begin();
+ it != data_type_controllers_.end(); ++it) {
+ types.insert((*it).first);
}
- wizard_.Step(SyncSetupWizard::DONE);
- FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
+ data_type_manager_.reset(
+ factory_->CreateDataTypeManager(backend_.get(), data_type_controllers_));
+ data_type_manager_->Configure(types);
}
void ProfileSyncService::ActivateDataType(
@@ -442,6 +447,33 @@ void ProfileSyncService::DeactivateDataType(
backend_->DeactivateDataType(data_type_controller, change_processor);
}
+void ProfileSyncService::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type.value) {
+ case NotificationType::SYNC_CONFIGURE_START: {
+ // TODO(sync): Maybe toast?
+ break;
+ }
+ case NotificationType::SYNC_CONFIGURE_DONE: {
+ DataTypeManager::ConfigureResult result =
+ *(Details<DataTypeManager::ConfigureResult>(details).ptr());
+ if (result != DataTypeManager::OK) {
+ OnUnrecoverableError();
+ return;
+ }
+
+ // TODO(sync): Less wizard, more toast.
+ wizard_.Step(SyncSetupWizard::DONE);
+ FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
+ break;
+ }
+ default: {
+ NOTREACHED();
+ }
+ }
+}
+
void ProfileSyncService::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
@@ -470,5 +502,5 @@ bool ProfileSyncService::ShouldPushChanges() {
if (!data_type_manager_.get())
return false;
- return data_type_manager_->state() == DataTypeManager::STARTED;
+ return data_type_manager_->state() == DataTypeManager::CONFIGURED;
}
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index 18eca8a..d9d8f65 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -21,9 +21,15 @@
#include "chrome/browser/sync/sync_setup_wizard.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/unrecoverable_error_handler.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
+class NotificationDetails;
+class NotificationSource;
+class NotificationType;
+
// Various UI components such as the New Tab page can be driven by observing
// the ProfileSyncService through this interface.
class ProfileSyncServiceObserver {
@@ -42,7 +48,8 @@ class ProfileSyncServiceObserver {
// ProfileSyncService is the layer between browser subsystems like bookmarks,
// and the sync backend.
class ProfileSyncService : public browser_sync::SyncFrontend,
- public browser_sync::UnrecoverableErrorHandler {
+ public browser_sync::UnrecoverableErrorHandler,
+ public NotificationObserver {
public:
typedef ProfileSyncServiceObserver Observer;
typedef browser_sync::SyncBackendHost::Status Status;
@@ -202,6 +209,11 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
browser_sync::DataTypeController* data_type_controller,
browser_sync::ChangeProcessor* change_processor);
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
protected:
// Call this after any of the subsystems being synced (the bookmark
// model and the sync backend) finishes its initialization. When everything
@@ -223,9 +235,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
void RegisterPreferences();
void ClearPreferences();
- void DataTypeManagerStartCallback(
- browser_sync::DataTypeManager::StartResult result);
-
// Tests need to override this. If |delete_sync_data_folder| is true, then
// this method will delete all previous "Sync Data" folders. (useful if the
// folder is partial/corrupt)
@@ -316,11 +325,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// doing any work that might corrupt things further.
bool unrecoverable_error_detected_;
- // True if at least one of the data types started up was started for
- // the first time. TODO(sync): Remove this when we have full
- // support for starting multiple data types.
- bool startup_had_first_time_;
-
// Which peer-to-peer notification method to use.
browser_sync::NotificationMethod notification_method_;
@@ -329,6 +333,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
ObserverList<Observer> observers_;
+ NotificationRegistrar registrar_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileSyncService);
};
diff --git a/chrome/browser/sync/profile_sync_service_startup_unittest.cc b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
index e828109..18815e6 100644
--- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/sync/profile_sync_factory_mock.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
+#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_profile.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -19,6 +20,7 @@
using browser_sync::DataTypeManager;
using browser_sync::DataTypeManagerMock;
using testing::_;
+using testing::DoAll;
using testing::InvokeArgument;
using testing::Mock;
using testing::Return;
@@ -74,7 +76,7 @@ class ProfileSyncServiceStartupTest : public testing::Test {
TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Start(_)).Times(0);
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(0);
// We've never completed startup.
profile_.GetPrefs()->ClearPref(prefs::kSyncHasSetupCompleted);
@@ -90,10 +92,9 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) {
Mock::VerifyAndClearExpectations(data_type_manager);
// Then start things up.
- EXPECT_CALL(*data_type_manager, Start(_)).
- WillOnce(InvokeCallback(DataTypeManager::OK));
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(1);
EXPECT_CALL(*data_type_manager, state()).
- WillOnce(Return(DataTypeManager::STARTED));
+ WillOnce(Return(DataTypeManager::CONFIGURED));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
EXPECT_CALL(observer_, OnStateChanged()).Times(3);
service_->EnableForUser();
@@ -101,10 +102,9 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) {
TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartNormal)) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Start(_)).
- WillOnce(InvokeCallback(DataTypeManager::OK));
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(1);
EXPECT_CALL(*data_type_manager, state()).
- WillOnce(Return(DataTypeManager::STARTED));
+ WillOnce(Return(DataTypeManager::CONFIGURED));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
EXPECT_CALL(observer_, OnStateChanged()).Times(2);
@@ -114,8 +114,12 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartNormal)) {
TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFailure)) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Start(_)).
- WillOnce(InvokeCallback(DataTypeManager::ASSOCIATION_FAILED));
+ DataTypeManager::ConfigureResult result =
+ DataTypeManager::ASSOCIATION_FAILED;
+ EXPECT_CALL(*data_type_manager, Configure(_)).
+ WillOnce(DoAll(Notify(NotificationType::SYNC_CONFIGURE_START),
+ NotifyWithResult(NotificationType::SYNC_CONFIGURE_DONE,
+ &result)));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
EXPECT_CALL(*data_type_manager, state()).
WillOnce(Return(DataTypeManager::STOPPED));
@@ -126,8 +130,8 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFailure)) {
EXPECT_TRUE(service_->unrecoverable_error_detected());
}
-class ProfileSyncServiceStartupBootstrapTest :
- public ProfileSyncServiceStartupTest {
+class ProfileSyncServiceStartupBootstrapTest
+ : public ProfileSyncServiceStartupTest {
public:
ProfileSyncServiceStartupBootstrapTest() {}
virtual ~ProfileSyncServiceStartupBootstrapTest() {}
@@ -140,10 +144,9 @@ class ProfileSyncServiceStartupBootstrapTest :
TEST_F(ProfileSyncServiceStartupBootstrapTest, SKIP_MACOSX(StartFirstTime)) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Start(_)).
- WillOnce(InvokeCallback(DataTypeManager::OK));
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(1);
EXPECT_CALL(*data_type_manager, state()).
- WillOnce(Return(DataTypeManager::STARTED));
+ WillOnce(Return(DataTypeManager::CONFIGURED));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
EXPECT_CALL(observer_, OnStateChanged()).Times(3);