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