migration_test.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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.
4
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"
19
20using bookmarks_helper::AddURL;
21using bookmarks_helper::IndexedURL;
22using bookmarks_helper::IndexedURLTitle;
23
24using preferences_helper::BooleanPrefMatches;
25using preferences_helper::ChangeBooleanPref;
26
27namespace {
28
29// Utility functions to make a model type set out of a small number of
30// model types.
31
32syncer::ModelTypeSet MakeSet(syncer::ModelType type) {
33  return syncer::ModelTypeSet(type);
34}
35
36syncer::ModelTypeSet MakeSet(syncer::ModelType type1,
37                             syncer::ModelType type2) {
38  return syncer::ModelTypeSet(type1, type2);
39}
40
41// An ordered list of model types sets to migrate.  Used by
42// RunMigrationTest().
43typedef std::deque<syncer::ModelTypeSet> MigrationList;
44
45// Utility functions to make a MigrationList out of a small number of
46// model types / model type sets.
47
48MigrationList MakeList(syncer::ModelTypeSet model_types) {
49  return MigrationList(1, model_types);
50}
51
52MigrationList 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;
58}
59
60MigrationList MakeList(syncer::ModelType type) {
61  return MakeList(MakeSet(type));
62}
63
64MigrationList MakeList(syncer::ModelType type1,
65                       syncer::ModelType type2) {
66  return MakeList(MakeSet(type1), MakeSet(type2));
67}
68
69// Helper class that checks if the sync backend has successfully completed
70// migration for a set of data types.
71class MigrationChecker : public SingleClientStatusChangeChecker,
72                         public browser_sync::MigrationObserver {
73 public:
74  explicit MigrationChecker(ProfileSyncServiceHarness* harness)
75      : SingleClientStatusChangeChecker(harness->service()),
76        harness_(harness) {
77    DCHECK(harness_);
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.
82    DCHECK(migrator);
83    migrator->AddMigrationObserver(this);
84  }
85
86  virtual ~MigrationChecker() {}
87
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 " :
96                                               " does not contain ")
97             << syncer::ModelTypeSetToString(expected_types_);
98    return all_expected_types_migrated &&
99           !HasPendingBackendMigration();
100  }
101
102  virtual std::string GetDebugMessage() const OVERRIDE {
103    return "Waiting to migrate (" + ModelTypeSetToString(expected_types_) + ")";
104  }
105
106  bool HasPendingBackendMigration() const {
107    browser_sync::BackendMigrator* migrator =
108        harness_->service()->GetBackendMigratorForTest();
109    return migrator && migrator->state() != browser_sync::BackendMigrator::IDLE;
110  }
111
112  void set_expected_types(syncer::ModelTypeSet expected_types) {
113    expected_types_ = expected_types;
114  }
115
116  syncer::ModelTypeSet migrated_types() const {
117    return migrated_types_;
118  }
119
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_);
130    } else {
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_);
137    }
138
139    // Manually trigger a check of the exit condition.
140    if (!expected_types_.Empty())
141      OnStateChanged();
142  }
143
144 private:
145  // The sync client for which migration is being verified.
146  ProfileSyncServiceHarness* harness_;
147
148  // The set of data types that are expected to eventually undergo migration.
149  syncer::ModelTypeSet expected_types_;
150
151  // The set of data types currently undergoing migration.
152  syncer::ModelTypeSet pending_types_;
153
154  // The set of data types for which migration is complete. Accumulated by
155  // successive calls to OnMigrationStateChanged.
156  syncer::ModelTypeSet migrated_types_;
157
158  DISALLOW_COPY_AND_ASSIGN(MigrationChecker);
159};
160
161class MigrationTest : public SyncTest  {
162 public:
163  explicit MigrationTest(TestType test_type) : SyncTest(test_type) {}
164  virtual ~MigrationTest() {}
165
166  enum TriggerMethod { MODIFY_PREF, MODIFY_BOOKMARK, TRIGGER_NOTIFICATION };
167
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())
173      return false;
174
175    for (int i = 0; i < num_clients(); ++i) {
176      MigrationChecker* checker = new MigrationChecker(GetClient(i));
177      migration_checkers_.push_back(checker);
178    }
179    return true;
180  }
181
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
190    // The managed user settings will be "unready" during this test, so we
191    // should not request that they be migrated.
192    preferred_data_types.Remove(syncer::SUPERVISED_USER_SETTINGS);
193
194    // Make sure all clients have the same preferred data types.
195    for (int i = 1; i < num_clients(); ++i) {
196      const syncer::ModelTypeSet other_preferred_data_types =
197          GetSyncService((i))->GetPreferredDataTypes();
198      EXPECT_TRUE(preferred_data_types.Equals(other_preferred_data_types));
199    }
200    return preferred_data_types;
201  }
202
203  // Returns a MigrationList with every enabled data type in its own
204  // set.
205  MigrationList GetPreferredDataTypesList() {
206    MigrationList migration_list;
207    const syncer::ModelTypeSet preferred_data_types =
208        GetPreferredDataTypes();
209    for (syncer::ModelTypeSet::Iterator it =
210             preferred_data_types.First(); it.Good(); it.Inc()) {
211      migration_list.push_back(MakeSet(it.Get()));
212    }
213    return migration_list;
214  }
215
216  // Trigger a migration for the given types with the given method.
217  void TriggerMigration(syncer::ModelTypeSet model_types,
218                        TriggerMethod trigger_method) {
219    switch (trigger_method) {
220      case MODIFY_PREF:
221        // Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
222        // notification to happen (since model association on a
223        // boolean pref clobbers the local value), so it doesn't work
224        // for anything but single-client tests.
225        ASSERT_EQ(1, num_clients());
226        ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
227        ChangeBooleanPref(0, prefs::kShowHomeButton);
228        break;
229      case MODIFY_BOOKMARK:
230        ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
231        break;
232      case TRIGGER_NOTIFICATION:
233        TriggerNotification(model_types);
234        break;
235      default:
236        ADD_FAILURE();
237    }
238  }
239
240  // Block until all clients have completed migration for the given
241  // types.
242  void AwaitMigration(syncer::ModelTypeSet migrate_types) {
243    for (int i = 0; i < num_clients(); ++i) {
244      MigrationChecker* checker = migration_checkers_[i];
245      checker->set_expected_types(migrate_types);
246      checker->Wait();
247      ASSERT_FALSE(checker->TimedOut());
248    }
249  }
250
251  // Makes sure migration works with the given migration list and
252  // trigger method.
253  void RunMigrationTest(const MigrationList& migration_list,
254                        TriggerMethod trigger_method) {
255    // If we have only one client, turn off notifications to avoid the
256    // possibility of spurious sync cycles.
257    bool do_test_without_notifications =
258        (trigger_method != TRIGGER_NOTIFICATION && num_clients() == 1);
259
260    if (do_test_without_notifications) {
261      DisableNotifications();
262    }
263
264    // Make sure migration hasn't been triggered prematurely.
265    for (int i = 0; i < num_clients(); ++i) {
266      ASSERT_TRUE(migration_checkers_[i]->migrated_types().Empty());
267    }
268
269    // Phase 1: Trigger the migrations on the server.
270    for (MigrationList::const_iterator it = migration_list.begin();
271         it != migration_list.end(); ++it) {
272      TriggerMigrationDoneError(*it);
273    }
274
275    // Phase 2: Trigger each migration individually and wait for it to
276    // complete.  (Multiple migrations may be handled by each
277    // migration cycle, but there's no guarantee of that, so we have
278    // to trigger each migration individually.)
279    for (MigrationList::const_iterator it = migration_list.begin();
280         it != migration_list.end(); ++it) {
281      TriggerMigration(*it, trigger_method);
282      AwaitMigration(*it);
283    }
284
285    // Phase 3: Wait for all clients to catch up.
286    //
287    // AwaitQuiescence() will not succeed when notifications are disabled.  We
288    // can safely avoid calling it because we know that, in the single client
289    // case, there is no one else to wait for.
290    //
291    // TODO(rlarocque, 97780): Remove the if condition when the test harness
292    // supports calling AwaitQuiescence() when notifications are disabled.
293    if (!do_test_without_notifications) {
294      AwaitQuiescence();
295    }
296
297    // TODO(rlarocque): It should be possible to re-enable notifications
298    // here, but doing so makes some windows tests flaky.
299  }
300
301 private:
302  // Used to keep track of the migration progress for each sync client.
303  ScopedVector<MigrationChecker> migration_checkers_;
304
305  DISALLOW_COPY_AND_ASSIGN(MigrationTest);
306};
307
308class MigrationSingleClientTest : public MigrationTest {
309 public:
310  MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT_LEGACY) {}
311  virtual ~MigrationSingleClientTest() {}
312
313  void RunSingleClientMigrationTest(const MigrationList& migration_list,
314                                    TriggerMethod trigger_method) {
315    ASSERT_TRUE(SetupSync());
316    RunMigrationTest(migration_list, trigger_method);
317  }
318
319 private:
320  DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest);
321};
322
323// The simplest possible migration tests -- a single data type.
324
325IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
326  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_PREF);
327}
328
329IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
330  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
331                               MODIFY_BOOKMARK);
332}
333
334IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
335                       PrefsOnlyTriggerNotification) {
336  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
337                               TRIGGER_NOTIFICATION);
338}
339
340// Nigori is handled specially, so we test that separately.
341
342IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
343  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
344                               TRIGGER_NOTIFICATION);
345}
346
347// A little more complicated -- two data types.
348
349IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsIndividually) {
350  RunSingleClientMigrationTest(
351      MakeList(syncer::BOOKMARKS, syncer::PREFERENCES),
352      MODIFY_PREF);
353}
354
355IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
356  RunSingleClientMigrationTest(
357      MakeList(MakeSet(syncer::BOOKMARKS, syncer::PREFERENCES)),
358      MODIFY_BOOKMARK);
359}
360
361// Two data types with one being nigori.
362
363// See crbug.com/124480.
364IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
365                       DISABLED_PrefsNigoriIndividiaully) {
366  RunSingleClientMigrationTest(
367      MakeList(syncer::PREFERENCES, syncer::NIGORI),
368      TRIGGER_NOTIFICATION);
369}
370
371IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
372  RunSingleClientMigrationTest(
373      MakeList(MakeSet(syncer::PREFERENCES, syncer::NIGORI)),
374      MODIFY_PREF);
375}
376
377// The whole shebang -- all data types.
378#if defined(OS_WIN)
379// http://crbug.com/403778
380#define MAYBE_AllTypesIndividually DISABLED_AllTypesIndividually
381#else
382#define MAYBE_AllTypesIndividually AllTypesIndividually
383#endif
384IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, MAYBE_AllTypesIndividually) {
385  ASSERT_TRUE(SetupClients());
386  RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
387}
388
389#if defined(OS_WIN)
390// http://crbug.com/403778
391#define MAYBE_AllTypesIndividuallyTriggerNotification DISABLED_AllTypesIndividuallyTriggerNotification
392#else
393#define MAYBE_AllTypesIndividuallyTriggerNotification AllTypesIndividuallyTriggerNotification
394#endif
395IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
396                       MAYBE_AllTypesIndividuallyTriggerNotification) {
397  ASSERT_TRUE(SetupClients());
398  RunSingleClientMigrationTest(GetPreferredDataTypesList(),
399                               TRIGGER_NOTIFICATION);
400}
401
402IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
403  ASSERT_TRUE(SetupClients());
404  RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
405                               MODIFY_PREF);
406}
407
408IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
409                       AllTypesAtOnceTriggerNotification) {
410  ASSERT_TRUE(SetupClients());
411  RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
412                               TRIGGER_NOTIFICATION);
413}
414
415// All data types plus nigori.
416
417// See crbug.com/124480.
418IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
419                       DISABLED_AllTypesWithNigoriIndividually) {
420  ASSERT_TRUE(SetupClients());
421  MigrationList migration_list = GetPreferredDataTypesList();
422  migration_list.push_front(MakeSet(syncer::NIGORI));
423  RunSingleClientMigrationTest(migration_list, MODIFY_BOOKMARK);
424}
425
426IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesWithNigoriAtOnce) {
427  ASSERT_TRUE(SetupClients());
428  syncer::ModelTypeSet all_types = GetPreferredDataTypes();
429  all_types.Put(syncer::NIGORI);
430  RunSingleClientMigrationTest(MakeList(all_types), MODIFY_PREF);
431}
432
433class MigrationTwoClientTest : public MigrationTest {
434 public:
435  MigrationTwoClientTest() : MigrationTest(TWO_CLIENT_LEGACY) {}
436  virtual ~MigrationTwoClientTest() {}
437
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));
444  }
445
446  void RunTwoClientMigrationTest(const MigrationList& migration_list,
447                                 TriggerMethod trigger_method) {
448    ASSERT_TRUE(SetupSync());
449
450    // Make sure pref sync works before running the migration test.
451    VerifyPrefSync();
452
453    RunMigrationTest(migration_list, trigger_method);
454
455    // Make sure pref sync still works after running the migration
456    // test.
457    VerifyPrefSync();
458  }
459
460 private:
461  DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest);
462};
463
464// Easiest possible test of migration errors: triggers a server
465// migration on one datatype, then modifies some other datatype.
466IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigratePrefsThenModifyBookmark) {
467  RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES),
468                            MODIFY_BOOKMARK);
469}
470
471// Triggers a server migration on two datatypes, then makes a local
472// modification to one of them.
473IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
474                       MigratePrefsAndBookmarksThenModifyBookmark) {
475  RunTwoClientMigrationTest(
476      MakeList(syncer::PREFERENCES, syncer::BOOKMARKS),
477      MODIFY_BOOKMARK);
478}
479
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
485// to zero.
486IN_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
491  // bookmarks.
492  migration_list.push_front(MakeSet(syncer::THEMES));
493  RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
494}
495
496// See crbug.com/124480.
497IN_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
502  // bookmarks.
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);
510}
511
512class MigrationReconfigureTest : public MigrationTwoClientTest {
513 public:
514  MigrationReconfigureTest() {}
515
516  virtual void SetUpCommandLine(base::CommandLine* cl) OVERRIDE {
517    AddTestSwitches(cl);
518    // Do not add optional datatypes.
519  }
520
521  virtual ~MigrationReconfigureTest() {}
522
523 private:
524  DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest);
525};
526
527}  // namespace
528