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