普通文本  |  268行  |  9.89 KB

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