| // Copyright (c) 2011 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 "chrome/browser/sync/backend_migrator.h" |
| |
| #include "chrome/browser/sync/glue/data_type_manager_mock.h" |
| #include "chrome/browser/sync/profile_sync_service_mock.h" |
| #include "chrome/browser/sync/sessions/session_state.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| using ::testing::Eq; |
| using ::testing::Mock; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| using ::testing::SetArgumentPointee; |
| |
| namespace browser_sync { |
| |
| using sessions::ErrorCounters; |
| using sessions::SyncerStatus; |
| using sessions::SyncSessionSnapshot; |
| |
| class BackendMigratorTest : public testing::Test { |
| public: |
| BackendMigratorTest() { } |
| virtual ~BackendMigratorTest() { } |
| |
| virtual void SetUp() { |
| Mock::VerifyAndClear(manager()); |
| Mock::VerifyAndClear(&service_); |
| preferred_types_.insert(syncable::BOOKMARKS); |
| preferred_types_.insert(syncable::PREFERENCES); |
| preferred_types_.insert(syncable::AUTOFILL); |
| |
| ON_CALL(service_, GetPreferredDataTypes(_)). |
| WillByDefault(SetArgumentPointee<0>(preferred_types_)); |
| } |
| |
| void ReturnEmptyProgressMarkersInSnapshot() { |
| ReturnNonEmptyProgressMarkersInSnapshot(syncable::ModelTypeSet()); |
| } |
| |
| void ReturnNonEmptyProgressMarkersInSnapshot( |
| const syncable::ModelTypeSet& for_types) { |
| std::string download_progress_markers[syncable::MODEL_TYPE_COUNT]; |
| for (syncable::ModelTypeSet::const_iterator it = for_types.begin(); |
| it != for_types.end(); ++it) { |
| download_progress_markers[*it] = "foobar"; |
| } |
| |
| snap_.reset(new SyncSessionSnapshot(SyncerStatus(), ErrorCounters(), |
| 0, false, syncable::ModelTypeBitSet(), download_progress_markers, |
| false, false, 0, 0, false, sessions::SyncSourceInfo())); |
| EXPECT_CALL(service_, GetLastSessionSnapshot()) |
| .WillOnce(Return(snap_.get())); |
| } |
| |
| void SendConfigureDone(DataTypeManager::ConfigureResult result, |
| const syncable::ModelTypeSet& types) { |
| DataTypeManager::ConfigureResultWithErrorLocation result_with_location( |
| result, FROM_HERE, types); |
| NotificationService::current()->Notify( |
| NotificationType::SYNC_CONFIGURE_DONE, |
| Source<DataTypeManager>(&manager_), |
| Details<DataTypeManager::ConfigureResultWithErrorLocation>( |
| &result_with_location)); |
| } |
| |
| ProfileSyncService* service() { return &service_; } |
| DataTypeManagerMock* manager() { return &manager_; } |
| const syncable::ModelTypeSet& preferred_types() { return preferred_types_; } |
| void RemovePreferredType(syncable::ModelType type) { |
| preferred_types_.erase(type); |
| Mock::VerifyAndClear(&service_); |
| ON_CALL(service_, GetPreferredDataTypes(_)). |
| WillByDefault(SetArgumentPointee<0>(preferred_types_)); |
| } |
| private: |
| scoped_ptr<SyncSessionSnapshot> snap_; |
| syncable::ModelTypeSet preferred_types_; |
| NiceMock<ProfileSyncServiceMock> service_; |
| NiceMock<DataTypeManagerMock> manager_; |
| }; |
| |
| // Test that in the normal case a migration does transition through each state |
| // and wind up back in IDLE. |
| TEST_F(BackendMigratorTest, Sanity) { |
| BackendMigrator migrator(service(), manager()); |
| syncable::ModelTypeSet to_migrate, difference; |
| to_migrate.insert(syncable::PREFERENCES); |
| difference.insert(syncable::AUTOFILL); |
| difference.insert(syncable::BOOKMARKS); |
| |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(_)); |
| |
| migrator.MigrateTypes(to_migrate); |
| EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state()); |
| |
| SendConfigureDone(DataTypeManager::OK, difference); |
| EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state()); |
| |
| ReturnEmptyProgressMarkersInSnapshot(); |
| EXPECT_CALL(*manager(), Configure(preferred_types())); |
| migrator.OnStateChanged(); |
| EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state()); |
| |
| SendConfigureDone(DataTypeManager::OK, preferred_types()); |
| EXPECT_EQ(BackendMigrator::IDLE, migrator.state()); |
| } |
| |
| // Test that the migrator waits for the data type manager to be idle before |
| // starting a migration. |
| TEST_F(BackendMigratorTest, WaitToStart) { |
| BackendMigrator migrator(service(), manager()); |
| syncable::ModelTypeSet to_migrate; |
| to_migrate.insert(syncable::PREFERENCES); |
| |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURING)); |
| EXPECT_CALL(*manager(), Configure(_)).Times(0); |
| migrator.MigrateTypes(to_migrate); |
| EXPECT_EQ(BackendMigrator::WAITING_TO_START, migrator.state()); |
| |
| Mock::VerifyAndClearExpectations(manager()); |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(_)); |
| SendConfigureDone(DataTypeManager::OK, syncable::ModelTypeSet()); |
| |
| EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state()); |
| } |
| |
| // Test that the migrator can cope with a migration request while a migration |
| // is in progress. |
| TEST_F(BackendMigratorTest, RestartMigration) { |
| BackendMigrator migrator(service(), manager()); |
| syncable::ModelTypeSet to_migrate1, to_migrate2, bookmarks; |
| to_migrate1.insert(syncable::PREFERENCES); |
| to_migrate2.insert(syncable::AUTOFILL); |
| bookmarks.insert(syncable::BOOKMARKS); |
| |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(_)).Times(1); |
| migrator.MigrateTypes(to_migrate1); |
| |
| EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state()); |
| migrator.MigrateTypes(to_migrate2); |
| |
| syncable::ModelTypeSet difference1; |
| std::set_difference(preferred_types().begin(), preferred_types().end(), |
| to_migrate1.begin(), to_migrate1.end(), |
| std::inserter(difference1, difference1.end())); |
| |
| Mock::VerifyAndClearExpectations(manager()); |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(bookmarks)); |
| SendConfigureDone(DataTypeManager::OK, difference1); |
| EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state()); |
| |
| SendConfigureDone(DataTypeManager::OK, bookmarks); |
| EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state()); |
| } |
| |
| // Test that an external invocation of Configure(...) during a migration results |
| // in a migration reattempt. |
| TEST_F(BackendMigratorTest, InterruptedWhileDisablingTypes) { |
| BackendMigrator migrator(service(), manager()); |
| syncable::ModelTypeSet to_migrate; |
| syncable::ModelTypeSet difference; |
| to_migrate.insert(syncable::PREFERENCES); |
| difference.insert(syncable::AUTOFILL); |
| difference.insert(syncable::BOOKMARKS); |
| |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(difference)); |
| migrator.MigrateTypes(to_migrate); |
| EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state()); |
| |
| Mock::VerifyAndClearExpectations(manager()); |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(difference)); |
| SendConfigureDone(DataTypeManager::OK, preferred_types()); |
| |
| EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state()); |
| } |
| |
| // Test that spurious OnStateChanged events don't confuse the migrator while |
| // it's waiting for disabled types to have been purged from the sync db. |
| TEST_F(BackendMigratorTest, WaitingForPurge) { |
| BackendMigrator migrator(service(), manager()); |
| syncable::ModelTypeSet to_migrate, difference; |
| to_migrate.insert(syncable::PREFERENCES); |
| to_migrate.insert(syncable::AUTOFILL); |
| difference.insert(syncable::BOOKMARKS); |
| |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(_)); |
| migrator.MigrateTypes(to_migrate); |
| SendConfigureDone(DataTypeManager::OK, difference); |
| EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state()); |
| |
| ReturnNonEmptyProgressMarkersInSnapshot(to_migrate); |
| migrator.OnStateChanged(); |
| EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state()); |
| |
| syncable::ModelTypeSet prefs; |
| prefs.insert(syncable::PREFERENCES); |
| ReturnNonEmptyProgressMarkersInSnapshot(prefs); |
| migrator.OnStateChanged(); |
| EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state()); |
| |
| syncable::ModelTypeSet bookmarks; |
| bookmarks.insert(syncable::BOOKMARKS); |
| ReturnNonEmptyProgressMarkersInSnapshot(bookmarks); |
| EXPECT_CALL(*manager(), Configure(preferred_types())); |
| migrator.OnStateChanged(); |
| EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state()); |
| } |
| |
| TEST_F(BackendMigratorTest, MigratedTypeDisabledByUserDuringMigration) { |
| BackendMigrator migrator(service(), manager()); |
| syncable::ModelTypeSet to_migrate; |
| to_migrate.insert(syncable::PREFERENCES); |
| |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(_)); |
| migrator.MigrateTypes(to_migrate); |
| |
| RemovePreferredType(syncable::PREFERENCES); |
| SendConfigureDone(DataTypeManager::OK, preferred_types()); |
| EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state()); |
| |
| Mock::VerifyAndClearExpectations(manager()); |
| ReturnEmptyProgressMarkersInSnapshot(); |
| EXPECT_CALL(*manager(), Configure(preferred_types())); |
| migrator.OnStateChanged(); |
| |
| EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state()); |
| SendConfigureDone(DataTypeManager::OK, preferred_types()); |
| EXPECT_EQ(BackendMigrator::IDLE, migrator.state()); |
| } |
| |
| TEST_F(BackendMigratorTest, ConfigureFailure) { |
| BackendMigrator migrator(service(), manager()); |
| syncable::ModelTypeSet to_migrate; |
| to_migrate.insert(syncable::PREFERENCES); |
| |
| EXPECT_CALL(*manager(), state()) |
| .WillOnce(Return(DataTypeManager::CONFIGURED)); |
| EXPECT_CALL(*manager(), Configure(_)).Times(1); |
| migrator.MigrateTypes(to_migrate); |
| SendConfigureDone(DataTypeManager::ABORTED, syncable::ModelTypeSet()); |
| EXPECT_EQ(BackendMigrator::IDLE, migrator.state()); |
| } |
| |
| }; // namespace browser_sync |