diff options
author | skrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-23 21:47:04 +0000 |
---|---|---|
committer | skrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-23 21:47:04 +0000 |
commit | c4c672aea10f13b53c4271b42309dd5a2cdd4cb9 (patch) | |
tree | 391dd8fcab139449970ea119e373120b60914569 /chrome/browser/sync | |
parent | f4481633ac082f136453be1926c77d0c079b10c4 (diff) | |
download | chromium_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.h | 68 | ||||
-rw-r--r-- | chrome/browser/sync/glue/data_type_manager_impl.cc | 315 | ||||
-rw-r--r-- | chrome/browser/sync/glue/data_type_manager_impl.h | 23 | ||||
-rw-r--r-- | chrome/browser/sync/glue/data_type_manager_impl_unittest.cc | 372 | ||||
-rw-r--r-- | chrome/browser/sync/glue/data_type_manager_mock.h | 29 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.cc | 66 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.h | 24 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service_startup_unittest.cc | 31 |
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); |