migration_test.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/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"
18
19using bookmarks_helper::AddURL;
20using bookmarks_helper::IndexedURL;
21using bookmarks_helper::IndexedURLTitle;
22
23using preferences_helper::BooleanPrefMatches;
24using preferences_helper::ChangeBooleanPref;
25
26namespace {
27
28// Utility functions to make a model type set out of a small number of
29// model types.
30
31syncer::ModelTypeSet MakeSet(syncer::ModelType type) {
32  return syncer::ModelTypeSet(type);
33}
34
35syncer::ModelTypeSet MakeSet(syncer::ModelType type1,
36                             syncer::ModelType type2) {
37  return syncer::ModelTypeSet(type1, type2);
38}
39
40// An ordered list of model types sets to migrate.  Used by
41// RunMigrationTest().
42typedef std::deque<syncer::ModelTypeSet> MigrationList;
43
44// Utility functions to make a MigrationList out of a small number of
45// model types / model type sets.
46
47MigrationList MakeList(syncer::ModelTypeSet model_types) {
48  return MigrationList(1, model_types);
49}
50
51MigrationList 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;
57}
58
59MigrationList MakeList(syncer::ModelType type) {
60  return MakeList(MakeSet(type));
61}
62
63MigrationList MakeList(syncer::ModelType type1,
64                       syncer::ModelType type2) {
65  return MakeList(MakeSet(type1), MakeSet(type2));
66}
67
68// Helper class that checks if the sync backend has successfully completed
69// migration for a set of data types.
70class MigrationChecker : public StatusChangeChecker,
71                         public browser_sync::MigrationObserver {
72 public:
73  explicit MigrationChecker(ProfileSyncServiceHarness* harness)
74      : StatusChangeChecker("MigrationChecker"),
75        harness_(harness) {
76    DCHECK(harness_);
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.
81    DCHECK(migrator);
82    migrator->AddMigrationObserver(this);
83  }
84
85  virtual ~MigrationChecker() {}
86
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 " :
95                                               " does not contain ")
96             << syncer::ModelTypeSetToString(expected_types_);
97    return all_expected_types_migrated &&
98           !HasPendingBackendMigration();
99  }
100
101  bool HasPendingBackendMigration() const {
102    browser_sync::BackendMigrator* migrator =
103        harness_->service()->GetBackendMigratorForTest();
104    return migrator && migrator->state() != browser_sync::BackendMigrator::IDLE;
105  }
106
107  void set_expected_types(syncer::ModelTypeSet expected_types) {
108    expected_types_ = expected_types;
109  }
110
111  syncer::ModelTypeSet migrated_types() const {
112    return migrated_types_;
113  }
114
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_);
125    } else {
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_);
132    }
133
134    // Nudge ProfileSyncServiceHarness to inspect the exit condition provided by
135    // AwaitMigration.
136    harness_->OnStateChanged();
137  }
138
139 private:
140  // The sync client for which migration is being verified.
141  ProfileSyncServiceHarness* harness_;
142
143  // The set of data types that are expected to eventually undergo migration.
144  syncer::ModelTypeSet expected_types_;
145
146  // The set of data types currently undergoing migration.
147  syncer::ModelTypeSet pending_types_;
148
149  // The set of data types for which migration is complete. Accumulated by
150  // successive calls to OnMigrationStateChanged.
151  syncer::ModelTypeSet migrated_types_;
152
153  DISALLOW_COPY_AND_ASSIGN(MigrationChecker);
154};
155
156class MigrationTest : public SyncTest  {
157 public:
158  explicit MigrationTest(TestType test_type) : SyncTest(test_type) {}
159  virtual ~MigrationTest() {}
160
161  enum TriggerMethod { MODIFY_PREF, MODIFY_BOOKMARK, TRIGGER_NOTIFICATION };
162
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())
168      return false;
169
170    for (int i = 0; i < num_clients(); ++i) {
171      MigrationChecker* checker = new MigrationChecker(GetClient(i));
172      migration_checkers_.push_back(checker);
173    }
174    return true;
175  }
176
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));
189    }
190    return preferred_data_types;
191  }
192
193  // Returns a MigrationList with every enabled data type in its own
194  // set.
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()));
202    }
203    return migration_list;
204  }
205
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) {
210      case MODIFY_PREF:
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);
218        break;
219      case MODIFY_BOOKMARK:
220        ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
221        break;
222      case TRIGGER_NOTIFICATION:
223        TriggerNotification(model_types);
224        break;
225      default:
226        ADD_FAILURE();
227    }
228  }
229
230  // Block until all clients have completed migration for the given
231  // types.
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"));
238    }
239  }
240
241  bool ShouldRunMigrationTest() const {
242    if (!ServerSupportsNotificationControl() ||
243        !ServerSupportsErrorTriggering()) {
244      LOG(WARNING) << "Test skipped in this server environment.";
245      return false;
246    }
247    return true;
248  }
249
250  // Makes sure migration works with the given migration list and
251  // trigger method.
252  void RunMigrationTest(const MigrationList& migration_list,
253                        TriggerMethod trigger_method) {
254    ASSERT_TRUE(ShouldRunMigrationTest());
255
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);
260
261    if (do_test_without_notifications) {
262      DisableNotifications();
263    }
264
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());
268    }
269
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);
274    }
275
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);
283      AwaitMigration(*it);
284    }
285
286    // Phase 3: Wait for all clients to catch up.
287    //
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.
291    //
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) {
295      AwaitQuiescence();
296    }
297
298    // TODO(rlarocque): It should be possible to re-enable notifications
299    // here, but doing so makes some windows tests flaky.
300  }
301
302 private:
303  // Used to keep track of the migration progress for each sync client.
304  ScopedVector<MigrationChecker> migration_checkers_;
305
306  DISALLOW_COPY_AND_ASSIGN(MigrationTest);
307};
308
309class MigrationSingleClientTest : public MigrationTest {
310 public:
311  MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT) {}
312  virtual ~MigrationSingleClientTest() {}
313
314  void RunSingleClientMigrationTest(const MigrationList& migration_list,
315                                    TriggerMethod trigger_method) {
316    if (!ShouldRunMigrationTest()) {
317      return;
318    }
319    ASSERT_TRUE(SetupSync());
320    RunMigrationTest(migration_list, trigger_method);
321  }
322
323 private:
324  DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest);
325};
326
327// The simplest possible migration tests -- a single data type.
328
329IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
330  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_PREF);
331}
332
333IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
334  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
335                               MODIFY_BOOKMARK);
336}
337
338IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
339                       PrefsOnlyTriggerNotification) {
340  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
341                               TRIGGER_NOTIFICATION);
342}
343
344// Nigori is handled specially, so we test that separately.
345
346IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
347  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
348                               TRIGGER_NOTIFICATION);
349}
350
351// A little more complicated -- two data types.
352
353IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
354                       BookmarksPrefsIndividually) {
355  RunSingleClientMigrationTest(
356      MakeList(syncer::BOOKMARKS, syncer::PREFERENCES),
357      MODIFY_PREF);
358}
359
360IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
361  RunSingleClientMigrationTest(
362      MakeList(MakeSet(syncer::BOOKMARKS, syncer::PREFERENCES)),
363      MODIFY_BOOKMARK);
364}
365
366// Two data types with one being nigori.
367
368// See crbug.com/124480.
369IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
370                       DISABLED_PrefsNigoriIndividiaully) {
371  RunSingleClientMigrationTest(
372      MakeList(syncer::PREFERENCES, syncer::NIGORI),
373      TRIGGER_NOTIFICATION);
374}
375
376IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
377  RunSingleClientMigrationTest(
378      MakeList(MakeSet(syncer::PREFERENCES, syncer::NIGORI)),
379      MODIFY_PREF);
380}
381
382// The whole shebang -- all data types.
383
384IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesIndividually) {
385  ASSERT_TRUE(SetupClients());
386  RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
387}
388
389IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
390                       AllTypesIndividuallyTriggerNotification) {
391  ASSERT_TRUE(SetupClients());
392  RunSingleClientMigrationTest(GetPreferredDataTypesList(),
393                               TRIGGER_NOTIFICATION);
394}
395
396IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
397  ASSERT_TRUE(SetupClients());
398  RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
399                               MODIFY_PREF);
400}
401
402IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
403                       AllTypesAtOnceTriggerNotification) {
404  ASSERT_TRUE(SetupClients());
405  RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
406                               TRIGGER_NOTIFICATION);
407}
408
409// All data types plus nigori.
410
411// See crbug.com/124480.
412IN_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);
418}
419
420IN_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);
426}
427
428class MigrationTwoClientTest : public MigrationTest {
429 public:
430  MigrationTwoClientTest() : MigrationTest(TWO_CLIENT) {}
431  virtual ~MigrationTwoClientTest() {}
432
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));
439  }
440
441  void RunTwoClientMigrationTest(const MigrationList& migration_list,
442                                 TriggerMethod trigger_method) {
443    if (!ShouldRunMigrationTest()) {
444      return;
445    }
446    ASSERT_TRUE(SetupSync());
447
448    // Make sure pref sync works before running the migration test.
449    VerifyPrefSync();
450
451    RunMigrationTest(migration_list, trigger_method);
452
453    // Make sure pref sync still works after running the migration
454    // test.
455    VerifyPrefSync();
456  }
457
458 private:
459  DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest);
460};
461
462// Easiest possible test of migration errors: triggers a server
463// migration on one datatype, then modifies some other datatype.
464IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigratePrefsThenModifyBookmark) {
465  RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES),
466                            MODIFY_BOOKMARK);
467}
468
469// Triggers a server migration on two datatypes, then makes a local
470// modification to one of them.
471IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
472                       MigratePrefsAndBookmarksThenModifyBookmark) {
473  RunTwoClientMigrationTest(
474      MakeList(syncer::PREFERENCES, syncer::BOOKMARKS),
475      MODIFY_BOOKMARK);
476}
477
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
483// to zero.
484IN_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
489  // bookmarks.
490  migration_list.push_front(MakeSet(syncer::THEMES));
491  RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
492}
493
494// See crbug.com/124480.
495IN_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
500  // bookmarks.
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);
508}
509
510class MigrationReconfigureTest : public MigrationTwoClientTest {
511 public:
512  MigrationReconfigureTest() {}
513
514  virtual void SetUpCommandLine(CommandLine* cl) OVERRIDE {
515    AddTestSwitches(cl);
516    // Do not add optional datatypes.
517  }
518
519  virtual ~MigrationReconfigureTest() {}
520
521 private:
522  DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest);
523};
524
525}  // namespace
526