Home | History | Annotate | Download | only in integration
      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 
     20 using bookmarks_helper::AddURL;
     21 using bookmarks_helper::IndexedURL;
     22 using bookmarks_helper::IndexedURLTitle;
     23 
     24 using preferences_helper::BooleanPrefMatches;
     25 using preferences_helper::ChangeBooleanPref;
     26 
     27 namespace {
     28 
     29 // Utility functions to make a model type set out of a small number of
     30 // model types.
     31 
     32 syncer::ModelTypeSet MakeSet(syncer::ModelType type) {
     33   return syncer::ModelTypeSet(type);
     34 }
     35 
     36 syncer::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().
     43 typedef 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 
     48 MigrationList MakeList(syncer::ModelTypeSet model_types) {
     49   return MigrationList(1, model_types);
     50 }
     51 
     52 MigrationList 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 
     60 MigrationList MakeList(syncer::ModelType type) {
     61   return MakeList(MakeSet(type));
     62 }
     63 
     64 MigrationList 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.
     71 class 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 
    161 class 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     // Make sure all clients have the same preferred data types.
    190     for (int i = 1; i < num_clients(); ++i) {
    191       const syncer::ModelTypeSet other_preferred_data_types =
    192           GetSyncService((i))->GetPreferredDataTypes();
    193       EXPECT_TRUE(preferred_data_types.Equals(other_preferred_data_types));
    194     }
    195     return preferred_data_types;
    196   }
    197 
    198   // Returns a MigrationList with every enabled data type in its own
    199   // set.
    200   MigrationList GetPreferredDataTypesList() {
    201     MigrationList migration_list;
    202     const syncer::ModelTypeSet preferred_data_types =
    203         GetPreferredDataTypes();
    204     for (syncer::ModelTypeSet::Iterator it =
    205              preferred_data_types.First(); it.Good(); it.Inc()) {
    206       migration_list.push_back(MakeSet(it.Get()));
    207     }
    208     return migration_list;
    209   }
    210 
    211   // Trigger a migration for the given types with the given method.
    212   void TriggerMigration(syncer::ModelTypeSet model_types,
    213                         TriggerMethod trigger_method) {
    214     switch (trigger_method) {
    215       case MODIFY_PREF:
    216         // Unlike MODIFY_BOOKMARK, MODIFY_PREF doesn't cause a
    217         // notification to happen (since model association on a
    218         // boolean pref clobbers the local value), so it doesn't work
    219         // for anything but single-client tests.
    220         ASSERT_EQ(1, num_clients());
    221         ASSERT_TRUE(BooleanPrefMatches(prefs::kShowHomeButton));
    222         ChangeBooleanPref(0, prefs::kShowHomeButton);
    223         break;
    224       case MODIFY_BOOKMARK:
    225         ASSERT_TRUE(AddURL(0, IndexedURLTitle(0), GURL(IndexedURL(0))));
    226         break;
    227       case TRIGGER_NOTIFICATION:
    228         TriggerNotification(model_types);
    229         break;
    230       default:
    231         ADD_FAILURE();
    232     }
    233   }
    234 
    235   // Block until all clients have completed migration for the given
    236   // types.
    237   void AwaitMigration(syncer::ModelTypeSet migrate_types) {
    238     for (int i = 0; i < num_clients(); ++i) {
    239       MigrationChecker* checker = migration_checkers_[i];
    240       checker->set_expected_types(migrate_types);
    241       checker->Wait();
    242       ASSERT_FALSE(checker->TimedOut());
    243     }
    244   }
    245 
    246   bool ShouldRunMigrationTest() const {
    247     if (!ServerSupportsNotificationControl() ||
    248         !ServerSupportsErrorTriggering()) {
    249       LOG(WARNING) << "Test skipped in this server environment.";
    250       return false;
    251     }
    252     return true;
    253   }
    254 
    255   // Makes sure migration works with the given migration list and
    256   // trigger method.
    257   void RunMigrationTest(const MigrationList& migration_list,
    258                         TriggerMethod trigger_method) {
    259     ASSERT_TRUE(ShouldRunMigrationTest());
    260 
    261     // If we have only one client, turn off notifications to avoid the
    262     // possibility of spurious sync cycles.
    263     bool do_test_without_notifications =
    264         (trigger_method != TRIGGER_NOTIFICATION && num_clients() == 1);
    265 
    266     if (do_test_without_notifications) {
    267       DisableNotifications();
    268     }
    269 
    270     // Make sure migration hasn't been triggered prematurely.
    271     for (int i = 0; i < num_clients(); ++i) {
    272       ASSERT_TRUE(migration_checkers_[i]->migrated_types().Empty());
    273     }
    274 
    275     // Phase 1: Trigger the migrations on the server.
    276     for (MigrationList::const_iterator it = migration_list.begin();
    277          it != migration_list.end(); ++it) {
    278       TriggerMigrationDoneError(*it);
    279     }
    280 
    281     // Phase 2: Trigger each migration individually and wait for it to
    282     // complete.  (Multiple migrations may be handled by each
    283     // migration cycle, but there's no guarantee of that, so we have
    284     // to trigger each migration individually.)
    285     for (MigrationList::const_iterator it = migration_list.begin();
    286          it != migration_list.end(); ++it) {
    287       TriggerMigration(*it, trigger_method);
    288       AwaitMigration(*it);
    289     }
    290 
    291     // Phase 3: Wait for all clients to catch up.
    292     //
    293     // AwaitQuiescence() will not succeed when notifications are disabled.  We
    294     // can safely avoid calling it because we know that, in the single client
    295     // case, there is no one else to wait for.
    296     //
    297     // TODO(rlarocque, 97780): Remove the if condition when the test harness
    298     // supports calling AwaitQuiescence() when notifications are disabled.
    299     if (!do_test_without_notifications) {
    300       AwaitQuiescence();
    301     }
    302 
    303     // TODO(rlarocque): It should be possible to re-enable notifications
    304     // here, but doing so makes some windows tests flaky.
    305   }
    306 
    307  private:
    308   // Used to keep track of the migration progress for each sync client.
    309   ScopedVector<MigrationChecker> migration_checkers_;
    310 
    311   DISALLOW_COPY_AND_ASSIGN(MigrationTest);
    312 };
    313 
    314 class MigrationSingleClientTest : public MigrationTest {
    315  public:
    316   MigrationSingleClientTest() : MigrationTest(SINGLE_CLIENT) {}
    317   virtual ~MigrationSingleClientTest() {}
    318 
    319   void RunSingleClientMigrationTest(const MigrationList& migration_list,
    320                                     TriggerMethod trigger_method) {
    321     if (!ShouldRunMigrationTest()) {
    322       return;
    323     }
    324     ASSERT_TRUE(SetupSync());
    325     RunMigrationTest(migration_list, trigger_method);
    326   }
    327 
    328  private:
    329   DISALLOW_COPY_AND_ASSIGN(MigrationSingleClientTest);
    330 };
    331 
    332 // The simplest possible migration tests -- a single data type.
    333 
    334 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyPref) {
    335   RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES), MODIFY_PREF);
    336 }
    337 
    338 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsOnlyModifyBookmark) {
    339   RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
    340                                MODIFY_BOOKMARK);
    341 }
    342 
    343 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
    344                        PrefsOnlyTriggerNotification) {
    345   RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
    346                                TRIGGER_NOTIFICATION);
    347 }
    348 
    349 // Nigori is handled specially, so we test that separately.
    350 
    351 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, NigoriOnly) {
    352   RunSingleClientMigrationTest(MakeList(syncer::PREFERENCES),
    353                                TRIGGER_NOTIFICATION);
    354 }
    355 
    356 // A little more complicated -- two data types.
    357 
    358 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
    359                        BookmarksPrefsIndividually) {
    360   RunSingleClientMigrationTest(
    361       MakeList(syncer::BOOKMARKS, syncer::PREFERENCES),
    362       MODIFY_PREF);
    363 }
    364 
    365 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, BookmarksPrefsBoth) {
    366   RunSingleClientMigrationTest(
    367       MakeList(MakeSet(syncer::BOOKMARKS, syncer::PREFERENCES)),
    368       MODIFY_BOOKMARK);
    369 }
    370 
    371 // Two data types with one being nigori.
    372 
    373 // See crbug.com/124480.
    374 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
    375                        DISABLED_PrefsNigoriIndividiaully) {
    376   RunSingleClientMigrationTest(
    377       MakeList(syncer::PREFERENCES, syncer::NIGORI),
    378       TRIGGER_NOTIFICATION);
    379 }
    380 
    381 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, PrefsNigoriBoth) {
    382   RunSingleClientMigrationTest(
    383       MakeList(MakeSet(syncer::PREFERENCES, syncer::NIGORI)),
    384       MODIFY_PREF);
    385 }
    386 
    387 // The whole shebang -- all data types.
    388 
    389 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesIndividually) {
    390   ASSERT_TRUE(SetupClients());
    391   RunSingleClientMigrationTest(GetPreferredDataTypesList(), MODIFY_BOOKMARK);
    392 }
    393 
    394 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
    395                        AllTypesIndividuallyTriggerNotification) {
    396   ASSERT_TRUE(SetupClients());
    397   RunSingleClientMigrationTest(GetPreferredDataTypesList(),
    398                                TRIGGER_NOTIFICATION);
    399 }
    400 
    401 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest, AllTypesAtOnce) {
    402   ASSERT_TRUE(SetupClients());
    403   RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
    404                                MODIFY_PREF);
    405 }
    406 
    407 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
    408                        AllTypesAtOnceTriggerNotification) {
    409   ASSERT_TRUE(SetupClients());
    410   RunSingleClientMigrationTest(MakeList(GetPreferredDataTypes()),
    411                                TRIGGER_NOTIFICATION);
    412 }
    413 
    414 // All data types plus nigori.
    415 
    416 // See crbug.com/124480.
    417 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
    418                        DISABLED_AllTypesWithNigoriIndividually) {
    419   ASSERT_TRUE(SetupClients());
    420   MigrationList migration_list = GetPreferredDataTypesList();
    421   migration_list.push_front(MakeSet(syncer::NIGORI));
    422   RunSingleClientMigrationTest(migration_list, MODIFY_BOOKMARK);
    423 }
    424 
    425 IN_PROC_BROWSER_TEST_F(MigrationSingleClientTest,
    426                        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 
    433 class MigrationTwoClientTest : public MigrationTest {
    434  public:
    435   MigrationTwoClientTest() : MigrationTest(TWO_CLIENT) {}
    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     if (!ShouldRunMigrationTest()) {
    449       return;
    450     }
    451     ASSERT_TRUE(SetupSync());
    452 
    453     // Make sure pref sync works before running the migration test.
    454     VerifyPrefSync();
    455 
    456     RunMigrationTest(migration_list, trigger_method);
    457 
    458     // Make sure pref sync still works after running the migration
    459     // test.
    460     VerifyPrefSync();
    461   }
    462 
    463  private:
    464   DISALLOW_COPY_AND_ASSIGN(MigrationTwoClientTest);
    465 };
    466 
    467 // Easiest possible test of migration errors: triggers a server
    468 // migration on one datatype, then modifies some other datatype.
    469 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest, MigratePrefsThenModifyBookmark) {
    470   RunTwoClientMigrationTest(MakeList(syncer::PREFERENCES),
    471                             MODIFY_BOOKMARK);
    472 }
    473 
    474 // Triggers a server migration on two datatypes, then makes a local
    475 // modification to one of them.
    476 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
    477                        MigratePrefsAndBookmarksThenModifyBookmark) {
    478   RunTwoClientMigrationTest(
    479       MakeList(syncer::PREFERENCES, syncer::BOOKMARKS),
    480       MODIFY_BOOKMARK);
    481 }
    482 
    483 // Migrate every datatype in sequence; the catch being that the server
    484 // will only tell the client about the migrations one at a time.
    485 // TODO(rsimha): This test takes longer than 60 seconds, and will cause tree
    486 // redness due to sharding.
    487 // Re-enable this test after syncer::kInitialBackoffShortRetrySeconds is reduced
    488 // to zero.
    489 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
    490                        DISABLED_MigrationHellWithoutNigori) {
    491   ASSERT_TRUE(SetupClients());
    492   MigrationList migration_list = GetPreferredDataTypesList();
    493   // Let the first nudge be a datatype that's neither prefs nor
    494   // bookmarks.
    495   migration_list.push_front(MakeSet(syncer::THEMES));
    496   RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
    497 }
    498 
    499 // See crbug.com/124480.
    500 IN_PROC_BROWSER_TEST_F(MigrationTwoClientTest,
    501                        DISABLED_MigrationHellWithNigori) {
    502   ASSERT_TRUE(SetupClients());
    503   MigrationList migration_list = GetPreferredDataTypesList();
    504   // Let the first nudge be a datatype that's neither prefs nor
    505   // bookmarks.
    506   migration_list.push_front(MakeSet(syncer::THEMES));
    507   // Pop off one so that we don't migrate all data types; the syncer
    508   // freaks out if we do that (see http://crbug.com/94882).
    509   ASSERT_GE(migration_list.size(), 2u);
    510   ASSERT_FALSE(migration_list.back().Equals(MakeSet(syncer::NIGORI)));
    511   migration_list.back() = MakeSet(syncer::NIGORI);
    512   RunTwoClientMigrationTest(migration_list, MODIFY_BOOKMARK);
    513 }
    514 
    515 class MigrationReconfigureTest : public MigrationTwoClientTest {
    516  public:
    517   MigrationReconfigureTest() {}
    518 
    519   virtual void SetUpCommandLine(base::CommandLine* cl) OVERRIDE {
    520     AddTestSwitches(cl);
    521     // Do not add optional datatypes.
    522   }
    523 
    524   virtual ~MigrationReconfigureTest() {}
    525 
    526  private:
    527   DISALLOW_COPY_AND_ASSIGN(MigrationReconfigureTest);
    528 };
    529 
    530 }  // namespace
    531