1 // Copyright (c) 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/browser/translate/translate_prefs.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/ui_test_utils.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 !harness_
->HasPendingBackendMigration();
101 void set_expected_types(syncer::ModelTypeSet expected_types
) {
102 expected_types_
= expected_types
;
105 syncer::ModelTypeSet
migrated_types() const {
106 return migrated_types_
;
109 virtual void OnMigrationStateChange() OVERRIDE
{
110 if (harness_
->HasPendingBackendMigration()) {
111 // A new bunch of data types are in the process of being migrated. Merge
112 // them into |pending_types_|.
113 pending_types_
.PutAll(
114 harness_
->service()->GetBackendMigratorForTest()->
115 GetPendingMigrationTypesForTest());
116 DVLOG(1) << harness_
->profile_debug_name()
117 << ": new pending migration types "
118 << syncer::ModelTypeSetToString(pending_types_
);
120 // Migration just finished for a bunch of data types. Merge them into
121 // |migrated_types_|.
122 migrated_types_
.PutAll(pending_types_
);
123 pending_types_
.Clear();
124 DVLOG(1) << harness_
->profile_debug_name() << ": new migrated types "
125 << syncer::ModelTypeSetToString(migrated_types_
);
128 // Nudge ProfileSyncServiceHarness to inspect the exit condition provided by
130 harness_
->OnStateChanged();
134 // The sync client for which migration is being verified.
135 ProfileSyncServiceHarness
* harness_
;
137 // The set of data types that are expected to eventually undergo migration.
138 syncer::ModelTypeSet expected_types_
;
140 // The set of data types currently undergoing migration.
141 syncer::ModelTypeSet pending_types_
;
143 // The set of data types for which migration is complete. Accumulated by
144 // successive calls to OnMigrationStateChanged.
145 syncer::ModelTypeSet migrated_types_
;
147 DISALLOW_COPY_AND_ASSIGN(MigrationChecker
);
150 class MigrationTest
: public SyncTest
{
152 explicit MigrationTest(TestType test_type
) : SyncTest(test_type
) {}
153 virtual ~MigrationTest() {}
155 enum TriggerMethod
{ MODIFY_PREF
, MODIFY_BOOKMARK
, TRIGGER_NOTIFICATION
};
157 // Set up sync for all profiles and initialize all MigrationCheckers. This
158 // helps ensure that all migration events are captured, even if they were to
159 // occur before a test calls AwaitMigration for a specific profile.
160 virtual bool SetupSync() OVERRIDE
{
161 if (!SyncTest::SetupSync())
164 for (int i
= 0; i
< num_clients(); ++i
) {
165 MigrationChecker
* checker
= new MigrationChecker(GetClient(i
));
166 migration_checkers_
.push_back(checker
);
171 syncer::ModelTypeSet
GetPreferredDataTypes() {
172 // ProfileSyncService must already have been created before we can call
173 // GetPreferredDataTypes().
174 DCHECK(GetClient(0)->service());
175 syncer::ModelTypeSet preferred_data_types
=
176 GetClient(0)->service()->GetPreferredDataTypes();
177 preferred_data_types
.RemoveAll(syncer::ProxyTypes());
178 // Make sure all clients have the same preferred data types.
179 for (int i
= 1; i
< num_clients(); ++i
) {
180 const syncer::ModelTypeSet other_preferred_data_types
=
181 GetClient(i
)->service()->GetPreferredDataTypes();
182 EXPECT_TRUE(preferred_data_types
.Equals(other_preferred_data_types
));
184 return preferred_data_types
;
187 // Returns a MigrationList with every enabled data type in its own
189 MigrationList
GetPreferredDataTypesList() {
190 MigrationList migration_list
;
191 const syncer::ModelTypeSet preferred_data_types
=
192 GetPreferredDataTypes();
193 for (syncer::ModelTypeSet::Iterator it
=
194 preferred_data_types
.First(); it
.Good(); it
.Inc()) {
195 migration_list
.push_back(MakeSet(it
.Get()));
197 return migration_list
;
200 // Trigger a migration for the given types with the given method.
201 void TriggerMigration(syncer::ModelTypeSet model_types
,
202 TriggerMethod trigger_method
) {
203 switch (trigger_method
) {
205 // Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
206 // notification to happen (since model association on a
207 // boolean pref clobbers the local value), so it doesn't work
208 // for anything but single-client tests.
209 ASSERT_EQ(1, num_clients());
210 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton
));
211 ChangeBooleanPref(0, prefs::kShowHomeButton
);
213 case MODIFY_BOOKMARK
:
214 ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
216 case TRIGGER_NOTIFICATION
:
217 TriggerNotification(model_types
);
224 // Block until all clients have completed migration for the given
226 void AwaitMigration(syncer::ModelTypeSet migrate_types
) {
227 for (int i
= 0; i
< num_clients(); ++i
) {
228 MigrationChecker
* checker
= migration_checkers_
[i
];
229 checker
->set_expected_types(migrate_types
);
230 if (!checker
->IsExitConditionSatisfied())
231 ASSERT_TRUE(GetClient(i
)->AwaitStatusChange(checker
, "AwaitMigration"));
232 // We must use AwaitDataSyncCompletion rather than the more common
233 // AwaitFullSyncCompletion. As long as crbug.com/97780 is open, we will
234 // rely on self-notifications to ensure that progress markers are updated,
235 // which allows AwaitFullSyncCompletion to return. However, in some
236 // migration tests these notifications are completely disabled, so the
237 // progress markers do not get updated. This is why we must use the less
238 // strict condition, AwaitDataSyncCompletion.
239 ASSERT_TRUE(GetClient(i
)->AwaitDataSyncCompletion());
243 bool ShouldRunMigrationTest() const {
244 if (!ServerSupportsNotificationControl() ||
245 !ServerSupportsErrorTriggering()) {
246 LOG(WARNING
) << "Test skipped in this server environment.";
252 // Makes sure migration works with the given migration list and
254 void RunMigrationTest(const MigrationList
& migration_list
,
255 TriggerMethod trigger_method
) {
256 ASSERT_TRUE(ShouldRunMigrationTest());
258 // If we have only one client, turn off notifications to avoid the
259 // possibility of spurious sync cycles.
260 bool do_test_without_notifications
=
261 (trigger_method
!= TRIGGER_NOTIFICATION
&& num_clients() == 1);
263 if (do_test_without_notifications
) {
264 DisableNotifications();
267 // Make sure migration hasn't been triggered prematurely.
268 for (int i
= 0; i
< num_clients(); ++i
) {
269 ASSERT_TRUE(migration_checkers_
[i
]->migrated_types().Empty());
272 // Phase 1: Trigger the migrations on the server.
273 for (MigrationList::const_iterator it
= migration_list
.begin();
274 it
!= migration_list
.end(); ++it
) {
275 TriggerMigrationDoneError(*it
);
278 // Phase 2: Trigger each migration individually and wait for it to
279 // complete. (Multiple migrations may be handled by each
280 // migration cycle, but there's no guarantee of that, so we have
281 // to trigger each migration individually.)
282 for (MigrationList::const_iterator it
= migration_list
.begin();
283 it
!= migration_list
.end(); ++it
) {
284 TriggerMigration(*it
, trigger_method
);
288 // Phase 3: Wait for all clients to catch up.
290 // AwaitQuiescence() will not succeed when notifications are disabled. We
291 // can safely avoid calling it because we know that, in the single client
292 // case, there is no one else to wait for.
294 // TODO(rlarocque, 97780): Remove the if condition when the test harness
295 // supports calling AwaitQuiescence() when notifications are disabled.
296 if (!do_test_without_notifications
) {
300 // TODO(rlarocque): It should be possible to re-enable notifications
301 // here, but doing so makes some windows tests flaky.
305 // Used to keep track of the migration progress for each sync client.
306 ScopedVector
<MigrationChecker
> migration_checkers_
;
308 DISALLOW_COPY_AND_ASSIGN(MigrationTest
);
311 class MigrationSingleClientTest
: public MigrationTest
{
313 MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT
) {}
314 virtual ~MigrationSingleClientTest() {}
316 void RunSingleClientMigrationTest(const MigrationList
& migration_list
,
317 TriggerMethod trigger_method
) {
318 if (!ShouldRunMigrationTest()) {
321 ASSERT_TRUE(SetupSync());
322 RunMigrationTest(migration_list
, trigger_method
);
326 DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest
);
329 // The simplest possible migration tests -- a single data type.
331 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
, PrefsOnlyModifyPref
) {
332 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES
), MODIFY_PREF
);
335 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
, PrefsOnlyModifyBookmark
) {
336 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES
),
340 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
,
341 PrefsOnlyTriggerNotification
) {
342 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES
),
343 TRIGGER_NOTIFICATION
);
346 // Nigori is handled specially, so we test that separately.
348 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
, NigoriOnly
) {
349 RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES
),
350 TRIGGER_NOTIFICATION
);
353 // A little more complicated -- two data types.
355 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
,
356 BookmarksPrefsIndividually
) {
357 RunSingleClientMigrationTest(
358 MakeList(syncer::BOOKMARKS
, syncer::PREFERENCES
),
362 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
, BookmarksPrefsBoth
) {
363 RunSingleClientMigrationTest(
364 MakeList(MakeSet(syncer::BOOKMARKS
, syncer::PREFERENCES
)),
368 // Two data types with one being nigori.
370 // See crbug.com/124480.
371 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
,
372 DISABLED_PrefsNigoriIndividiaully
) {
373 RunSingleClientMigrationTest(
374 MakeList(syncer::PREFERENCES
, syncer::NIGORI
),
375 TRIGGER_NOTIFICATION
);
378 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
, PrefsNigoriBoth
) {
379 RunSingleClientMigrationTest(
380 MakeList(MakeSet(syncer::PREFERENCES
, syncer::NIGORI
)),
384 // The whole shebang -- all data types.
386 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
, AllTypesIndividually
) {
387 ASSERT_TRUE(SetupClients());
388 RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK
);
391 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
,
392 AllTypesIndividuallyTriggerNotification
) {
393 ASSERT_TRUE(SetupClients());
394 RunSingleClientMigrationTest(GetPreferredDataTypesList(),
395 TRIGGER_NOTIFICATION
);
398 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
, AllTypesAtOnce
) {
399 ASSERT_TRUE(SetupClients());
400 RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
404 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
,
405 AllTypesAtOnceTriggerNotification
) {
406 ASSERT_TRUE(SetupClients());
407 RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
408 TRIGGER_NOTIFICATION
);
411 // All data types plus nigori.
413 // See crbug.com/124480.
414 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
,
415 DISABLED_AllTypesWithNigoriIndividually
) {
416 ASSERT_TRUE(SetupClients());
417 MigrationList migration_list
= GetPreferredDataTypesList();
418 migration_list
.push_front(MakeSet(syncer::NIGORI
));
419 RunSingleClientMigrationTest(migration_list
, MODIFY_BOOKMARK
);
422 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest
,
423 AllTypesWithNigoriAtOnce
) {
424 ASSERT_TRUE(SetupClients());
425 syncer::ModelTypeSet all_types
= GetPreferredDataTypes();
426 all_types
.Put(syncer::NIGORI
);
427 RunSingleClientMigrationTest(MakeList(all_types
), MODIFY_PREF
);
430 class MigrationTwoClientTest
: public MigrationTest
{
432 MigrationTwoClientTest() : MigrationTest(TWO_CLIENT
) {}
433 virtual ~MigrationTwoClientTest() {}
435 // Helper function that verifies that preferences sync still works.
436 void VerifyPrefSync() {
437 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton
));
438 ChangeBooleanPref(0, prefs::kShowHomeButton
);
439 ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
440 ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton
));
443 void RunTwoClientMigrationTest(const MigrationList
& migration_list
,
444 TriggerMethod trigger_method
) {
445 if (!ShouldRunMigrationTest()) {
448 ASSERT_TRUE(SetupSync());
450 // Make sure pref sync works before running the migration test.
453 RunMigrationTest(migration_list
, trigger_method
);
455 // Make sure pref sync still works after running the migration
461 DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest
);
464 // Easiest possible test of migration errors: triggers a server
465 // migration on one datatype, then modifies some other datatype.
466 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest
, MigratePrefsThenModifyBookmark
) {
467 RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES
),
471 // Triggers a server migration on two datatypes, then makes a local
472 // modification to one of them.
473 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest
,
474 MigratePrefsAndBookmarksThenModifyBookmark
) {
475 RunTwoClientMigrationTest(
476 MakeList(syncer::PREFERENCES
, syncer::BOOKMARKS
),
480 // Migrate every datatype in sequence; the catch being that the server
481 // will only tell the client about the migrations one at a time.
482 // TODO(rsimha): This test takes longer than 60 seconds, and will cause tree
483 // redness due to sharding.
484 // Re-enable this test after syncer::kInitialBackoffShortRetrySeconds is reduced
486 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest
,
487 DISABLED_MigrationHellWithoutNigori
) {
488 ASSERT_TRUE(SetupClients());
489 MigrationList migration_list
= GetPreferredDataTypesList();
490 // Let the first nudge be a datatype that's neither prefs nor
492 migration_list
.push_front(MakeSet(syncer::THEMES
));
493 RunTwoClientMigrationTest(migration_list
, MODIFY_BOOKMARK
);
496 // See crbug.com/124480.
497 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest
,
498 DISABLED_MigrationHellWithNigori
) {
499 ASSERT_TRUE(SetupClients());
500 MigrationList migration_list
= GetPreferredDataTypesList();
501 // Let the first nudge be a datatype that's neither prefs nor
503 migration_list
.push_front(MakeSet(syncer::THEMES
));
504 // Pop off one so that we don't migrate all data types; the syncer
505 // freaks out if we do that (see http://crbug.com/94882).
506 ASSERT_GE(migration_list
.size(), 2u);
507 ASSERT_FALSE(migration_list
.back().Equals(MakeSet(syncer::NIGORI
)));
508 migration_list
.back() = MakeSet(syncer::NIGORI
);
509 RunTwoClientMigrationTest(migration_list
, MODIFY_BOOKMARK
);
512 class MigrationReconfigureTest
: public MigrationTwoClientTest
{
514 MigrationReconfigureTest() {}
516 virtual void SetUpCommandLine(CommandLine
* cl
) OVERRIDE
{
518 // Do not add optional datatypes.
521 virtual ~MigrationReconfigureTest() {}
524 DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest
);