migration_test.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/single_client_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 SingleClientStatusChangeChecker,
71                         public browser_sync::MigrationObserver {
72 public:
73  explicit MigrationChecker(ProfileSyncServiceHarness* harness)
74      : SingleClientStatusChangeChecker(harness->service()),
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  virtual std::string GetDebugMessage() const OVERRIDE {
102    return "Waiting to migrate (" + ModelTypeSetToString(expected_types_) + ")";
103  }
104
105  bool HasPendingBackendMigration() const {
106    browser_sync::BackendMigrator* migrator =
107        harness_->service()->GetBackendMigratorForTest();
108    return migrator && migrator->state() != browser_sync::BackendMigrator::IDLE;
109  }
110
111  void set_expected_types(syncer::ModelTypeSet expected_types) {
112    expected_types_ = expected_types;
113  }
114
115  syncer::ModelTypeSet migrated_types() const {
116    return migrated_types_;
117  }
118
119  virtual void OnMigrationStateChange() OVERRIDE {
120    if (HasPendingBackendMigration()) {
121      // A new bunch of data types are in the process of being migrated. Merge
122      // them into |pending_types_|.
123      pending_types_.PutAll(
124          harness_->service()->GetBackendMigratorForTest()->
125              GetPendingMigrationTypesForTest());
126      DVLOG(1) << harness_->profile_debug_name()
127               << ": new pending migration types "
128               << syncer::ModelTypeSetToString(pending_types_);
129    } else {
130      // Migration just finished for a bunch of data types. Merge them into
131      // |migrated_types_|.
132      migrated_types_.PutAll(pending_types_);
133      pending_types_.Clear();
134      DVLOG(1) << harness_->profile_debug_name() << ": new migrated types "
135               << syncer::ModelTypeSetToString(migrated_types_);
136    }
137
138    // Nudge ProfileSyncServiceHarness to inspect the exit condition provided by
139    // AwaitMigration.
140    harness_->OnStateChanged();
141  }
142
143 private:
144  // The sync client for which migration is being verified.
145  ProfileSyncServiceHarness* harness_;
146
147  // The set of data types that are expected to eventually undergo migration.
148  syncer::ModelTypeSet expected_types_;
149
150  // The set of data types currently undergoing migration.
151  syncer::ModelTypeSet pending_types_;
152
153  // The set of data types for which migration is complete. Accumulated by
154  // successive calls to OnMigrationStateChanged.
155  syncer::ModelTypeSet migrated_types_;
156
157  DISALLOW_COPY_AND_ASSIGN(MigrationChecker);
158};
159
160class MigrationTest : public SyncTest  {
161 public:
162  explicit MigrationTest(TestType test_type) : SyncTest(test_type) {}
163  virtual ~MigrationTest() {}
164
165  enum TriggerMethod { MODIFY_PREF, MODIFY_BOOKMARK, TRIGGER_NOTIFICATION };
166
167  // Set up sync for all profiles and initialize all MigrationCheckers. This
168  // helps ensure that all migration events are captured, even if they were to
169  // occur before a test calls AwaitMigration for a specific profile.
170  virtual bool SetupSync() OVERRIDE {
171    if (!SyncTest::SetupSync())
172      return false;
173
174    for (int i = 0; i < num_clients(); ++i) {
175      MigrationChecker* checker = new MigrationChecker(GetClient(i));
176      migration_checkers_.push_back(checker);
177    }
178    return true;
179  }
180
181  syncer::ModelTypeSet GetPreferredDataTypes() {
182    // ProfileSyncService must already have been created before we can call
183    // GetPreferredDataTypes().
184    DCHECK(GetClient(0)->service());
185    syncer::ModelTypeSet preferred_data_types =
186        GetClient(0)->service()->GetPreferredDataTypes();
187    preferred_data_types.RemoveAll(syncer::ProxyTypes());
188    // Make sure all clients have the same preferred data types.
189    for (int i = 1; i < num_clients(); ++i) {
190      const syncer::ModelTypeSet other_preferred_data_types =
191          GetClient(i)->service()->GetPreferredDataTypes();
192      EXPECT_TRUE(preferred_data_types.Equals(other_preferred_data_types));
193    }
194    return preferred_data_types;
195  }
196
197  // Returns a MigrationList with every enabled data type in its own
198  // set.
199  MigrationList GetPreferredDataTypesList() {
200    MigrationList migration_list;
201    const syncer::ModelTypeSet preferred_data_types =
202        GetPreferredDataTypes();
203    for (syncer::ModelTypeSet::Iterator it =
204             preferred_data_types.First(); it.Good(); it.Inc()) {
205      migration_list.push_back(MakeSet(it.Get()));
206    }
207    return migration_list;
208  }
209
210  // Trigger a migration for the given types with the given method.
211  void TriggerMigration(syncer::ModelTypeSet model_types,
212                        TriggerMethod trigger_method) {
213    switch (trigger_method) {
214      case MODIFY_PREF:
215        // Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
216        // notification to happen (since model association on a
217        // boolean pref clobbers the local value), so it doesn't work
218        // for anything but single-client tests.
219        ASSERT_EQ(1, num_clients());
220        ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
221        ChangeBooleanPref(0, prefs::kShowHomeButton);
222        break;
223      case MODIFY_BOOKMARK:
224        ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
225        break;
226      case TRIGGER_NOTIFICATION:
227        TriggerNotification(model_types);
228        break;
229      default:
230        ADD_FAILURE();
231    }
232  }
233
234  // Block until all clients have completed migration for the given
235  // types.
236  void AwaitMigration(syncer::ModelTypeSet migrate_types) {
237    for (int i = 0; i < num_clients(); ++i) {
238      MigrationChecker* checker = migration_checkers_[i];
239      checker->set_expected_types(migrate_types);
240      if (!checker->IsExitConditionSatisfied())
241        ASSERT_TRUE(GetClient(i)->AwaitStatusChange(checker));
242    }
243  }
244
245  bool ShouldRunMigrationTest() const {
246    if (!ServerSupportsNotificationControl() ||
247        !ServerSupportsErrorTriggering()) {
248      LOG(WARNING) << "Test skipped in this server environment.";
249      return false;
250    }
251    return true;
252  }
253
254  // Makes sure migration works with the given migration list and
255  // trigger method.
256  void RunMigrationTest(const MigrationList& migration_list,
257                        TriggerMethod trigger_method) {
258    ASSERT_TRUE(ShouldRunMigrationTest());
259
260    // If we have only one client, turn off notifications to avoid the
261    // possibility of spurious sync cycles.
262    bool do_test_without_notifications =
263        (trigger_method != TRIGGER_NOTIFICATION && num_clients() == 1);
264
265    if (do_test_without_notifications) {
266      DisableNotifications();
267    }
268
269    // Make sure migration hasn't been triggered prematurely.
270    for (int i = 0; i < num_clients(); ++i) {
271      ASSERT_TRUE(migration_checkers_[i]->migrated_types().Empty());
272    }
273
274    // Phase 1: Trigger the migrations on the server.
275    for (MigrationList::const_iterator it = migration_list.begin();
276         it != migration_list.end(); ++it) {
277      TriggerMigrationDoneError(*it);
278    }
279
280    // Phase 2: Trigger each migration individually and wait for it to
281    // complete.  (Multiple migrations may be handled by each
282    // migration cycle, but there's no guarantee of that, so we have
283    // to trigger each migration individually.)
284    for (MigrationList::const_iterator it = migration_list.begin();
285         it != migration_list.end(); ++it) {
286      TriggerMigration(*it, trigger_method);
287      AwaitMigration(*it);
288    }
289
290    // Phase 3: Wait for all clients to catch up.
291    //
292    // AwaitQuiescence() will not succeed when notifications are disabled.  We
293    // can safely avoid calling it because we know that, in the single client
294    // case, there is no one else to wait for.
295    //
296    // TODO(rlarocque, 97780): Remove the if condition when the test harness
297    // supports calling AwaitQuiescence() when notifications are disabled.
298    if (!do_test_without_notifications) {
299      AwaitQuiescence();
300    }
301
302    // TODO(rlarocque): It should be possible to re-enable notifications
303    // here, but doing so makes some windows tests flaky.
304  }
305
306 private:
307  // Used to keep track of the migration progress for each sync client.
308  ScopedVector<MigrationChecker> migration_checkers_;
309
310  DISALLOW_COPY_AND_ASSIGN(MigrationTest);
311};
312
313class MigrationSingleClientTest : public MigrationTest {
314 public:
315  MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT) {}
316  virtual ~MigrationSingleClientTest() {}
317
318  void RunSingleClientMigrationTest(const MigrationList& migration_list,
319                                    TriggerMethod trigger_method) {
320    if (!ShouldRunMigrationTest()) {
321      return;
322    }
323    ASSERT_TRUE(SetupSync());
324    RunMigrationTest(migration_list, trigger_method);
325  }
326
327 private:
328  DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest);
329};
330
331// The simplest possible migration tests -- a single data type.
332
333IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
334  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_PREF);
335}
336
337IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
338  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
339                               MODIFY_BOOKMARK);
340}
341
342IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
343                       PrefsOnlyTriggerNotification) {
344  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
345                               TRIGGER_NOTIFICATION);
346}
347
348// Nigori is handled specially, so we test that separately.
349
350IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
351  RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
352                               TRIGGER_NOTIFICATION);
353}
354
355// A little more complicated -- two data types.
356
357IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
358                       BookmarksPrefsIndividually) {
359  RunSingleClientMigrationTest(
360      MakeList(syncer::BOOKMARKS, syncer::PREFERENCES),
361      MODIFY_PREF);
362}
363
364IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
365  RunSingleClientMigrationTest(
366      MakeList(MakeSet(syncer::BOOKMARKS, syncer::PREFERENCES)),
367      MODIFY_BOOKMARK);
368}
369
370// Two data types with one being nigori.
371
372// See crbug.com/124480.
373IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
374                       DISABLED_PrefsNigoriIndividiaully) {
375  RunSingleClientMigrationTest(
376      MakeList(syncer::PREFERENCES, syncer::NIGORI),
377      TRIGGER_NOTIFICATION);
378}
379
380IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
381  RunSingleClientMigrationTest(
382      MakeList(MakeSet(syncer::PREFERENCES, syncer::NIGORI)),
383      MODIFY_PREF);
384}
385
386// The whole shebang -- all data types.
387
388IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesIndividually) {
389  ASSERT_TRUE(SetupClients());
390  RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
391}
392
393IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
394                       AllTypesIndividuallyTriggerNotification) {
395  ASSERT_TRUE(SetupClients());
396  RunSingleClientMigrationTest(GetPreferredDataTypesList(),
397                               TRIGGER_NOTIFICATION);
398}
399
400IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
401  ASSERT_TRUE(SetupClients());
402  RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
403                               MODIFY_PREF);
404}
405
406IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
407                       AllTypesAtOnceTriggerNotification) {
408  ASSERT_TRUE(SetupClients());
409  RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
410                               TRIGGER_NOTIFICATION);
411}
412
413// All data types plus nigori.
414
415// See crbug.com/124480.
416IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
417                       DISABLED_AllTypesWithNigoriIndividually) {
418  ASSERT_TRUE(SetupClients());
419  MigrationList migration_list = GetPreferredDataTypesList();
420  migration_list.push_front(MakeSet(syncer::NIGORI));
421  RunSingleClientMigrationTest(migration_list, MODIFY_BOOKMARK);
422}
423
424IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
425                       AllTypesWithNigoriAtOnce) {
426  ASSERT_TRUE(SetupClients());
427  syncer::ModelTypeSet all_types = GetPreferredDataTypes();
428  all_types.Put(syncer::NIGORI);
429  RunSingleClientMigrationTest(MakeList(all_types), MODIFY_PREF);
430}
431
432class MigrationTwoClientTest : public MigrationTest {
433 public:
434  MigrationTwoClientTest() : MigrationTest(TWO_CLIENT) {}
435  virtual ~MigrationTwoClientTest() {}
436
437  // Helper function that verifies that preferences sync still works.
438  void VerifyPrefSync() {
439    ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
440    ChangeBooleanPref(0, prefs::kShowHomeButton);
441    ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
442    ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
443  }
444
445  void RunTwoClientMigrationTest(const MigrationList& migration_list,
446                                 TriggerMethod trigger_method) {
447    if (!ShouldRunMigrationTest()) {
448      return;
449    }
450    ASSERT_TRUE(SetupSync());
451
452    // Make sure pref sync works before running the migration test.
453    VerifyPrefSync();
454
455    RunMigrationTest(migration_list, trigger_method);
456
457    // Make sure pref sync still works after running the migration
458    // test.
459    VerifyPrefSync();
460  }
461
462 private:
463  DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest);
464};
465
466// Easiest possible test of migration errors: triggers a server
467// migration on one datatype, then modifies some other datatype.
468IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigratePrefsThenModifyBookmark) {
469  RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES),
470                            MODIFY_BOOKMARK);
471}
472
473// Triggers a server migration on two datatypes, then makes a local
474// modification to one of them.
475IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
476                       MigratePrefsAndBookmarksThenModifyBookmark) {
477  RunTwoClientMigrationTest(
478      MakeList(syncer::PREFERENCES, syncer::BOOKMARKS),
479      MODIFY_BOOKMARK);
480}
481
482// Migrate every datatype in sequence; the catch being that the server
483// will only tell the client about the migrations one at a time.
484// TODO(rsimha): This test takes longer than 60 seconds, and will cause tree
485// redness due to sharding.
486// Re-enable this test after syncer::kInitialBackoffShortRetrySeconds is reduced
487// to zero.
488IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
489                       DISABLED_MigrationHellWithoutNigori) {
490  ASSERT_TRUE(SetupClients());
491  MigrationList migration_list = GetPreferredDataTypesList();
492  // Let the first nudge be a datatype that's neither prefs nor
493  // bookmarks.
494  migration_list.push_front(MakeSet(syncer::THEMES));
495  RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
496}
497
498// See crbug.com/124480.
499IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
500                       DISABLED_MigrationHellWithNigori) {
501  ASSERT_TRUE(SetupClients());
502  MigrationList migration_list = GetPreferredDataTypesList();
503  // Let the first nudge be a datatype that's neither prefs nor
504  // bookmarks.
505  migration_list.push_front(MakeSet(syncer::THEMES));
506  // Pop off one so that we don't migrate all data types; the syncer
507  // freaks out if we do that (see http://crbug.com/94882).
508  ASSERT_GE(migration_list.size(), 2u);
509  ASSERT_FALSE(migration_list.back().Equals(MakeSet(syncer::NIGORI)));
510  migration_list.back() = MakeSet(syncer::NIGORI);
511  RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
512}
513
514class MigrationReconfigureTest : public MigrationTwoClientTest {
515 public:
516  MigrationReconfigureTest() {}
517
518  virtual void SetUpCommandLine(base::CommandLine* cl) OVERRIDE {
519    AddTestSwitches(cl);
520    // Do not add optional datatypes.
521  }
522
523  virtual ~MigrationReconfigureTest() {}
524
525 private:
526  DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest);
527};
528
529}  // namespace
530