blob: 067fccd29b03d54a1aa87347c4f40d5d931dfc87 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <vector>
#include "base/containers/circular_deque.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/test/integration/bookmarks_helper.h"
#include "chrome/browser/sync/test/integration/migration_waiter.h"
#include "chrome/browser/sync/test/integration/migration_watcher.h"
#include "chrome/browser/sync/test/integration/preferences_helper.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/common/pref_names.h"
#include "components/sync/base/features.h"
#include "components/sync/service/sync_service_impl.h"
#include "content/public/test/browser_test.h"
using bookmarks_helper::AddURL;
using bookmarks_helper::IndexedURL;
using bookmarks_helper::IndexedURLTitle;
using preferences_helper::BooleanPrefMatches;
using preferences_helper::ChangeBooleanPref;
namespace {
// Utility functions to make a data type set out of a small number of
// data types.
// TODO(crbug.com/40911681): MakeSet() seems pretty redundant, can be replaced
// with its body.
syncer::DataTypeSet MakeSet(syncer::DataType type) {
return {type};
}
syncer::DataTypeSet MakeSet(syncer::DataType type1, syncer::DataType type2) {
return {type1, type2};
}
// An ordered list of data types sets to migrate. Used by
// RunMigrationTest().
using MigrationList = base::circular_deque<syncer::DataTypeSet>;
// Utility functions to make a MigrationList out of a small number of
// data types / data type sets.
MigrationList MakeList(syncer::DataTypeSet data_types) {
return MigrationList(1, data_types);
}
MigrationList MakeList(syncer::DataTypeSet data_types1,
syncer::DataTypeSet data_types2) {
MigrationList migration_list;
migration_list.push_back(data_types1);
migration_list.push_back(data_types2);
return migration_list;
}
MigrationList MakeList(syncer::DataType type) {
return MakeList(MakeSet(type));
}
MigrationList MakeList(syncer::DataType type1, syncer::DataType type2) {
return MakeList(MakeSet(type1), MakeSet(type2));
}
class MigrationTest : public SyncTest {
public:
explicit MigrationTest(TestType test_type) : SyncTest(test_type) {}
MigrationTest(const MigrationTest&) = delete;
MigrationTest& operator=(const MigrationTest&) = delete;
~MigrationTest() override = default;
enum TriggerMethod { MODIFY_PREF, MODIFY_BOOKMARK, TRIGGER_REFRESH };
// Initialize all MigrationWatchers. This helps ensure that all migration
// events are captured, even if they were to occur before a test calls
// AwaitMigration for a specific profile.
void Initialize() {
for (int i = 0; i < num_clients(); ++i) {
migration_watchers_.push_back(
std::make_unique<MigrationWatcher>(GetClient(i)));
}
}
syncer::DataTypeSet GetPreferredDataTypes() {
// SyncServiceImpl must already have been created before we can call
// GetPreferredDataTypes().
DCHECK(GetSyncService(0));
syncer::DataTypeSet preferred_data_types =
GetSyncService(0)->GetPreferredDataTypes();
// Make sure all clients have the same preferred data types.
for (int i = 1; i < num_clients(); ++i) {
const syncer::DataTypeSet other_preferred_data_types =
GetSyncService(i)->GetPreferredDataTypes();
EXPECT_EQ(other_preferred_data_types, preferred_data_types);
}
preferred_data_types.RetainAll(syncer::ProtocolTypes());
// Supervised user data types will be "unready" during this test, so we
// should not request that they be migrated.
preferred_data_types.Remove(syncer::SUPERVISED_USER_SETTINGS);
// Autofill wallet and plus address will be unready during this test, so we
// should not request that it be migrated.
preferred_data_types.RemoveAll(
{syncer::AUTOFILL_WALLET_DATA, syncer::AUTOFILL_WALLET_METADATA,
syncer::AUTOFILL_WALLET_OFFER, syncer::PLUS_ADDRESS,
syncer::PLUS_ADDRESS_SETTING});
// ARC package will be unready during this test, so we should not request
// that it be migrated.
preferred_data_types.Remove(syncer::ARC_PACKAGE);
// Doesn't make sense to migrate commit only types.
preferred_data_types.RemoveAll(syncer::CommitOnlyTypes());
return preferred_data_types;
}
// Returns a MigrationList with every enabled data type in its own
// set.
MigrationList GetPreferredDataTypesList() {
MigrationList migration_list;
const syncer::DataTypeSet preferred_data_types = GetPreferredDataTypes();
for (syncer::DataType type : preferred_data_types) {
migration_list.push_back(MakeSet(type));
}
return migration_list;
}
// Trigger a migration for the given types with the given method.
void TriggerMigration(syncer::DataTypeSet data_types,
TriggerMethod trigger_method) {
switch (trigger_method) {
case MODIFY_PREF:
// Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
// notification to happen (since model association on a
// boolean pref clobbers the local value), so it doesn't work
// for anything but single-client tests.
ASSERT_EQ(1, num_clients());
ChangeBooleanPref(0, prefs::kShowHomeButton);
break;
case MODIFY_BOOKMARK:
ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
break;
case TRIGGER_REFRESH:
TriggerSyncForDataTypes(/*index=*/0, data_types);
break;
default:
ADD_FAILURE();
}
}
// Block until all clients have completed migration for the given
// types.
void AwaitMigration(syncer::DataTypeSet migrate_types) {
for (int i = 0; i < num_clients(); ++i) {
ASSERT_TRUE(
MigrationWaiter(migrate_types, migration_watchers_[i].get()).Wait());
}
}
// Makes sure migration works with the given migration list and
// trigger method.
void RunMigrationTest(const MigrationList& migration_list,
TriggerMethod trigger_method) {
// Make sure migration hasn't been triggered prematurely.
for (int i = 0; i < num_clients(); ++i) {
ASSERT_TRUE(migration_watchers_[i]->GetMigratedTypes().empty());
}
// Phase 1: Trigger the migrations on the server.
for (const syncer::DataTypeSet& data_types : migration_list) {
TriggerMigrationDoneError(data_types);
}
// Phase 2: Trigger each migration individually and wait for it to
// complete. (Multiple migrations may be handled by each
// migration cycle, but there's no guarantee of that, so we have
// to trigger each migration individually.)
for (const syncer::DataTypeSet& data_types : migration_list) {
TriggerMigration(data_types, trigger_method);
AwaitMigration(data_types);
}
// Phase 3: Wait for all clients to catch up.
ASSERT_TRUE(AwaitQuiescence());
}
private:
// Used to keep track of the migration progress for each sync client.
std::vector<std::unique_ptr<MigrationWatcher>> migration_watchers_;
};
class MigrationSingleClientTest : public MigrationTest {
public:
MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT) {}
MigrationSingleClientTest(const MigrationSingleClientTest&) = delete;
MigrationSingleClientTest& operator=(const MigrationSingleClientTest&) =
delete;
~MigrationSingleClientTest() override = default;
void RunSingleClientMigrationTest(const MigrationList& migration_list,
TriggerMethod trigger_method) {
ASSERT_TRUE(SetupSync());
Initialize();
RunMigrationTest(migration_list, trigger_method);
}
};
// The simplest possible migration tests -- a single data type.
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_PREF);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_BOOKMARK);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyTriggerRefresh) {
RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), TRIGGER_REFRESH);
}
// Nigori is handled specially, so we test that separately.
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), TRIGGER_REFRESH);
}
// A little more complicated -- two data types.
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsIndividually) {
RunSingleClientMigrationTest(MakeList(syncer::BOOKMARKS, syncer::PREFERENCES),
MODIFY_PREF);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
RunSingleClientMigrationTest(
MakeList(MakeSet(syncer::BOOKMARKS, syncer::PREFERENCES)),
MODIFY_BOOKMARK);
}
// Two data types with one being nigori.
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriIndividiaully) {
RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES, syncer::NIGORI),
TRIGGER_REFRESH);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
RunSingleClientMigrationTest(
MakeList(MakeSet(syncer::PREFERENCES, syncer::NIGORI)), MODIFY_PREF);
}
// The whole shebang -- all data types.
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesIndividually) {
ASSERT_TRUE(SetupClients());
RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
AllTypesIndividuallyTriggerRefresh) {
ASSERT_TRUE(SetupClients());
RunSingleClientMigrationTest(GetPreferredDataTypesList(), TRIGGER_REFRESH);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
ASSERT_TRUE(SetupClients());
RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()), MODIFY_PREF);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
AllTypesAtOnceTriggerRefresh) {
ASSERT_TRUE(SetupClients());
RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
TRIGGER_REFRESH);
}
// All data types plus nigori.
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
AllTypesWithNigoriIndividually) {
ASSERT_TRUE(SetupClients());
MigrationList migration_list = GetPreferredDataTypesList();
migration_list.push_front(MakeSet(syncer::NIGORI));
RunSingleClientMigrationTest(migration_list, MODIFY_BOOKMARK);
}
IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesWithNigoriAtOnce) {
ASSERT_TRUE(SetupClients());
syncer::DataTypeSet all_types = GetPreferredDataTypes();
all_types.Put(syncer::NIGORI);
RunSingleClientMigrationTest(MakeList(all_types), MODIFY_PREF);
}
class MigrationTwoClientTest : public MigrationTest {
public:
MigrationTwoClientTest() : MigrationTest(TWO_CLIENT) {}
MigrationTwoClientTest(const MigrationTwoClientTest&) = delete;
MigrationTwoClientTest& operator=(const MigrationTwoClientTest&) = delete;
~MigrationTwoClientTest() override = default;
// Helper function that verifies that preferences sync still works.
void VerifyPrefSync() {
ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
ChangeBooleanPref(0, prefs::kShowHomeButton);
ASSERT_TRUE(BooleanPrefMatchChecker(prefs::kShowHomeButton).Wait());
}
void RunTwoClientMigrationTest(const MigrationList& migration_list,
TriggerMethod trigger_method) {
ASSERT_TRUE(SetupSync());
Initialize();
// Make sure pref sync works before running the migration test.
VerifyPrefSync();
RunMigrationTest(migration_list, trigger_method);
// Make sure pref sync still works after running the migration
// test.
VerifyPrefSync();
}
};
// Easiest possible test of migration errors: triggers a server
// migration on one datatype, then modifies some other datatype.
IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigratePrefsThenModifyBookmark) {
RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_BOOKMARK);
}
// Triggers a server migration on two datatypes, then makes a local
// modification to one of them.
IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
MigratePrefsAndBookmarksThenModifyBookmark) {
RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES, syncer::BOOKMARKS),
MODIFY_BOOKMARK);
}
// Migrate every datatype in sequence; the catch being that the server
// will only tell the client about the migrations one at a time.
IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigrationHellWithoutNigori) {
ASSERT_TRUE(SetupClients());
MigrationList migration_list = GetPreferredDataTypesList();
// Let the first nudge be a datatype that's neither prefs nor bookmarks.
migration_list.push_front(MakeSet(syncer::THEMES));
ASSERT_EQ(MakeSet(syncer::NIGORI), migration_list.back());
migration_list.pop_back();
RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
}
IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigrationHellWithNigori) {
ASSERT_TRUE(SetupClients());
MigrationList migration_list = GetPreferredDataTypesList();
// Let the first nudge be a datatype that's neither prefs nor bookmarks.
migration_list.push_front(MakeSet(syncer::THEMES));
ASSERT_EQ(MakeSet(syncer::NIGORI), migration_list.back());
RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
}
} // namespace