1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/compiler_specific.h"
6 #include "base/memory/scoped_vector.h"
7 #include "base/prefs/scoped_user_pref_update.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/sync/backend_migrator.h"
10 #include "chrome/browser/sync/profile_sync_service.h"
11 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
12 #include "chrome/browser/sync/test/integration/preferences_helper.h"
13 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
14 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
15 #include "chrome/browser/sync/test/integration/sync_test.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "components/translate/core/browser/translate_prefs.h"
20 using bookmarks_helper::AddURL;
21 using bookmarks_helper::IndexedURL;
22 using bookmarks_helper::IndexedURLTitle;
24 using preferences_helper::BooleanPrefMatches;
25 using preferences_helper::ChangeBooleanPref;
29 // Utility functions to make a model type set out of a small number of
32 syncer::ModelTypeSet MakeSet(syncer::ModelType type) {
33 return syncer::ModelTypeSet(type);
36 syncer::ModelTypeSet MakeSet(syncer::ModelType type1,
37 syncer::ModelType type2) {
38 return syncer::ModelTypeSet(type1, type2);
41 // An ordered list of model types sets to migrate. Used by
42 // RunMigrationTest().
43 typedef std::deque<syncer::ModelTypeSet> MigrationList;
45 // Utility functions to make a MigrationList out of a small number of
46 // model types / model type sets.
48 MigrationList MakeList(syncer::ModelTypeSet model_types) {
49 return MigrationList(1, model_types);
52 MigrationList MakeList(syncer::ModelTypeSet model_types1,
53 syncer::ModelTypeSet model_types2) {
54 MigrationList migration_list;
55 migration_list.push_back(model_types1);
56 migration_list.push_back(model_types2);
57 return migration_list;
60 MigrationList MakeList(syncer::ModelType type) {
61 return MakeList(MakeSet(type));
64 MigrationList MakeList(syncer::ModelType type1,
65 syncer::ModelType type2) {
66 return MakeList(MakeSet(type1), MakeSet(type2));
69 // Helper class that checks if the sync backend has successfully completed
70 // migration for a set of data types.
71 class MigrationChecker : public SingleClientStatusChangeChecker,
72 public browser_sync::MigrationObserver {
74 explicit MigrationChecker(ProfileSyncServiceHarness* harness)
75 : SingleClientStatusChangeChecker(harness->service()),
78 browser_sync::BackendMigrator* migrator =
79 harness_->service()->GetBackendMigratorForTest();
80 // PSS must have a migrator after sync is setup and initial data type
81 // configuration is complete.
83 migrator->AddMigrationObserver(this);
86 virtual ~MigrationChecker() {}
88 // Returns true when sync reports that there is no pending migration, and
89 // migration is complete for all data types in |expected_types_|.
90 virtual bool IsExitConditionSatisfied() OVERRIDE {
91 DCHECK(!expected_types_.Empty());
92 bool all_expected_types_migrated = migrated_types_.HasAll(expected_types_);
93 DVLOG(1) << harness_->profile_debug_name() << ": Migrated types "
94 << syncer::ModelTypeSetToString(migrated_types_)
95 << (all_expected_types_migrated ? " contains " :
97 << syncer::ModelTypeSetToString(expected_types_);
98 return all_expected_types_migrated &&
99 !HasPendingBackendMigration();
102 virtual std::string GetDebugMessage() const OVERRIDE {
103 return "Waiting to migrate (" + ModelTypeSetToString(expected_types_) + ")";
106 bool HasPendingBackendMigration() const {
107 browser_sync::BackendMigrator* migrator =
108 harness_->service()->GetBackendMigratorForTest();
109 return migrator && migrator->state() != browser_sync::BackendMigrator::IDLE;
112 void set_expected_types(syncer::ModelTypeSet expected_types) {
113 expected_types_ = expected_types;
116 syncer::ModelTypeSet migrated_types() const {
117 return migrated_types_;
120 virtual void OnMigrationStateChange() OVERRIDE {
121 if (HasPendingBackendMigration()) {
122 // A new bunch of data types are in the process of being migrated. Merge
123 // them into |pending_types_|.
124 pending_types_.PutAll(
125 harness_->service()->GetBackendMigratorForTest()->
126 GetPendingMigrationTypesForTest());
127 DVLOG(1) << harness_->profile_debug_name()
128 << ": new pending migration types "
129 << syncer::ModelTypeSetToString(pending_types_);
131 // Migration just finished for a bunch of data types. Merge them into
132 // |migrated_types_|.
133 migrated_types_.PutAll(pending_types_);
134 pending_types_.Clear();
135 DVLOG(1) << harness_->profile_debug_name() << ": new migrated types "
136 << syncer::ModelTypeSetToString(migrated_types_);
139 // Manually trigger a check of the exit condition.
140 if (!expected_types_.Empty())
145 // The sync client for which migration is being verified.
146 ProfileSyncServiceHarness* harness_;
148 // The set of data types that are expected to eventually undergo migration.
149 syncer::ModelTypeSet expected_types_;
151 // The set of data types currently undergoing migration.
152 syncer::ModelTypeSet pending_types_;
154 // The set of data types for which migration is complete. Accumulated by
155 // successive calls to OnMigrationStateChanged.
156 syncer::ModelTypeSet migrated_types_;
158 DISALLOW_COPY_AND_ASSIGN(MigrationChecker);
161 class MigrationTest : public SyncTest {
163 explicit MigrationTest(TestType test_type) : SyncTest(test_type) {}
164 virtual ~MigrationTest() {}
166 enum TriggerMethod { MODIFY_PREF, MODIFY_BOOKMARK, TRIGGER_NOTIFICATION };
168 // Set up sync for all profiles and initialize all MigrationCheckers. This
169 // helps ensure that all migration events are captured, even if they were to
170 // occur before a test calls AwaitMigration for a specific profile.
171 virtual bool SetupSync() OVERRIDE {
172 if (!SyncTest::SetupSync())
175 for (int i = 0; i < num_clients(); ++i) {
176 MigrationChecker* checker = new MigrationChecker(GetClient(i));
177 migration_checkers_.push_back(checker);
182 syncer::ModelTypeSet GetPreferredDataTypes() {
183 // ProfileSyncService must already have been created before we can call
184 // GetPreferredDataTypes().
185 DCHECK(GetSyncService((0)));
186 syncer::ModelTypeSet preferred_data_types =
187 GetSyncService((0))->GetPreferredDataTypes();
188 preferred_data_types.RemoveAll(syncer::ProxyTypes());
189 // Make sure all clients have the same preferred data types.
190 for (int i = 1; i < num_clients(); ++i) {
191 const syncer::ModelTypeSet other_preferred_data_types =
192 GetSyncService((i))->GetPreferredDataTypes();
193 EXPECT_TRUE(preferred_data_types.Equals(other_preferred_data_types));
195 return preferred_data_types;
198 // Returns a MigrationList with every enabled data type in its own
200 MigrationList GetPreferredDataTypesList() {
201 MigrationList migration_list;
202 const syncer::ModelTypeSet preferred_data_types =
203 GetPreferredDataTypes();
204 for (syncer::ModelTypeSet::Iterator it =
205 preferred_data_types.First(); it.Good(); it.Inc()) {
206 migration_list.push_back(MakeSet(it.Get()));
208 return migration_list;
211 // Trigger a migration for the given types with the given method.
212 void TriggerMigration(syncer::ModelTypeSet model_types,
213 TriggerMethod trigger_method) {
214 switch (trigger_method) {
216 // Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
217 // notification to happen (since model association on a
218 // boolean pref clobbers the local value), so it doesn't work
219 // for anything but single-client tests.
220 ASSERT_EQ(1, num_clients());
221 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
222 ChangeBooleanPref(0, prefs::kShowHomeButton);
224 case MODIFY_BOOKMARK:
225 ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
227 case TRIGGER_NOTIFICATION:
228 TriggerNotification(model_types);
235 // Block until all clients have completed migration for the given
237 void AwaitMigration(syncer::ModelTypeSet migrate_types) {
238 for (int i = 0; i < num_clients(); ++i) {
239 MigrationChecker* checker = migration_checkers_[i];
240 checker->set_expected_types(migrate_types);
242 ASSERT_FALSE(checker->TimedOut());
246 bool ShouldRunMigrationTest() const {
247 if (!ServerSupportsNotificationControl() ||
248 !ServerSupportsErrorTriggering()) {
249 LOG(WARNING) << "Test skipped in this server environment.";
255 // Makes sure migration works with the given migration list and
257 void RunMigrationTest(const MigrationList& migration_list,
258 TriggerMethod trigger_method) {
259 ASSERT_TRUE(ShouldRunMigrationTest());
261 // If we have only one client, turn off notifications to avoid the
262 // possibility of spurious sync cycles.
263 bool do_test_without_notifications =
264 (trigger_method != TRIGGER_NOTIFICATION && num_clients() == 1);
266 if (do_test_without_notifications) {
267 DisableNotifications();
270 // Make sure migration hasn't been triggered prematurely.
271 for (int i = 0; i < num_clients(); ++i) {
272 ASSERT_TRUE(migration_checkers_[i]->migrated_types().Empty());
275 // Phase 1: Trigger the migrations on the server.
276 for (MigrationList::const_iterator it = migration_list.begin();
277 it != migration_list.end(); ++it) {
278 TriggerMigrationDoneError(*it);
281 // Phase 2: Trigger each migration individually and wait for it to
282 // complete. (Multiple migrations may be handled by each
283 // migration cycle, but there's no guarantee of that, so we have
284 // to trigger each migration individually.)
285 for (MigrationList::const_iterator it = migration_list.begin();
286 it != migration_list.end(); ++it) {
287 TriggerMigration(*it, trigger_method);
291 // Phase 3: Wait for all clients to catch up.
293 // AwaitQuiescence() will not succeed when notifications are disabled. We
294 // can safely avoid calling it because we know that, in the single client
295 // case, there is no one else to wait for.
297 // TODO(rlarocque, 97780): Remove the if condition when the test harness
298 // supports calling AwaitQuiescence() when notifications are disabled.
299 if (!do_test_without_notifications) {
303 // TODO(rlarocque): It should be possible to re-enable notifications
304 // here, but doing so makes some windows tests flaky.
308 // Used to keep track of the migration progress for each sync client.
309 ScopedVector<MigrationChecker> migration_checkers_;
311 DISALLOW_COPY_AND_ASSIGN(MigrationTest);
314 class MigrationSingleClientTest : public MigrationTest {
316 MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT) {}
317 virtual ~MigrationSingleClientTest() {}
319 void RunSingleClientMigrationTest(const MigrationList& migration_list,
320 TriggerMethod trigger_method) {
321 if (!ShouldRunMigrationTest()) {
324 ASSERT_TRUE(SetupSync());
325 RunMigrationTest(migration_list, trigger_method);
329 DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest);
332 // The simplest possible migration tests -- a single data type.
334 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
335 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_PREF);
338 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
339 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
343 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
344 PrefsOnlyTriggerNotification) {
345 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
346 TRIGGER_NOTIFICATION);
349 // Nigori is handled specially, so we test that separately.
351 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
352 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
353 TRIGGER_NOTIFICATION);
356 // A little more complicated -- two data types.
358 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
359 BookmarksPrefsIndividually) {
360 RunSingleClientMigrationTest(
361 MakeList(syncer::BOOKMARKS, syncer::PREFERENCES),
365 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
366 RunSingleClientMigrationTest(
367 MakeList(MakeSet(syncer::BOOKMARKS, syncer::PREFERENCES)),
371 // Two data types with one being nigori.
373 // See crbug.com/124480.
374 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
375 DISABLED_PrefsNigoriIndividiaully) {
376 RunSingleClientMigrationTest(
377 MakeList(syncer::PREFERENCES, syncer::NIGORI),
378 TRIGGER_NOTIFICATION);
381 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
382 RunSingleClientMigrationTest(
383 MakeList(MakeSet(syncer::PREFERENCES, syncer::NIGORI)),
387 // The whole shebang -- all data types.
389 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesIndividually) {
390 ASSERT_TRUE(SetupClients());
391 RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
394 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
395 AllTypesIndividuallyTriggerNotification) {
396 ASSERT_TRUE(SetupClients());
397 RunSingleClientMigrationTest(GetPreferredDataTypesList(),
398 TRIGGER_NOTIFICATION);
401 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
402 ASSERT_TRUE(SetupClients());
403 RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
407 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
408 AllTypesAtOnceTriggerNotification) {
409 ASSERT_TRUE(SetupClients());
410 RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
411 TRIGGER_NOTIFICATION);
414 // All data types plus nigori.
416 // See crbug.com/124480.
417 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
418 DISABLED_AllTypesWithNigoriIndividually) {
419 ASSERT_TRUE(SetupClients());
420 MigrationList migration_list = GetPreferredDataTypesList();
421 migration_list.push_front(MakeSet(syncer::NIGORI));
422 RunSingleClientMigrationTest(migration_list, MODIFY_BOOKMARK);
425 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
426 AllTypesWithNigoriAtOnce) {
427 ASSERT_TRUE(SetupClients());
428 syncer::ModelTypeSet all_types = GetPreferredDataTypes();
429 all_types.Put(syncer::NIGORI);
430 RunSingleClientMigrationTest(MakeList(all_types), MODIFY_PREF);
433 class MigrationTwoClientTest : public MigrationTest {
435 MigrationTwoClientTest() : MigrationTest(TWO_CLIENT) {}
436 virtual ~MigrationTwoClientTest() {}
438 // Helper function that verifies that preferences sync still works.
439 void VerifyPrefSync() {
440 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
441 ChangeBooleanPref(0, prefs::kShowHomeButton);
442 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
443 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
446 void RunTwoClientMigrationTest(const MigrationList& migration_list,
447 TriggerMethod trigger_method) {
448 if (!ShouldRunMigrationTest()) {
451 ASSERT_TRUE(SetupSync());
453 // Make sure pref sync works before running the migration test.
456 RunMigrationTest(migration_list, trigger_method);
458 // Make sure pref sync still works after running the migration
464 DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest);
467 // Easiest possible test of migration errors: triggers a server
468 // migration on one datatype, then modifies some other datatype.
469 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigratePrefsThenModifyBookmark) {
470 RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES),
474 // Triggers a server migration on two datatypes, then makes a local
475 // modification to one of them.
476 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
477 MigratePrefsAndBookmarksThenModifyBookmark) {
478 RunTwoClientMigrationTest(
479 MakeList(syncer::PREFERENCES, syncer::BOOKMARKS),
483 // Migrate every datatype in sequence; the catch being that the server
484 // will only tell the client about the migrations one at a time.
485 // TODO(rsimha): This test takes longer than 60 seconds, and will cause tree
486 // redness due to sharding.
487 // Re-enable this test after syncer::kInitialBackoffShortRetrySeconds is reduced
489 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
490 DISABLED_MigrationHellWithoutNigori) {
491 ASSERT_TRUE(SetupClients());
492 MigrationList migration_list = GetPreferredDataTypesList();
493 // Let the first nudge be a datatype that's neither prefs nor
495 migration_list.push_front(MakeSet(syncer::THEMES));
496 RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
499 // See crbug.com/124480.
500 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
501 DISABLED_MigrationHellWithNigori) {
502 ASSERT_TRUE(SetupClients());
503 MigrationList migration_list = GetPreferredDataTypesList();
504 // Let the first nudge be a datatype that's neither prefs nor
506 migration_list.push_front(MakeSet(syncer::THEMES));
507 // Pop off one so that we don't migrate all data types; the syncer
508 // freaks out if we do that (see http://crbug.com/94882).
509 ASSERT_GE(migration_list.size(), 2u);
510 ASSERT_FALSE(migration_list.back().Equals(MakeSet(syncer::NIGORI)));
511 migration_list.back() = MakeSet(syncer::NIGORI);
512 RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
515 class MigrationReconfigureTest : public MigrationTwoClientTest {
517 MigrationReconfigureTest() {}
519 virtual void SetUpCommandLine(base::CommandLine* cl) OVERRIDE {
521 // Do not add optional datatypes.
524 virtual ~MigrationReconfigureTest() {}
527 DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest);