// 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