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