diff options
author | haitaol@chromium.org <haitaol@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-21 18:35:44 +0000 |
---|---|---|
committer | haitaol@chromium.org <haitaol@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-21 18:35:44 +0000 |
commit | d50311f0a8785d61a806337d842e0a48bb3e75f4 (patch) | |
tree | 2568bafe3ee83322c1e646931516ccd41d8f7e3b | |
parent | bcae6b83ce10987bf5bca2fddfc9e2c058a5c4b5 (diff) | |
download | chromium_src-d50311f0a8785d61a806337d842e0a48bb3e75f4.zip chromium_src-d50311f0a8785d61a806337d842e0a48bb3e75f4.tar.gz chromium_src-d50311f0a8785d61a806337d842e0a48bb3e75f4.tar.bz2 |
Add sync manager classes for backup/rollback:
* SyncBackupManager runs before first-sync to save local data in backup DB.
* SyncRollbackManager deletes local data that's not found in backup DB.
BUG=362679
Review URL: https://codereview.chromium.org/235053006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@265027 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | sync/internal_api/public/base/model_type.h | 3 | ||||
-rw-r--r-- | sync/internal_api/sync_backup_manager.cc | 112 | ||||
-rw-r--r-- | sync/internal_api/sync_backup_manager.h | 69 | ||||
-rw-r--r-- | sync/internal_api/sync_backup_manager_unittest.cc | 112 | ||||
-rw-r--r-- | sync/internal_api/sync_rollback_manager.cc | 133 | ||||
-rw-r--r-- | sync/internal_api/sync_rollback_manager.h | 62 | ||||
-rw-r--r-- | sync/internal_api/sync_rollback_manager_base.cc | 322 | ||||
-rw-r--r-- | sync/internal_api/sync_rollback_manager_base.h | 140 | ||||
-rw-r--r-- | sync/internal_api/sync_rollback_manager_base_unittest.cc | 70 | ||||
-rw-r--r-- | sync/internal_api/sync_rollback_manager_unittest.cc | 205 | ||||
-rw-r--r-- | sync/sync_internal_api.gypi | 6 | ||||
-rw-r--r-- | sync/sync_tests.gypi | 5 | ||||
-rw-r--r-- | sync/syncable/model_type.cc | 14 |
13 files changed, 1252 insertions, 1 deletions
diff --git a/sync/internal_api/public/base/model_type.h b/sync/internal_api/public/base/model_type.h index 831fd2c..f412716 100644 --- a/sync/internal_api/public/base/model_type.h +++ b/sync/internal_api/public/base/model_type.h @@ -317,6 +317,9 @@ SYNC_EXPORT bool IsRealDataType(ModelType model_type); // TODO(haitaol): Make entries of act-once data types immutable. SYNC_EXPORT bool IsActOnceDataType(ModelType model_type); +// Returns set of model types that should be backed up before first sync. +SYNC_EXPORT ModelTypeSet BackupTypes(); + } // namespace syncer #endif // SYNC_INTERNAL_API_PUBLIC_BASE_MODEL_TYPE_H_ diff --git a/sync/internal_api/sync_backup_manager.cc b/sync/internal_api/sync_backup_manager.cc new file mode 100644 index 0000000..f255ae6 --- /dev/null +++ b/sync/internal_api/sync_backup_manager.cc @@ -0,0 +1,112 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sync/internal_api/sync_backup_manager.h" + +#include "sync/internal_api/public/write_transaction.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/mutable_entry.h" + +namespace syncer { + +SyncBackupManager::SyncBackupManager() + : in_normalization_(false) { +} + +SyncBackupManager::~SyncBackupManager() { +} + +void SyncBackupManager::Init( + const base::FilePath& database_location, + const WeakHandle<JsEventHandler>& event_handler, + const std::string& sync_server_and_path, + int sync_server_port, + bool use_ssl, + scoped_ptr<HttpPostProviderFactory> post_factory, + const std::vector<scoped_refptr<ModelSafeWorker> >& workers, + ExtensionsActivity* extensions_activity, + SyncManager::ChangeDelegate* change_delegate, + const SyncCredentials& credentials, + const std::string& invalidator_client_id, + const std::string& restored_key_for_bootstrapping, + const std::string& restored_keystore_key_for_bootstrapping, + InternalComponentsFactory* internal_components_factory, + Encryptor* encryptor, + scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler, + ReportUnrecoverableErrorFunction + report_unrecoverable_error_function, + CancelationSignal* cancelation_signal) { + SyncRollbackManagerBase::Init(database_location, event_handler, + sync_server_and_path, sync_server_port, + use_ssl, post_factory.Pass(), + workers, extensions_activity, change_delegate, + credentials, invalidator_client_id, + restored_key_for_bootstrapping, + restored_keystore_key_for_bootstrapping, + internal_components_factory, encryptor, + unrecoverable_error_handler.Pass(), + report_unrecoverable_error_function, + cancelation_signal); + + GetUserShare()->directory->CollectMetaHandleCounts( + &status_.num_entries_by_type, + &status_.num_to_delete_entries_by_type); +} + +void SyncBackupManager::SaveChanges() { + NormalizeEntries(); + GetUserShare()->directory->SaveChanges(); +} + +SyncStatus SyncBackupManager::GetDetailedStatus() const { + return status_; +} + +ModelTypeSet SyncBackupManager::HandleTransactionEndingChangeEvent( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans) { + ModelTypeSet types; + if (in_normalization_) { + // Skip if in our own WriteTransaction from NormalizeEntries(). + in_normalization_ = false; + return types; + } + + for (syncable::EntryKernelMutationMap::const_iterator it = + write_transaction_info.Get().mutations.Get().begin(); + it != write_transaction_info.Get().mutations.Get().end(); ++it) { + int64 id = it->first; + if (unsynced_.find(id) == unsynced_.end()) { + unsynced_.insert(id); + + const syncable::EntryKernel& e = it->second.mutated; + ModelType type = e.GetModelType(); + types.Put(type); + if (!e.ref(syncable::ID).ServerKnows()) + status_.num_entries_by_type[type]++; + if (e.ref(syncable::IS_DEL)) + status_.num_to_delete_entries_by_type[type]++; + } + } + return types; +} + +void SyncBackupManager::NormalizeEntries() { + WriteTransaction trans(FROM_HERE, GetUserShare()); + in_normalization_ = true; + for (std::set<int64>::const_iterator it = unsynced_.begin(); + it != unsynced_.end(); ++it) { + syncable::MutableEntry entry(trans.GetWrappedWriteTrans(), + syncable::GET_BY_HANDLE, *it); + CHECK(entry.good()); + + if (!entry.GetId().ServerKnows()) + entry.PutId(syncable::Id::CreateFromServerId(entry.GetId().value())); + entry.PutBaseVersion(1); + entry.PutIsUnsynced(false); + } + unsynced_.clear(); +} + +} // namespace syncer diff --git a/sync/internal_api/sync_backup_manager.h b/sync/internal_api/sync_backup_manager.h new file mode 100644 index 0000000..1bee105 --- /dev/null +++ b/sync/internal_api/sync_backup_manager.h @@ -0,0 +1,69 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SYNC_INTERNAL_API_SYNC_BACKUP_MANAGER_H_ +#define SYNC_INTERNAL_API_SYNC_BACKUP_MANAGER_H_ + +#include <set> + +#include "sync/internal_api/sync_rollback_manager_base.h" + +namespace syncer { + +// SyncBackupManager runs before user signs in to sync to back up user's data +// before sync starts. The data that's backed up can be used to restore user's +// settings to pre-sync state. +class SYNC_EXPORT_PRIVATE SyncBackupManager : public SyncRollbackManagerBase { + public: + SyncBackupManager(); + virtual ~SyncBackupManager(); + + // SyncManager implementation. + virtual void Init( + const base::FilePath& database_location, + const WeakHandle<JsEventHandler>& event_handler, + const std::string& sync_server_and_path, + int sync_server_port, + bool use_ssl, + scoped_ptr<HttpPostProviderFactory> post_factory, + const std::vector<scoped_refptr<ModelSafeWorker> >& workers, + ExtensionsActivity* extensions_activity, + SyncManager::ChangeDelegate* change_delegate, + const SyncCredentials& credentials, + const std::string& invalidator_client_id, + const std::string& restored_key_for_bootstrapping, + const std::string& restored_keystore_key_for_bootstrapping, + InternalComponentsFactory* internal_components_factory, + Encryptor* encryptor, + scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler, + ReportUnrecoverableErrorFunction + report_unrecoverable_error_function, + CancelationSignal* cancelation_signal) OVERRIDE; + virtual void SaveChanges() OVERRIDE; + virtual SyncStatus GetDetailedStatus() const OVERRIDE; + + // DirectoryChangeDelegate implementation. + virtual ModelTypeSet HandleTransactionEndingChangeEvent( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans) OVERRIDE; + + private: + // Replaces local IDs with server IDs and clear unsynced bit of modified + // entries. + void NormalizeEntries(); + + // Handles of unsynced entries caused by local model changes. + std::set<int64> unsynced_; + + // True if NormalizeEntries() is being called. + bool in_normalization_; + + SyncStatus status_; + + DISALLOW_COPY_AND_ASSIGN(SyncBackupManager); +}; + +} // namespace syncer + +#endif // SYNC_INTERNAL_API_SYNC_BACKUP_MANAGER_H_ diff --git a/sync/internal_api/sync_backup_manager_unittest.cc b/sync/internal_api/sync_backup_manager_unittest.cc new file mode 100644 index 0000000..5de790b --- /dev/null +++ b/sync/internal_api/sync_backup_manager_unittest.cc @@ -0,0 +1,112 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sync/internal_api/sync_backup_manager.h" + +#include "base/files/scoped_temp_dir.h" +#include "sync/internal_api/public/read_node.h" +#include "sync/internal_api/public/read_transaction.h" +#include "sync/internal_api/public/test/test_internal_components_factory.h" +#include "sync/internal_api/public/write_node.h" +#include "sync/internal_api/public/write_transaction.h" +#include "sync/syncable/entry.h" +#include "sync/test/test_directory_backing_store.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { + +namespace { + +void OnConfigDone(bool success) { + EXPECT_TRUE(success); +} + +class SyncBackupManagerTest : public testing::Test { + protected: + virtual void SetUp() OVERRIDE { + CHECK(temp_dir_.CreateUniqueTempDir()); + } + + void InitManager(SyncManager* manager) { + TestInternalComponentsFactory factory(InternalComponentsFactory::Switches(), + STORAGE_ON_DISK); + + manager->Init(temp_dir_.path(), + MakeWeakHandle(base::WeakPtr<JsEventHandler>()), + "", 0, true, scoped_ptr<HttpPostProviderFactory>().Pass(), + std::vector<scoped_refptr<ModelSafeWorker> >(), + NULL, NULL, SyncCredentials(), "", "", "", &factory, + NULL, scoped_ptr<UnrecoverableErrorHandler>().Pass(), + NULL, NULL); + manager->ConfigureSyncer( + CONFIGURE_REASON_NEW_CLIENT, + ModelTypeSet(PREFERENCES), + ModelTypeSet(), ModelTypeSet(), ModelTypeSet(), + ModelSafeRoutingInfo(), + base::Bind(&OnConfigDone, true), + base::Bind(&OnConfigDone, false)); + } + + void CreateEntry(UserShare* user_share, ModelType type, + const std::string& client_tag) { + WriteTransaction trans(FROM_HERE, user_share); + ReadNode type_root(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + type_root.InitByTagLookup(ModelTypeToRootTag(type))); + + WriteNode node(&trans); + EXPECT_EQ(WriteNode::INIT_SUCCESS, + node.InitUniqueByCreation(type, type_root, client_tag)); + } + + base::ScopedTempDir temp_dir_; + base::MessageLoop loop_; // Needed for WeakHandle +}; + +TEST_F(SyncBackupManagerTest, NormalizeAndPersist) { + scoped_ptr<SyncBackupManager> manager(new SyncBackupManager); + InitManager(manager.get()); + + CreateEntry(manager->GetUserShare(), PREFERENCES, "test"); + + { + // New entry is local and unsynced at first. + ReadTransaction trans(FROM_HERE, manager->GetUserShare()); + ReadNode pref(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + pref.InitByClientTagLookup(PREFERENCES, "test")); + EXPECT_FALSE(pref.GetEntry()->GetId().ServerKnows()); + EXPECT_TRUE(pref.GetEntry()->GetIsUnsynced()); + } + + manager->SaveChanges(); + + { + // New entry has server ID and unsynced bit is cleared after saving. + ReadTransaction trans(FROM_HERE, manager->GetUserShare()); + ReadNode pref(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + pref.InitByClientTagLookup(PREFERENCES, "test")); + EXPECT_TRUE(pref.GetEntry()->GetId().ServerKnows()); + EXPECT_FALSE(pref.GetEntry()->GetIsUnsynced()); + } + manager->ShutdownOnSyncThread(); + + // Reopen db to verify entry is persisted. + manager.reset(new SyncBackupManager); + InitManager(manager.get()); + { + ReadTransaction trans(FROM_HERE, manager->GetUserShare()); + ReadNode pref(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + pref.InitByClientTagLookup(PREFERENCES, "test")); + EXPECT_TRUE(pref.GetEntry()->GetId().ServerKnows()); + EXPECT_FALSE(pref.GetEntry()->GetIsUnsynced()); + } +} + +} // anonymous namespace + +} // namespace syncer + diff --git a/sync/internal_api/sync_rollback_manager.cc b/sync/internal_api/sync_rollback_manager.cc new file mode 100644 index 0000000..240218d --- /dev/null +++ b/sync/internal_api/sync_rollback_manager.cc @@ -0,0 +1,133 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sync/internal_api/sync_rollback_manager.h" + +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/read_node.h" +#include "sync/internal_api/public/read_transaction.h" +#include "sync/internal_api/public/util/syncer_error.h" +#include "sync/internal_api/public/write_transaction.h" +#include "sync/syncable/directory.h" +#include "sync/syncable/mutable_entry.h" + +namespace syncer { + +SyncRollbackManager::SyncRollbackManager() + : change_delegate_(NULL) { +} + +SyncRollbackManager::~SyncRollbackManager() { +} + +void SyncRollbackManager::Init( + const base::FilePath& database_location, + const WeakHandle<JsEventHandler>& event_handler, + const std::string& sync_server_and_path, + int sync_server_port, + bool use_ssl, + scoped_ptr<HttpPostProviderFactory> post_factory, + const std::vector<scoped_refptr<ModelSafeWorker> >& workers, + ExtensionsActivity* extensions_activity, + SyncManager::ChangeDelegate* change_delegate, + const SyncCredentials& credentials, + const std::string& invalidator_client_id, + const std::string& restored_key_for_bootstrapping, + const std::string& restored_keystore_key_for_bootstrapping, + InternalComponentsFactory* internal_components_factory, + Encryptor* encryptor, + scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler, + ReportUnrecoverableErrorFunction + report_unrecoverable_error_function, + CancelationSignal* cancelation_signal) { + SyncRollbackManagerBase::Init(database_location, event_handler, + sync_server_and_path, sync_server_port, + use_ssl, post_factory.Pass(), + workers, extensions_activity, change_delegate, + credentials, invalidator_client_id, + restored_key_for_bootstrapping, + restored_keystore_key_for_bootstrapping, + internal_components_factory, encryptor, + unrecoverable_error_handler.Pass(), + report_unrecoverable_error_function, + cancelation_signal); + + change_delegate_ = change_delegate; + + for (size_t i = 0; i < workers.size(); ++i) { + ModelSafeGroup group = workers[i]->GetModelSafeGroup(); + CHECK(workers_.find(group) == workers_.end()); + workers_[group] = workers[i]; + } + + rollback_ready_types_ = GetUserShare()->directory->InitialSyncEndedTypes(); + rollback_ready_types_.RetainAll(BackupTypes()); +} + +void SyncRollbackManager::StartSyncingNormally( + const ModelSafeRoutingInfo& routing_info){ + std::map<ModelType, syncable::Directory::Metahandles> to_delete; + + { + WriteTransaction trans(FROM_HERE, GetUserShare()); + syncable::Directory::Metahandles unsynced; + GetUserShare()->directory->GetUnsyncedMetaHandles(trans.GetWrappedTrans(), + &unsynced); + for (size_t i = 0; i < unsynced.size(); ++i) { + syncable::MutableEntry e(trans.GetWrappedWriteTrans(), + syncable::GET_BY_HANDLE, unsynced[i]); + if (!e.good() || e.GetIsDel() || e.GetId().ServerKnows()) + continue; + + // TODO(haitaol): roll back entries that are backed up but whose content + // is merged with local model during association. + + ModelType type = GetModelTypeFromSpecifics(e.GetSpecifics()); + if (!rollback_ready_types_.Has(type)) + continue; + + to_delete[type].push_back(unsynced[i]); + } + } + + for (std::map<ModelType, syncable::Directory::Metahandles>::iterator it = + to_delete.begin(); it != to_delete.end(); ++it) { + ModelSafeGroup group = routing_info.find(it->first)->second; + CHECK(workers_.find(group) != workers_.end()); + workers_[group]->DoWorkAndWaitUntilDone( + base::Bind(&SyncRollbackManager::DeleteOnWorkerThread, + base::Unretained(this), + it->first, it->second)); + } +} + +SyncerError SyncRollbackManager::DeleteOnWorkerThread( + ModelType type, std::vector<int64> handles) { + CHECK(change_delegate_); + + { + ChangeRecordList deletes; + WriteTransaction trans(FROM_HERE, GetUserShare()); + for (size_t i = 0; i < handles.size(); ++i) { + syncable::MutableEntry e(trans.GetWrappedWriteTrans(), + syncable::GET_BY_HANDLE, handles[i]); + if (!e.good() || e.GetIsDel()) + continue; + + ChangeRecord del; + del.action = ChangeRecord::ACTION_DELETE; + del.id = handles[i]; + del.specifics = e.GetSpecifics(); + deletes.push_back(del); + } + + change_delegate_->OnChangesApplied(type, 1, &trans, + MakeImmutable(&deletes)); + } + + change_delegate_->OnChangesComplete(type); + return SYNCER_OK; +} + +} // namespace syncer diff --git a/sync/internal_api/sync_rollback_manager.h b/sync/internal_api/sync_rollback_manager.h new file mode 100644 index 0000000..c8b8719 --- /dev/null +++ b/sync/internal_api/sync_rollback_manager.h @@ -0,0 +1,62 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SYNC_INTERNAL_API_SYNC_ROLLBACK_MANAGER_H_ +#define SYNC_INTERNAL_API_SYNC_ROLLBACK_MANAGER_H_ + +#include <string> +#include <vector> + +#include "sync/internal_api/sync_rollback_manager_base.h" + +namespace syncer { + +// SyncRollbackManager restores user's data to pre-sync state using backup +// DB created by SyncBackupManager. +class SYNC_EXPORT_PRIVATE SyncRollbackManager : public SyncRollbackManagerBase { + public: + SyncRollbackManager(); + virtual ~SyncRollbackManager(); + + // SyncManager implementation. + virtual void Init( + const base::FilePath& database_location, + const WeakHandle<JsEventHandler>& event_handler, + const std::string& sync_server_and_path, + int sync_server_port, + bool use_ssl, + scoped_ptr<HttpPostProviderFactory> post_factory, + const std::vector<scoped_refptr<ModelSafeWorker> >& workers, + ExtensionsActivity* extensions_activity, + SyncManager::ChangeDelegate* change_delegate, + const SyncCredentials& credentials, + const std::string& invalidator_client_id, + const std::string& restored_key_for_bootstrapping, + const std::string& restored_keystore_key_for_bootstrapping, + InternalComponentsFactory* internal_components_factory, + Encryptor* encryptor, + scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler, + ReportUnrecoverableErrorFunction + report_unrecoverable_error_function, + CancelationSignal* cancelation_signal) OVERRIDE; + virtual void StartSyncingNormally( + const ModelSafeRoutingInfo& routing_info) OVERRIDE; + + private: + // Deletes specified entries in local model. + SyncerError DeleteOnWorkerThread(ModelType type, std::vector<int64> handles); + + std::map<ModelSafeGroup, scoped_refptr<ModelSafeWorker> > workers_; + + SyncManager::ChangeDelegate* change_delegate_; + + // Types that can be rolled back. + ModelTypeSet rollback_ready_types_; + + DISALLOW_COPY_AND_ASSIGN(SyncRollbackManager); +}; + +} // namespace syncer + +#endif // SYNC_INTERNAL_API_SYNC_ROLLBACK_MANAGER_H_ diff --git a/sync/internal_api/sync_rollback_manager_base.cc b/sync/internal_api/sync_rollback_manager_base.cc new file mode 100644 index 0000000..98fcd48 --- /dev/null +++ b/sync/internal_api/sync_rollback_manager_base.cc @@ -0,0 +1,322 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sync/internal_api/sync_rollback_manager_base.h" + +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/internal_components_factory.h" +#include "sync/internal_api/public/read_node.h" +#include "sync/internal_api/public/read_transaction.h" +#include "sync/internal_api/public/util/syncer_error.h" +#include "sync/internal_api/public/write_transaction.h" +#include "sync/syncable/directory_backing_store.h" +#include "sync/syncable/mutable_entry.h" + +namespace { + +// Permanent bookmark folders as defined in bookmark_model_associator.cc. +const char kBookmarkBarTag[] = "bookmark_bar"; +const char kMobileBookmarksTag[] = "synced_bookmarks"; +const char kOtherBookmarksTag[] = "other_bookmarks"; + +} // anonymous namespace + +namespace syncer { + +SyncRollbackManagerBase::SyncRollbackManagerBase() + : report_unrecoverable_error_function_(NULL), + weak_ptr_factory_(this) { +} + +SyncRollbackManagerBase::~SyncRollbackManagerBase() { +} + +void SyncRollbackManagerBase::Init( + const base::FilePath& database_location, + const WeakHandle<JsEventHandler>& event_handler, + const std::string& sync_server_and_path, + int sync_server_port, + bool use_ssl, + scoped_ptr<HttpPostProviderFactory> post_factory, + const std::vector<scoped_refptr<ModelSafeWorker> >& workers, + ExtensionsActivity* extensions_activity, + SyncManager::ChangeDelegate* change_delegate, + const SyncCredentials& credentials, + const std::string& invalidator_client_id, + const std::string& restored_key_for_bootstrapping, + const std::string& restored_keystore_key_for_bootstrapping, + InternalComponentsFactory* internal_components_factory, + Encryptor* encryptor, + scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler, + ReportUnrecoverableErrorFunction + report_unrecoverable_error_function, + CancelationSignal* cancelation_signal) { + unrecoverable_error_handler_ = unrecoverable_error_handler.Pass(); + report_unrecoverable_error_function_ = report_unrecoverable_error_function; + + if (!InitBackupDB(database_location, internal_components_factory)) { + NotifyInitializationFailure(); + return; + } + + NotifyInitializationSuccess(); +} + +void SyncRollbackManagerBase::ThrowUnrecoverableError() { + NOTREACHED(); +} + +ModelTypeSet SyncRollbackManagerBase::InitialSyncEndedTypes() { + return share_.directory->InitialSyncEndedTypes(); +} + +ModelTypeSet SyncRollbackManagerBase::GetTypesWithEmptyProgressMarkerToken( + ModelTypeSet types) { + ModelTypeSet inited_types = share_.directory->InitialSyncEndedTypes(); + types.RemoveAll(inited_types); + return types; +} + +bool SyncRollbackManagerBase::PurgePartiallySyncedTypes() { + NOTREACHED(); + return true; +} + +void SyncRollbackManagerBase::UpdateCredentials( + const SyncCredentials& credentials) { + NOTREACHED(); +} + +void SyncRollbackManagerBase::StartSyncingNormally( + const ModelSafeRoutingInfo& routing_info){ +} + +void SyncRollbackManagerBase::ConfigureSyncer( + ConfigureReason reason, + ModelTypeSet to_download, + ModelTypeSet to_purge, + ModelTypeSet to_journal, + ModelTypeSet to_unapply, + const ModelSafeRoutingInfo& new_routing_info, + const base::Closure& ready_task, + const base::Closure& retry_task) { + DCHECK(to_purge.Empty()); + DCHECK(to_journal.Empty()); + DCHECK(to_unapply.Empty()); + for (ModelTypeSet::Iterator type = to_download.First(); + type.Good(); type.Inc()) { + if (InitTypeRootNode(type.Get())) { + if (type.Get() == BOOKMARKS) { + InitBookmarkFolder(kBookmarkBarTag); + InitBookmarkFolder(kMobileBookmarksTag); + InitBookmarkFolder(kOtherBookmarksTag); + } + } + } + + ready_task.Run(); +} + +void SyncRollbackManagerBase::OnInvalidatorStateChange(InvalidatorState state) { +} + +void SyncRollbackManagerBase::OnIncomingInvalidation( + const ObjectIdInvalidationMap& invalidation_map) { + NOTREACHED(); +} + +void SyncRollbackManagerBase::AddObserver(SyncManager::Observer* observer) { + observers_.AddObserver(observer); +} + +void SyncRollbackManagerBase::RemoveObserver(SyncManager::Observer* observer) { + observers_.RemoveObserver(observer); +} + +SyncStatus SyncRollbackManagerBase::GetDetailedStatus() const { + return SyncStatus(); +} + +void SyncRollbackManagerBase::SaveChanges() { +} + +void SyncRollbackManagerBase::ShutdownOnSyncThread() { + if (share_.directory) { + SaveChanges(); + share_.directory->Close(); + share_.directory.reset(); + } +} + +UserShare* SyncRollbackManagerBase::GetUserShare() { + return &share_; +} + +const std::string SyncRollbackManagerBase::cache_guid() { + return share_.directory->cache_guid(); +} + +bool SyncRollbackManagerBase::ReceivedExperiment(Experiments* experiments) { + return false; +} + +bool SyncRollbackManagerBase::HasUnsyncedItems() { + ReadTransaction trans(FROM_HERE, &share_); + syncable::Directory::Metahandles unsynced; + share_.directory->GetUnsyncedMetaHandles(trans.GetWrappedTrans(), &unsynced); + return !unsynced.empty(); +} + +SyncEncryptionHandler* SyncRollbackManagerBase::GetEncryptionHandler() { + return NULL; +} + +void SyncRollbackManagerBase::RefreshTypes(ModelTypeSet types) { + +} + +void SyncRollbackManagerBase::HandleTransactionCompleteChangeEvent( + ModelTypeSet models_with_changes) { +} + +ModelTypeSet SyncRollbackManagerBase::HandleTransactionEndingChangeEvent( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans) { + return ModelTypeSet(); +} + +void SyncRollbackManagerBase::HandleCalculateChangesChangeEventFromSyncApi( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans, + std::vector<int64>* entries_changed) { +} + +void SyncRollbackManagerBase::HandleCalculateChangesChangeEventFromSyncer( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans, + std::vector<int64>* entries_changed) { +} + +void SyncRollbackManagerBase::OnTransactionWrite( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + ModelTypeSet models_with_changes) { +} + +void SyncRollbackManagerBase::NotifyInitializationSuccess() { + FOR_EACH_OBSERVER( + SyncManager::Observer, observers_, + OnInitializationComplete( + MakeWeakHandle(base::WeakPtr<JsBackend>()), + MakeWeakHandle(base::WeakPtr<DataTypeDebugInfoListener>()), + true, InitialSyncEndedTypes())); +} + +void SyncRollbackManagerBase::NotifyInitializationFailure() { + FOR_EACH_OBSERVER( + SyncManager::Observer, observers_, + OnInitializationComplete( + MakeWeakHandle(base::WeakPtr<JsBackend>()), + MakeWeakHandle(base::WeakPtr<DataTypeDebugInfoListener>()), + false, InitialSyncEndedTypes())); +} + +std::string SyncRollbackManagerBase::GetOwnerName() const { + return ""; +} + +syncer::SyncCoreProxy* SyncRollbackManagerBase::GetSyncCoreProxy() { + return NULL; +} + +ScopedVector<syncer::ProtocolEvent> +SyncRollbackManagerBase::GetBufferedProtocolEvents() { + return ScopedVector<syncer::ProtocolEvent>().Pass(); +} + +scoped_ptr<base::ListValue> SyncRollbackManagerBase::GetAllNodesForType( + syncer::ModelType type) { + ReadTransaction trans(FROM_HERE, GetUserShare()); + scoped_ptr<base::ListValue> nodes( + trans.GetDirectory()->GetNodeDetailsForType(trans.GetWrappedTrans(), + type)); + return nodes.Pass(); +} + +bool SyncRollbackManagerBase::InitBackupDB( + const base::FilePath& sync_folder, + InternalComponentsFactory* internal_components_factory) { + base::FilePath backup_db_path = sync_folder.Append( + syncable::Directory::kSyncDatabaseFilename); + scoped_ptr<syncable::DirectoryBackingStore> backing_store = + internal_components_factory->BuildDirectoryBackingStore( + "backup", backup_db_path).Pass(); + + DCHECK(backing_store.get()); + share_.directory.reset( + new syncable::Directory( + backing_store.release(), + unrecoverable_error_handler_.get(), + report_unrecoverable_error_function_, + NULL, + NULL)); + return syncable::OPENED == + share_.directory->Open( + "backup", this, + MakeWeakHandle(weak_ptr_factory_.GetWeakPtr())); +} + +bool SyncRollbackManagerBase::InitTypeRootNode(ModelType type) { + WriteTransaction trans(FROM_HERE, &share_); + ReadNode root(&trans); + if (BaseNode::INIT_OK == root.InitByTagLookup(ModelTypeToRootTag(type))) + return true; + + syncable::MutableEntry entry(trans.GetWrappedWriteTrans(), + syncable::CREATE_NEW_UPDATE_ITEM, + syncable::Id::CreateFromServerId( + ModelTypeToString(type))); + if (!entry.good()) + return false; + + entry.PutParentId(syncable::Id()); + entry.PutBaseVersion(1); + entry.PutUniqueServerTag(ModelTypeToRootTag(type)); + entry.PutNonUniqueName(ModelTypeToString(type)); + entry.PutIsDel(false); + entry.PutIsDir(true); + + sync_pb::EntitySpecifics specifics; + AddDefaultFieldValue(type, &specifics); + entry.PutSpecifics(specifics); + + return true; +} + +void SyncRollbackManagerBase::InitBookmarkFolder(const std::string& folder) { + WriteTransaction trans(FROM_HERE, &share_); + syncable::Entry bookmark_root(trans.GetWrappedTrans(), + syncable::GET_BY_SERVER_TAG, + ModelTypeToRootTag(BOOKMARKS)); + if (!bookmark_root.good()) + return; + + syncable::MutableEntry entry(trans.GetWrappedWriteTrans(), + syncable::CREATE_NEW_UPDATE_ITEM, + syncable::Id::CreateFromServerId(folder)); + if (!entry.good()) + return; + + entry.PutParentId(bookmark_root.GetId()); + entry.PutBaseVersion(1); + entry.PutUniqueServerTag(folder); + entry.PutNonUniqueName(folder); + entry.PutIsDel(false); + entry.PutIsDir(true); + + sync_pb::EntitySpecifics specifics; + AddDefaultFieldValue(BOOKMARKS, &specifics); + entry.PutSpecifics(specifics); +} + +} // namespace syncer diff --git a/sync/internal_api/sync_rollback_manager_base.h b/sync/internal_api/sync_rollback_manager_base.h new file mode 100644 index 0000000..d6ba564 --- /dev/null +++ b/sync/internal_api/sync_rollback_manager_base.h @@ -0,0 +1,140 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SYNC_INTERNAL_API_SYNC_ROLLBACK_MANAGER_BASE_H_ +#define SYNC_INTERNAL_API_SYNC_ROLLBACK_MANAGER_BASE_H_ + +#include <string> +#include <vector> + +#include "sync/base/sync_export.h" +#include "sync/internal_api/public/http_post_provider_factory.h" +#include "sync/internal_api/public/sync_manager.h" +#include "sync/internal_api/public/user_share.h" +#include "sync/syncable/directory_change_delegate.h" +#include "sync/syncable/transaction_observer.h" + +namespace syncer { + +class WriteTransaction; + +// Base class of sync managers used for backup and rollback. Two major +// functions are: +// * Init(): load backup DB into sync directory. +// * ConfigureSyncer(): initialize permanent sync nodes (root, bookmark +// permanent folders) for configured type as needed. +// +// Most of other functions are no ops. +class SYNC_EXPORT_PRIVATE SyncRollbackManagerBase : + public SyncManager, + public syncable::DirectoryChangeDelegate, + public syncable::TransactionObserver { + public: + SyncRollbackManagerBase(); + virtual ~SyncRollbackManagerBase(); + + // SyncManager implementation. + virtual void Init( + const base::FilePath& database_location, + const WeakHandle<JsEventHandler>& event_handler, + const std::string& sync_server_and_path, + int sync_server_port, + bool use_ssl, + scoped_ptr<HttpPostProviderFactory> post_factory, + const std::vector<scoped_refptr<ModelSafeWorker> >& workers, + ExtensionsActivity* extensions_activity, + SyncManager::ChangeDelegate* change_delegate, + const SyncCredentials& credentials, + const std::string& invalidator_client_id, + const std::string& restored_key_for_bootstrapping, + const std::string& restored_keystore_key_for_bootstrapping, + InternalComponentsFactory* internal_components_factory, + Encryptor* encryptor, + scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler, + ReportUnrecoverableErrorFunction + report_unrecoverable_error_function, + CancelationSignal* cancelation_signal) OVERRIDE; + virtual void ThrowUnrecoverableError() OVERRIDE; + virtual ModelTypeSet InitialSyncEndedTypes() OVERRIDE; + virtual ModelTypeSet GetTypesWithEmptyProgressMarkerToken( + ModelTypeSet types) OVERRIDE; + virtual bool PurgePartiallySyncedTypes() OVERRIDE; + virtual void UpdateCredentials(const SyncCredentials& credentials) OVERRIDE; + virtual void StartSyncingNormally(const ModelSafeRoutingInfo& routing_info) + OVERRIDE; + virtual void ConfigureSyncer( + ConfigureReason reason, + ModelTypeSet to_download, + ModelTypeSet to_purge, + ModelTypeSet to_journal, + ModelTypeSet to_unapply, + const ModelSafeRoutingInfo& new_routing_info, + const base::Closure& ready_task, + const base::Closure& retry_task) OVERRIDE; + virtual void OnInvalidatorStateChange(InvalidatorState state) OVERRIDE; + virtual void OnIncomingInvalidation( + const ObjectIdInvalidationMap& invalidation_map) OVERRIDE; + virtual void AddObserver(SyncManager::Observer* observer) OVERRIDE; + virtual void RemoveObserver(SyncManager::Observer* observer) OVERRIDE; + virtual SyncStatus GetDetailedStatus() const OVERRIDE; + virtual void SaveChanges() OVERRIDE; + virtual void ShutdownOnSyncThread() OVERRIDE; + virtual UserShare* GetUserShare() OVERRIDE; + virtual const std::string cache_guid() OVERRIDE; + virtual bool ReceivedExperiment(Experiments* experiments) OVERRIDE; + virtual bool HasUnsyncedItems() OVERRIDE; + virtual SyncEncryptionHandler* GetEncryptionHandler() OVERRIDE; + virtual void RefreshTypes(ModelTypeSet types) OVERRIDE; + virtual std::string GetOwnerName() const OVERRIDE; + virtual SyncCoreProxy* GetSyncCoreProxy() OVERRIDE; + virtual ScopedVector<ProtocolEvent> GetBufferedProtocolEvents() + OVERRIDE; + virtual scoped_ptr<base::ListValue> GetAllNodesForType( + syncer::ModelType type) OVERRIDE; + + // DirectoryChangeDelegate implementation. + virtual void HandleTransactionCompleteChangeEvent( + ModelTypeSet models_with_changes) OVERRIDE; + virtual ModelTypeSet HandleTransactionEndingChangeEvent( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans) OVERRIDE; + virtual void HandleCalculateChangesChangeEventFromSyncApi( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans, + std::vector<int64>* entries_changed) OVERRIDE; + virtual void HandleCalculateChangesChangeEventFromSyncer( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + syncable::BaseTransaction* trans, + std::vector<int64>* entries_changed) OVERRIDE; + + // syncable::TransactionObserver implementation. + virtual void OnTransactionWrite( + const syncable::ImmutableWriteTransactionInfo& write_transaction_info, + ModelTypeSet models_with_changes) OVERRIDE; + + private: + void NotifyInitializationSuccess(); + void NotifyInitializationFailure(); + + bool InitBackupDB( + const base::FilePath& sync_folder, + InternalComponentsFactory* internal_components_factory); + + bool InitTypeRootNode(ModelType type); + void InitBookmarkFolder(const std::string& folder); + + UserShare share_; + ObserverList<SyncManager::Observer> observers_; + + scoped_ptr<UnrecoverableErrorHandler> unrecoverable_error_handler_; + ReportUnrecoverableErrorFunction report_unrecoverable_error_function_; + + base::WeakPtrFactory<SyncRollbackManagerBase> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(SyncRollbackManagerBase); +}; + +} // namespace syncer + +#endif // SYNC_INTERNAL_API_SYNC_ROLLBACK_MANAGER_BASE_H_ diff --git a/sync/internal_api/sync_rollback_manager_base_unittest.cc b/sync/internal_api/sync_rollback_manager_base_unittest.cc new file mode 100644 index 0000000..5bb1548 --- /dev/null +++ b/sync/internal_api/sync_rollback_manager_base_unittest.cc @@ -0,0 +1,70 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sync/internal_api/sync_rollback_manager_base.h" + +#include "base/bind.h" +#include "sync/internal_api/public/read_node.h" +#include "sync/internal_api/public/read_transaction.h" +#include "sync/internal_api/public/test/test_internal_components_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { + +namespace { + +void OnConfigDone(bool success) { + EXPECT_TRUE(success); +} + +class SyncRollbackManagerBaseTest : public testing::Test { + protected: + virtual void SetUp() OVERRIDE { + TestInternalComponentsFactory factory(InternalComponentsFactory::Switches(), + STORAGE_IN_MEMORY); + manager_.Init(base::FilePath(base::FilePath::kCurrentDirectory), + MakeWeakHandle(base::WeakPtr<JsEventHandler>()), + "", 0, true, scoped_ptr<HttpPostProviderFactory>().Pass(), + std::vector<scoped_refptr<ModelSafeWorker> >(), + NULL, NULL, SyncCredentials(), "", "", "", &factory, + NULL, scoped_ptr<UnrecoverableErrorHandler>().Pass(), + NULL, NULL); + } + + SyncRollbackManagerBase manager_; + base::MessageLoop loop_; // Needed for WeakHandle +}; + +TEST_F(SyncRollbackManagerBaseTest, InitTypeOnConfiguration) { + EXPECT_TRUE(manager_.InitialSyncEndedTypes().Empty()); + + manager_.ConfigureSyncer( + CONFIGURE_REASON_NEW_CLIENT, + ModelTypeSet(PREFERENCES, BOOKMARKS), + ModelTypeSet(), ModelTypeSet(), ModelTypeSet(), ModelSafeRoutingInfo(), + base::Bind(&OnConfigDone, true), + base::Bind(&OnConfigDone, false)); + + ReadTransaction trans(FROM_HERE, manager_.GetUserShare()); + ReadNode pref_root(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + pref_root.InitByTagLookup(ModelTypeToRootTag(PREFERENCES))); + + ReadNode bookmark_root(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + bookmark_root.InitByTagLookup(ModelTypeToRootTag(BOOKMARKS))); + ReadNode bookmark_bar(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + bookmark_bar.InitByTagLookup("bookmark_bar")); + ReadNode bookmark_mobile(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + bookmark_mobile.InitByTagLookup("synced_bookmarks")); + ReadNode bookmark_other(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + bookmark_other.InitByTagLookup("other_bookmarks")); +} + +} // anonymous namespace + +} // namespace syncer diff --git a/sync/internal_api/sync_rollback_manager_unittest.cc b/sync/internal_api/sync_rollback_manager_unittest.cc new file mode 100644 index 0000000..d2d00e4 --- /dev/null +++ b/sync/internal_api/sync_rollback_manager_unittest.cc @@ -0,0 +1,205 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sync/internal_api/sync_rollback_manager.h" + +#include "base/files/scoped_temp_dir.h" +#include "sync/internal_api/public/read_node.h" +#include "sync/internal_api/public/read_transaction.h" +#include "sync/internal_api/public/test/test_internal_components_factory.h" +#include "sync/internal_api/public/write_node.h" +#include "sync/internal_api/public/write_transaction.h" +#include "sync/internal_api/sync_backup_manager.h" +#include "sync/syncable/entry.h" +#include "sync/test/engine/fake_model_worker.h" +#include "sync/test/test_directory_backing_store.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::DoDefault; +using ::testing::Invoke; +using ::testing::WithArgs; + +namespace syncer { + +namespace { + +class TestChangeDelegate : public SyncManager::ChangeDelegate { + public: + TestChangeDelegate() { + ON_CALL(*this, OnChangesApplied(_, _, _, _)) + .WillByDefault( + WithArgs<3>(Invoke(this, + &TestChangeDelegate::VerifyDeletes))); + } + + void add_expected_delete(int64 v) { + expected_deletes_.insert(v); + } + + MOCK_METHOD4(OnChangesApplied, + void(ModelType model_type, + int64 model_version, + const BaseTransaction* trans, + const ImmutableChangeRecordList& changes)); + MOCK_METHOD1(OnChangesComplete, void(ModelType model_type)); + + private: + void VerifyDeletes(const ImmutableChangeRecordList& changes) { + std::set<int64> deleted; + for (size_t i = 0; i < changes.Get().size(); ++i) { + const ChangeRecord& change = (changes.Get())[i]; + EXPECT_EQ(ChangeRecord::ACTION_DELETE, change.action); + EXPECT_TRUE(deleted.find(change.id) == deleted.end()); + deleted.insert(change.id); + } + EXPECT_TRUE(expected_deletes_ == deleted); + } + + std::set<int64> expected_deletes_; +}; + +class SyncRollbackManagerTest : public testing::Test { + protected: + virtual void SetUp() OVERRIDE { + CHECK(temp_dir_.CreateUniqueTempDir()); + + worker_ = new FakeModelWorker(GROUP_UI); + } + + void OnConfigDone(bool success) { + EXPECT_TRUE(success); + } + + int64 CreateEntry(UserShare* user_share, ModelType type, + const std::string& client_tag) { + WriteTransaction trans(FROM_HERE, user_share); + ReadNode type_root(&trans); + EXPECT_EQ(BaseNode::INIT_OK, + type_root.InitByTagLookup(ModelTypeToRootTag(type))); + + WriteNode node(&trans); + EXPECT_EQ(WriteNode::INIT_SUCCESS, + node.InitUniqueByCreation(type, type_root, client_tag)); + return node.GetEntry()->GetMetahandle(); + } + + void InitManager(SyncManager* manager, ModelTypeSet types, + TestChangeDelegate* delegate) { + TestInternalComponentsFactory factory(InternalComponentsFactory::Switches(), + STORAGE_ON_DISK); + + manager->Init(temp_dir_.path(), + MakeWeakHandle(base::WeakPtr<JsEventHandler>()), + "", 0, true, scoped_ptr<HttpPostProviderFactory>().Pass(), + std::vector<scoped_refptr<ModelSafeWorker> >(1, + worker_.get()), + NULL, delegate, SyncCredentials(), "", "", "", &factory, + NULL, scoped_ptr<UnrecoverableErrorHandler>().Pass(), + NULL, NULL); + manager->ConfigureSyncer( + CONFIGURE_REASON_NEW_CLIENT, + types, + ModelTypeSet(), ModelTypeSet(), ModelTypeSet(), ModelSafeRoutingInfo(), + base::Bind(&SyncRollbackManagerTest::OnConfigDone, + base::Unretained(this), true), + base::Bind(&SyncRollbackManagerTest::OnConfigDone, + base::Unretained(this), false)); + } + + // Create and persist an entry by unique tag in DB. + void PrepopulateDb(ModelType type, const std::string& client_tag) { + SyncBackupManager backup_manager; + TestChangeDelegate delegate; + InitManager(&backup_manager, ModelTypeSet(type), &delegate); + CreateEntry(backup_manager.GetUserShare(), type, client_tag); + backup_manager.ShutdownOnSyncThread(); + } + + // Verify entry with |client_tag| exists in sync directory. + bool VerifyEntry(UserShare* user_share, ModelType type, + const std::string& client_tag) { + ReadTransaction trans(FROM_HERE, user_share); + ReadNode node(&trans); + return BaseNode::INIT_OK == node.InitByClientTagLookup(type, client_tag); + } + + base::ScopedTempDir temp_dir_; + scoped_refptr<ModelSafeWorker> worker_; + base::MessageLoop loop_; // Needed for WeakHandle +}; + +TEST_F(SyncRollbackManagerTest, RollbackBasic) { + PrepopulateDb(PREFERENCES, "pref1"); + + TestChangeDelegate delegate; + SyncRollbackManager rollback_manager; + InitManager(&rollback_manager, ModelTypeSet(PREFERENCES), &delegate); + + // Simulate a new entry added during type initialization. + int64 new_pref_id = + CreateEntry(rollback_manager.GetUserShare(), PREFERENCES, "pref2"); + + delegate.add_expected_delete(new_pref_id); + EXPECT_CALL(delegate, OnChangesApplied(_, _, _, _)) + .Times(1) + .WillOnce(DoDefault()); + EXPECT_CALL(delegate, OnChangesComplete(_)).Times(1); + + ModelSafeRoutingInfo routing_info; + routing_info[PREFERENCES] = GROUP_UI; + rollback_manager.StartSyncingNormally(routing_info); +} + +TEST_F(SyncRollbackManagerTest, NoRollbackOfTypesNotBackedUp) { + PrepopulateDb(PREFERENCES, "pref1"); + + TestChangeDelegate delegate; + SyncRollbackManager rollback_manager; + InitManager(&rollback_manager, ModelTypeSet(PREFERENCES, APPS), &delegate); + + // Simulate new entry added during type initialization. + int64 new_pref_id = + CreateEntry(rollback_manager.GetUserShare(), PREFERENCES, "pref2"); + CreateEntry(rollback_manager.GetUserShare(), APPS, "app1"); + + delegate.add_expected_delete(new_pref_id); + EXPECT_CALL(delegate, OnChangesApplied(_, _, _, _)) + .Times(1) + .WillOnce(DoDefault()); + EXPECT_CALL(delegate, OnChangesComplete(_)).Times(1); + + ModelSafeRoutingInfo routing_info; + routing_info[PREFERENCES] = GROUP_UI; + rollback_manager.StartSyncingNormally(routing_info); + + // APP entry is still valid. + EXPECT_TRUE(VerifyEntry(rollback_manager.GetUserShare(), APPS, "app1")); +} + +TEST_F(SyncRollbackManagerTest, BackupDbNotChangedOnAbort) { + PrepopulateDb(PREFERENCES, "pref1"); + + TestChangeDelegate delegate; + scoped_ptr<SyncRollbackManager> rollback_manager( + new SyncRollbackManager); + InitManager(rollback_manager.get(), ModelTypeSet(PREFERENCES), &delegate); + + // Simulate a new entry added during type initialization. + CreateEntry(rollback_manager->GetUserShare(), PREFERENCES, "pref2"); + + // Manager was shut down before sync starts. + rollback_manager->ShutdownOnSyncThread(); + + // Verify new entry was not persisted. + rollback_manager.reset(new SyncRollbackManager); + InitManager(rollback_manager.get(), ModelTypeSet(PREFERENCES), &delegate); + EXPECT_FALSE(VerifyEntry(rollback_manager->GetUserShare(), PREFERENCES, + "pref2")); +} + +} // anonymous namespace + +} // namespace syncer diff --git a/sync/sync_internal_api.gypi b/sync/sync_internal_api.gypi index 3a572df..2e677fe 100644 --- a/sync/sync_internal_api.gypi +++ b/sync/sync_internal_api.gypi @@ -122,6 +122,8 @@ 'internal_api/public/write_transaction.h', 'internal_api/read_node.cc', 'internal_api/read_transaction.cc', + 'internal_api/sync_backup_manager.cc', + 'internal_api/sync_backup_manager.h', 'internal_api/sync_core.cc', 'internal_api/sync_core.h', 'internal_api/sync_core_proxy.cc', @@ -132,6 +134,10 @@ 'internal_api/sync_manager_factory.cc', 'internal_api/sync_manager_impl.cc', 'internal_api/sync_manager_impl.h', + 'internal_api/sync_rollback_manager_base.cc', + 'internal_api/sync_rollback_manager_base.h', + 'internal_api/sync_rollback_manager.cc', + 'internal_api/sync_rollback_manager.h', 'internal_api/syncapi_internal.cc', 'internal_api/syncapi_internal.h', 'internal_api/syncapi_server_connection_manager.cc', diff --git a/sync/sync_tests.gypi b/sync/sync_tests.gypi index 0db31fc..2eefc68 100644 --- a/sync/sync_tests.gypi +++ b/sync/sync_tests.gypi @@ -420,9 +420,12 @@ 'internal_api/protocol_event_buffer_unittest.cc', 'internal_api/public/change_record_unittest.cc', 'internal_api/public/sessions/sync_session_snapshot_unittest.cc', + 'internal_api/sync_backup_manager_unittest.cc', 'internal_api/sync_core_proxy_impl_unittest.cc', 'internal_api/sync_encryption_handler_impl_unittest.cc', - 'internal_api/sync_manager_impl_unittest.cc', + 'internal_api/sync_manager_impl_unittest.cc', + 'internal_api/sync_rollback_manager_base_unittest.cc', + 'internal_api/sync_rollback_manager_unittest.cc', 'internal_api/syncapi_server_connection_manager_unittest.cc', ], 'conditions': [ diff --git a/sync/syncable/model_type.cc b/sync/syncable/model_type.cc index 8e96bfc..ecb54d6 100644 --- a/sync/syncable/model_type.cc +++ b/sync/syncable/model_type.cc @@ -438,6 +438,20 @@ ModelTypeSet PriorityCoreTypes() { return result; } +ModelTypeSet BackupTypes() { + ModelTypeSet result; + result.Put(BOOKMARKS); + result.Put(PREFERENCES); + result.Put(THEMES); + result.Put(EXTENSIONS); + result.Put(SEARCH_ENGINES); + result.Put(APPS); + result.Put(APP_SETTINGS); + result.Put(EXTENSION_SETTINGS); + result.Put(PRIORITY_PREFERENCES); + return result; +} + const char* ModelTypeToString(ModelType model_type) { // This is used in serialization routines as well as for displaying debug // information. Do not attempt to change these string values unless you know |