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/test/integration/bookmarks_helper.h"
11 #include "chrome/browser/sync/test/integration/preferences_helper.h"
12 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
13 #include "chrome/browser/sync/test/integration/status_change_checker.h"
14 #include "chrome/browser/sync/test/integration/sync_test.h"
15 #include "chrome/common/pref_names.h"
16 #include "chrome/test/base/ui_test_utils.h"
17 #include "components/translate/core/browser/translate_prefs.h"
19 using bookmarks_helper::AddURL;
20 using bookmarks_helper::IndexedURL;
21 using bookmarks_helper::IndexedURLTitle;
23 using preferences_helper::BooleanPrefMatches;
24 using preferences_helper::ChangeBooleanPref;
28 // Utility functions to make a model type set out of a small number of
31 syncer::ModelTypeSet MakeSet(syncer::ModelType type) {
32 return syncer::ModelTypeSet(type);
35 syncer::ModelTypeSet MakeSet(syncer::ModelType type1,
36 syncer::ModelType type2) {
37 return syncer::ModelTypeSet(type1, type2);
40 // An ordered list of model types sets to migrate. Used by
41 // RunMigrationTest().
42 typedef std::deque<syncer::ModelTypeSet> MigrationList;
44 // Utility functions to make a MigrationList out of a small number of
45 // model types / model type sets.
47 MigrationList MakeList(syncer::ModelTypeSet model_types) {
48 return MigrationList(1, model_types);
51 MigrationList MakeList(syncer::ModelTypeSet model_types1,
52 syncer::ModelTypeSet model_types2) {
53 MigrationList migration_list;
54 migration_list.push_back(model_types1);
55 migration_list.push_back(model_types2);
56 return migration_list;
59 MigrationList MakeList(syncer::ModelType type) {
60 return MakeList(MakeSet(type));
63 MigrationList MakeList(syncer::ModelType type1,
64 syncer::ModelType type2) {
65 return MakeList(MakeSet(type1), MakeSet(type2));
68 // Helper class that checks if the sync backend has successfully completed
69 // migration for a set of data types.
70 class MigrationChecker : public StatusChangeChecker,
71 public browser_sync::MigrationObserver {
73 explicit MigrationChecker(ProfileSyncServiceHarness* harness)
74 : StatusChangeChecker("MigrationChecker"),
77 browser_sync::BackendMigrator* migrator =
78 harness_->service()->GetBackendMigratorForTest();
79 // PSS must have a migrator after sync is setup and initial data type
80 // configuration is complete.
82 migrator->AddMigrationObserver(this);
85 virtual ~MigrationChecker() {}
87 // Returns true when sync reports that there is no pending migration, and
88 // migration is complete for all data types in |expected_types_|.
89 virtual bool IsExitConditionSatisfied() OVERRIDE {
90 DCHECK(!expected_types_.Empty());
91 bool all_expected_types_migrated = migrated_types_.HasAll(expected_types_);
92 DVLOG(1) << harness_->profile_debug_name() << ": Migrated types "
93 << syncer::ModelTypeSetToString(migrated_types_)
94 << (all_expected_types_migrated ? " contains " :
96 << syncer::ModelTypeSetToString(expected_types_);
97 return all_expected_types_migrated &&
98 !HasPendingBackendMigration();
101 bool HasPendingBackendMigration() const {
102 browser_sync::BackendMigrator* migrator =
103 harness_->service()->GetBackendMigratorForTest();
104 return migrator && migrator->state() != browser_sync::BackendMigrator::IDLE;
107 void set_expected_types(syncer::ModelTypeSet expected_types) {
108 expected_types_ = expected_types;
111 syncer::ModelTypeSet migrated_types() const {
112 return migrated_types_;
115 virtual void OnMigrationStateChange() OVERRIDE {
116 if (HasPendingBackendMigration()) {
117 // A new bunch of data types are in the process of being migrated. Merge
118 // them into |pending_types_|.
119 pending_types_.PutAll(
120 harness_->service()->GetBackendMigratorForTest()->
121 GetPendingMigrationTypesForTest());
122 DVLOG(1) << harness_->profile_debug_name()
123 << ": new pending migration types "
124 << syncer::ModelTypeSetToString(pending_types_);
126 // Migration just finished for a bunch of data types. Merge them into
127 // |migrated_types_|.
128 migrated_types_.PutAll(pending_types_);
129 pending_types_.Clear();
130 DVLOG(1) << harness_->profile_debug_name() << ": new migrated types "
131 << syncer::ModelTypeSetToString(migrated_types_);
134 // Nudge ProfileSyncServiceHarness to inspect the exit condition provided by
136 harness_->OnStateChanged();
140 // The sync client for which migration is being verified.
141 ProfileSyncServiceHarness* harness_;
143 // The set of data types that are expected to eventually undergo migration.
144 syncer::ModelTypeSet expected_types_;
146 // The set of data types currently undergoing migration.
147 syncer::ModelTypeSet pending_types_;
149 // The set of data types for which migration is complete. Accumulated by
150 // successive calls to OnMigrationStateChanged.
151 syncer::ModelTypeSet migrated_types_;
153 DISALLOW_COPY_AND_ASSIGN(MigrationChecker);
156 class MigrationTest : public SyncTest {
158 explicit MigrationTest(TestType test_type) : SyncTest(test_type) {}
159 virtual ~MigrationTest() {}
161 enum TriggerMethod { MODIFY_PREF, MODIFY_BOOKMARK, TRIGGER_NOTIFICATION };
163 // Set up sync for all profiles and initialize all MigrationCheckers. This
164 // helps ensure that all migration events are captured, even if they were to
165 // occur before a test calls AwaitMigration for a specific profile.
166 virtual bool SetupSync() OVERRIDE {
167 if (!SyncTest::SetupSync())
170 for (int i = 0; i < num_clients(); ++i) {
171 MigrationChecker* checker = new MigrationChecker(GetClient(i));
172 migration_checkers_.push_back(checker);
177 syncer::ModelTypeSet GetPreferredDataTypes() {
178 // ProfileSyncService must already have been created before we can call
179 // GetPreferredDataTypes().
180 DCHECK(GetClient(0)->service());
181 syncer::ModelTypeSet preferred_data_types =
182 GetClient(0)->service()->GetPreferredDataTypes();
183 preferred_data_types.RemoveAll(syncer::ProxyTypes());
184 // Make sure all clients have the same preferred data types.
185 for (int i = 1; i < num_clients(); ++i) {
186 const syncer::ModelTypeSet other_preferred_data_types =
187 GetClient(i)->service()->GetPreferredDataTypes();
188 EXPECT_TRUE(preferred_data_types.Equals(other_preferred_data_types));
190 return preferred_data_types;
193 // Returns a MigrationList with every enabled data type in its own
195 MigrationList GetPreferredDataTypesList() {
196 MigrationList migration_list;
197 const syncer::ModelTypeSet preferred_data_types =
198 GetPreferredDataTypes();
199 for (syncer::ModelTypeSet::Iterator it =
200 preferred_data_types.First(); it.Good(); it.Inc()) {
201 migration_list.push_back(MakeSet(it.Get()));
203 return migration_list;
206 // Trigger a migration for the given types with the given method.
207 void TriggerMigration(syncer::ModelTypeSet model_types,
208 TriggerMethod trigger_method) {
209 switch (trigger_method) {
211 // Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
212 // notification to happen (since model association on a
213 // boolean pref clobbers the local value), so it doesn't work
214 // for anything but single-client tests.
215 ASSERT_EQ(1, num_clients());
216 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
217 ChangeBooleanPref(0, prefs::kShowHomeButton);
219 case MODIFY_BOOKMARK:
220 ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
222 case TRIGGER_NOTIFICATION:
223 TriggerNotification(model_types);
230 // Block until all clients have completed migration for the given
232 void AwaitMigration(syncer::ModelTypeSet migrate_types) {
233 for (int i = 0; i < num_clients(); ++i) {
234 MigrationChecker* checker = migration_checkers_[i];
235 checker->set_expected_types(migrate_types);
236 if (!checker->IsExitConditionSatisfied())
237 ASSERT_TRUE(GetClient(i)->AwaitStatusChange(checker, "AwaitMigration"));
241 bool ShouldRunMigrationTest() const {
242 if (!ServerSupportsNotificationControl() ||
243 !ServerSupportsErrorTriggering()) {
244 LOG(WARNING) << "Test skipped in this server environment.";
250 // Makes sure migration works with the given migration list and
252 void RunMigrationTest(const MigrationList& migration_list,
253 TriggerMethod trigger_method) {
254 ASSERT_TRUE(ShouldRunMigrationTest());
256 // If we have only one client, turn off notifications to avoid the
257 // possibility of spurious sync cycles.
258 bool do_test_without_notifications =
259 (trigger_method != TRIGGER_NOTIFICATION && num_clients() == 1);
261 if (do_test_without_notifications) {
262 DisableNotifications();
265 // Make sure migration hasn't been triggered prematurely.
266 for (int i = 0; i < num_clients(); ++i) {
267 ASSERT_TRUE(migration_checkers_[i]->migrated_types().Empty());
270 // Phase 1: Trigger the migrations on the server.
271 for (MigrationList::const_iterator it = migration_list.begin();
272 it != migration_list.end(); ++it) {
273 TriggerMigrationDoneError(*it);
276 // Phase 2: Trigger each migration individually and wait for it to
277 // complete. (Multiple migrations may be handled by each
278 // migration cycle, but there's no guarantee of that, so we have
279 // to trigger each migration individually.)
280 for (MigrationList::const_iterator it = migration_list.begin();
281 it != migration_list.end(); ++it) {
282 TriggerMigration(*it, trigger_method);
286 // Phase 3: Wait for all clients to catch up.
288 // AwaitQuiescence() will not succeed when notifications are disabled. We
289 // can safely avoid calling it because we know that, in the single client
290 // case, there is no one else to wait for.
292 // TODO(rlarocque, 97780): Remove the if condition when the test harness
293 // supports calling AwaitQuiescence() when notifications are disabled.
294 if (!do_test_without_notifications) {
298 // TODO(rlarocque): It should be possible to re-enable notifications
299 // here, but doing so makes some windows tests flaky.
303 // Used to keep track of the migration progress for each sync client.
304 ScopedVector<MigrationChecker> migration_checkers_;
306 DISALLOW_COPY_AND_ASSIGN(MigrationTest);
309 class MigrationSingleClientTest : public MigrationTest {
311 MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT) {}
312 virtual ~MigrationSingleClientTest() {}
314 void RunSingleClientMigrationTest(const MigrationList& migration_list,
315 TriggerMethod trigger_method) {
316 if (!ShouldRunMigrationTest()) {
319 ASSERT_TRUE(SetupSync());
320 RunMigrationTest(migration_list, trigger_method);
324 DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest);
327 // The simplest possible migration tests -- a single data type.
329 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
330 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_PREF);
333 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
334 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
338 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
339 PrefsOnlyTriggerNotification) {
340 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
341 TRIGGER_NOTIFICATION);
344 // Nigori is handled specially, so we test that separately.
346 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
347 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
348 TRIGGER_NOTIFICATION);
351 // A little more complicated -- two data types.
353 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
354 BookmarksPrefsIndividually) {
355 RunSingleClientMigrationTest(
356 MakeList(syncer::BOOKMARKS, syncer::PREFERENCES),
360 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
361 RunSingleClientMigrationTest(
362 MakeList(MakeSet(syncer::BOOKMARKS, syncer::PREFERENCES)),
366 // Two data types with one being nigori.
368 // See crbug.com/124480.
369 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
370 DISABLED_PrefsNigoriIndividiaully) {
371 RunSingleClientMigrationTest(
372 MakeList(syncer::PREFERENCES, syncer::NIGORI),
373 TRIGGER_NOTIFICATION);
376 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
377 RunSingleClientMigrationTest(
378 MakeList(MakeSet(syncer::PREFERENCES, syncer::NIGORI)),
382 // The whole shebang -- all data types.
384 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesIndividually) {
385 ASSERT_TRUE(SetupClients());
386 RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
389 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
390 AllTypesIndividuallyTriggerNotification) {
391 ASSERT_TRUE(SetupClients());
392 RunSingleClientMigrationTest(GetPreferredDataTypesList(),
393 TRIGGER_NOTIFICATION);
396 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
397 ASSERT_TRUE(SetupClients());
398 RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
402 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
403 AllTypesAtOnceTriggerNotification) {
404 ASSERT_TRUE(SetupClients());
405 RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
406 TRIGGER_NOTIFICATION);
409 // All data types plus nigori.
411 // See crbug.com/124480.
412 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
413 DISABLED_AllTypesWithNigoriIndividually) {
414 ASSERT_TRUE(SetupClients());
415 MigrationList migration_list = GetPreferredDataTypesList();
416 migration_list.push_front(MakeSet(syncer::NIGORI));
417 RunSingleClientMigrationTest(migration_list, MODIFY_BOOKMARK);
420 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
421 AllTypesWithNigoriAtOnce) {
422 ASSERT_TRUE(SetupClients());
423 syncer::ModelTypeSet all_types = GetPreferredDataTypes();
424 all_types.Put(syncer::NIGORI);
425 RunSingleClientMigrationTest(MakeList(all_types), MODIFY_PREF);
428 class MigrationTwoClientTest : public MigrationTest {
430 MigrationTwoClientTest() : MigrationTest(TWO_CLIENT) {}
431 virtual ~MigrationTwoClientTest() {}
433 // Helper function that verifies that preferences sync still works.
434 void VerifyPrefSync() {
435 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
436 ChangeBooleanPref(0, prefs::kShowHomeButton);
437 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
438 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
441 void RunTwoClientMigrationTest(const MigrationList& migration_list,
442 TriggerMethod trigger_method) {
443 if (!ShouldRunMigrationTest()) {
446 ASSERT_TRUE(SetupSync());
448 // Make sure pref sync works before running the migration test.
451 RunMigrationTest(migration_list, trigger_method);
453 // Make sure pref sync still works after running the migration
459 DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest);
462 // Easiest possible test of migration errors: triggers a server
463 // migration on one datatype, then modifies some other datatype.
464 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigratePrefsThenModifyBookmark) {
465 RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES),
469 // Triggers a server migration on two datatypes, then makes a local
470 // modification to one of them.
471 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
472 MigratePrefsAndBookmarksThenModifyBookmark) {
473 RunTwoClientMigrationTest(
474 MakeList(syncer::PREFERENCES, syncer::BOOKMARKS),
478 // Migrate every datatype in sequence; the catch being that the server
479 // will only tell the client about the migrations one at a time.
480 // TODO(rsimha): This test takes longer than 60 seconds, and will cause tree
481 // redness due to sharding.
482 // Re-enable this test after syncer::kInitialBackoffShortRetrySeconds is reduced
484 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
485 DISABLED_MigrationHellWithoutNigori) {
486 ASSERT_TRUE(SetupClients());
487 MigrationList migration_list = GetPreferredDataTypesList();
488 // Let the first nudge be a datatype that's neither prefs nor
490 migration_list.push_front(MakeSet(syncer::THEMES));
491 RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
494 // See crbug.com/124480.
495 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
496 DISABLED_MigrationHellWithNigori) {
497 ASSERT_TRUE(SetupClients());
498 MigrationList migration_list = GetPreferredDataTypesList();
499 // Let the first nudge be a datatype that's neither prefs nor
501 migration_list.push_front(MakeSet(syncer::THEMES));
502 // Pop off one so that we don't migrate all data types; the syncer
503 // freaks out if we do that (see http://crbug.com/94882).
504 ASSERT_GE(migration_list.size(), 2u);
505 ASSERT_FALSE(migration_list.back().Equals(MakeSet(syncer::NIGORI)));
506 migration_list.back() = MakeSet(syncer::NIGORI);
507 RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
510 class MigrationReconfigureTest : public MigrationTwoClientTest {
512 MigrationReconfigureTest() {}
514 virtual void SetUpCommandLine(CommandLine* cl) OVERRIDE {
516 // Do not add optional datatypes.
519 virtual ~MigrationReconfigureTest() {}
522 DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest);