1 // Copyright 2013 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 "chrome/browser/sync/test/integration/profile_sync_service_harness.h" 6 7 #include <cstddef> 8 #include <iterator> 9 #include <ostream> 10 #include <sstream> 11 #include <vector> 12 13 #include "base/compiler_specific.h" 14 #include "base/json/json_writer.h" 15 #include "base/logging.h" 16 #include "base/strings/stringprintf.h" 17 #include "base/timer/timer.h" 18 #include "chrome/browser/profiles/profile.h" 19 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 20 #include "chrome/browser/sync/about_sync_util.h" 21 #include "chrome/browser/sync/profile_sync_service.h" 22 #include "chrome/browser/sync/profile_sync_service_factory.h" 23 #include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h" 24 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h" 25 #include "chrome/common/chrome_switches.h" 26 #include "chrome/common/pref_names.h" 27 #include "components/invalidation/p2p_invalidation_service.h" 28 #include "components/signin/core/browser/profile_oauth2_token_service.h" 29 #include "components/signin/core/browser/signin_manager_base.h" 30 #include "components/sync_driver/data_type_controller.h" 31 #include "google_apis/gaia/gaia_constants.h" 32 #include "sync/internal_api/public/base/progress_marker_map.h" 33 #include "sync/internal_api/public/util/sync_string_conversions.h" 34 35 #if defined(ENABLE_MANAGED_USERS) 36 #include "chrome/browser/supervised_user/supervised_user_constants.h" 37 #endif 38 39 using syncer::sessions::SyncSessionSnapshot; 40 41 namespace { 42 43 bool HasAuthError(ProfileSyncService* service) { 44 return service->GetAuthError().state() == 45 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS || 46 service->GetAuthError().state() == 47 GoogleServiceAuthError::SERVICE_ERROR || 48 service->GetAuthError().state() == 49 GoogleServiceAuthError::REQUEST_CANCELED; 50 } 51 52 class BackendInitializeChecker : public SingleClientStatusChangeChecker { 53 public: 54 explicit BackendInitializeChecker(ProfileSyncService* service) 55 : SingleClientStatusChangeChecker(service) {} 56 57 virtual bool IsExitConditionSatisfied() OVERRIDE { 58 if (service()->backend_mode() != ProfileSyncService::SYNC) 59 return false; 60 if (service()->sync_initialized()) 61 return true; 62 // Backend initialization is blocked by an auth error. 63 if (HasAuthError(service())) 64 return true; 65 // Backend initialization is blocked by a failure to fetch Oauth2 tokens. 66 if (service()->IsRetryingAccessTokenFetchForTest()) 67 return true; 68 // Still waiting on backend initialization. 69 return false; 70 } 71 72 virtual std::string GetDebugMessage() const OVERRIDE { 73 return "Backend Initialize"; 74 } 75 }; 76 77 class SyncSetupChecker : public SingleClientStatusChangeChecker { 78 public: 79 explicit SyncSetupChecker(ProfileSyncService* service) 80 : SingleClientStatusChangeChecker(service) {} 81 82 virtual bool IsExitConditionSatisfied() OVERRIDE { 83 // Sync setup is complete, and the client is ready to sync new changes. 84 if (service()->ShouldPushChanges()) 85 return true; 86 // Sync is blocked because a custom passphrase is required. 87 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) 88 return true; 89 // Sync is blocked by an auth error. 90 if (HasAuthError(service())) 91 return true; 92 // Still waiting on sync setup. 93 return false; 94 } 95 96 virtual std::string GetDebugMessage() const OVERRIDE { 97 return "Sync Setup"; 98 } 99 }; 100 101 bool AwaitSyncSetupCompletion(ProfileSyncService* service) { 102 SyncSetupChecker checker(service); 103 checker.Wait(); 104 return !checker.TimedOut(); 105 } 106 107 } // namespace 108 109 // static 110 ProfileSyncServiceHarness* ProfileSyncServiceHarness::Create( 111 Profile* profile, 112 const std::string& username, 113 const std::string& password) { 114 return new ProfileSyncServiceHarness(profile, username, password); 115 } 116 117 ProfileSyncServiceHarness::ProfileSyncServiceHarness( 118 Profile* profile, 119 const std::string& username, 120 const std::string& password) 121 : profile_(profile), 122 service_(ProfileSyncServiceFactory::GetForProfile(profile)), 123 username_(username), 124 password_(password), 125 oauth2_refesh_token_number_(0), 126 profile_debug_name_(profile->GetDebugName()) { 127 } 128 129 ProfileSyncServiceHarness::~ProfileSyncServiceHarness() { } 130 131 void ProfileSyncServiceHarness::SetCredentials(const std::string& username, 132 const std::string& password) { 133 username_ = username; 134 password_ = password; 135 } 136 137 bool ProfileSyncServiceHarness::SetupSync() { 138 bool result = SetupSync(syncer::ModelTypeSet::All()); 139 if (result == false) { 140 std::string status = GetServiceStatus(); 141 LOG(ERROR) << profile_debug_name_ 142 << ": SetupSync failed. Syncer status:\n" << status; 143 } else { 144 DVLOG(1) << profile_debug_name_ << ": SetupSync successful."; 145 } 146 return result; 147 } 148 149 bool ProfileSyncServiceHarness::SetupSync( 150 syncer::ModelTypeSet synced_datatypes) { 151 // Initialize the sync client's profile sync service object. 152 if (service() == NULL) { 153 LOG(ERROR) << "SetupSync(): service() is null."; 154 return false; 155 } 156 157 // Tell the sync service that setup is in progress so we don't start syncing 158 // until we've finished configuration. 159 service()->SetSetupInProgress(true); 160 161 // Authenticate sync client using GAIA credentials. 162 service()->signin()->SetAuthenticatedUsername(username_); 163 service()->GoogleSigninSucceeded(username_, username_, password_); 164 165 #if defined(ENABLE_MANAGED_USERS) 166 std::string account_id = profile_->IsSupervised() ? 167 supervised_users::kSupervisedUserPseudoEmail : username_; 168 #else 169 std::string account_id = username_; 170 #endif 171 DCHECK(!account_id.empty()); 172 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)-> 173 UpdateCredentials(account_id, GenerateFakeOAuth2RefreshTokenString()); 174 175 // Wait for the OnBackendInitialized() callback. 176 BackendInitializeChecker checker(service()); 177 checker.Wait(); 178 179 if (checker.TimedOut()) { 180 LOG(ERROR) << "OnBackendInitialized() timed out."; 181 return false; 182 } 183 184 if (!service()->sync_initialized()) { 185 return false; 186 } 187 188 // Make sure that initial sync wasn't blocked by a missing passphrase. 189 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) { 190 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed" 191 " until SetDecryptionPassphrase is called."; 192 return false; 193 } 194 195 // Make sure that initial sync wasn't blocked by rejected credentials. 196 if (HasAuthError(service())) { 197 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed."; 198 return false; 199 } 200 201 // Choose the datatypes to be synced. If all datatypes are to be synced, 202 // set sync_everything to true; otherwise, set it to false. 203 bool sync_everything = 204 synced_datatypes.Equals(syncer::ModelTypeSet::All()); 205 service()->OnUserChoseDatatypes(sync_everything, synced_datatypes); 206 207 // Notify ProfileSyncService that we are done with configuration. 208 FinishSyncSetup(); 209 210 // Set an implicit passphrase for encryption if an explicit one hasn't already 211 // been set. If an explicit passphrase has been set, immediately return false, 212 // since a decryption passphrase is required. 213 if (!service()->IsUsingSecondaryPassphrase()) { 214 service()->SetEncryptionPassphrase(password_, ProfileSyncService::IMPLICIT); 215 } else { 216 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed" 217 " until SetDecryptionPassphrase is called."; 218 return false; 219 } 220 221 // Wait for initial sync cycle to be completed. 222 DCHECK(service()->sync_initialized()); 223 if (!AwaitSyncSetupCompletion(service())) { 224 LOG(ERROR) << "Initial sync cycle timed out."; 225 return false; 226 } 227 228 // Make sure that initial sync wasn't blocked by a missing passphrase. 229 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) { 230 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed" 231 " until SetDecryptionPassphrase is called."; 232 return false; 233 } 234 235 // Make sure that initial sync wasn't blocked by rejected credentials. 236 if (service()->GetAuthError().state() == 237 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) { 238 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed."; 239 return false; 240 } 241 242 return true; 243 } 244 245 bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion( 246 ProfileSyncServiceHarness* partner) { 247 std::vector<ProfileSyncServiceHarness*> harnesses; 248 harnesses.push_back(this); 249 harnesses.push_back(partner); 250 return AwaitQuiescence(harnesses); 251 } 252 253 bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion( 254 std::vector<ProfileSyncServiceHarness*>& partners) { 255 return AwaitQuiescence(partners); 256 } 257 258 // static 259 bool ProfileSyncServiceHarness::AwaitQuiescence( 260 std::vector<ProfileSyncServiceHarness*>& clients) { 261 std::vector<ProfileSyncService*> services; 262 if (clients.empty()) { 263 return true; 264 } 265 266 for (std::vector<ProfileSyncServiceHarness*>::iterator it = clients.begin(); 267 it != clients.end(); ++it) { 268 services.push_back((*it)->service()); 269 } 270 QuiesceStatusChangeChecker checker(services); 271 checker.Wait(); 272 return !checker.TimedOut(); 273 } 274 275 std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() { 276 return base::StringPrintf("oauth2_refresh_token_%d", 277 ++oauth2_refesh_token_number_); 278 } 279 280 bool ProfileSyncServiceHarness::IsSyncDisabled() const { 281 return !service()->setup_in_progress() && 282 !service()->HasSyncSetupCompleted(); 283 } 284 285 void ProfileSyncServiceHarness::FinishSyncSetup() { 286 service()->SetSetupInProgress(false); 287 service()->SetSyncSetupCompleted(); 288 } 289 290 SyncSessionSnapshot ProfileSyncServiceHarness::GetLastSessionSnapshot() const { 291 DCHECK(service() != NULL) << "Sync service has not yet been set up."; 292 if (service()->sync_initialized()) { 293 return service()->GetLastSessionSnapshot(); 294 } 295 return SyncSessionSnapshot(); 296 } 297 298 bool ProfileSyncServiceHarness::EnableSyncForDatatype( 299 syncer::ModelType datatype) { 300 DVLOG(1) << GetClientInfoString( 301 "EnableSyncForDatatype(" 302 + std::string(syncer::ModelTypeToString(datatype)) + ")"); 303 304 if (IsSyncDisabled()) 305 return SetupSync(syncer::ModelTypeSet(datatype)); 306 307 if (service() == NULL) { 308 LOG(ERROR) << "EnableSyncForDatatype(): service() is null."; 309 return false; 310 } 311 312 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes(); 313 if (synced_datatypes.Has(datatype)) { 314 DVLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype " 315 << syncer::ModelTypeToString(datatype) 316 << " on " << profile_debug_name_ << "."; 317 return true; 318 } 319 320 synced_datatypes.Put(syncer::ModelTypeFromInt(datatype)); 321 service()->OnUserChoseDatatypes(false, synced_datatypes); 322 if (AwaitSyncSetupCompletion(service())) { 323 DVLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype " 324 << syncer::ModelTypeToString(datatype) 325 << " on " << profile_debug_name_ << "."; 326 return true; 327 } 328 329 DVLOG(0) << GetClientInfoString("EnableSyncForDatatype failed"); 330 return false; 331 } 332 333 bool ProfileSyncServiceHarness::DisableSyncForDatatype( 334 syncer::ModelType datatype) { 335 DVLOG(1) << GetClientInfoString( 336 "DisableSyncForDatatype(" 337 + std::string(syncer::ModelTypeToString(datatype)) + ")"); 338 339 if (service() == NULL) { 340 LOG(ERROR) << "DisableSyncForDatatype(): service() is null."; 341 return false; 342 } 343 344 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes(); 345 if (!synced_datatypes.Has(datatype)) { 346 DVLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype " 347 << syncer::ModelTypeToString(datatype) 348 << " on " << profile_debug_name_ << "."; 349 return true; 350 } 351 352 synced_datatypes.RetainAll(syncer::UserSelectableTypes()); 353 synced_datatypes.Remove(datatype); 354 service()->OnUserChoseDatatypes(false, synced_datatypes); 355 if (AwaitSyncSetupCompletion(service())) { 356 DVLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype " 357 << syncer::ModelTypeToString(datatype) 358 << " on " << profile_debug_name_ << "."; 359 return true; 360 } 361 362 DVLOG(0) << GetClientInfoString("DisableSyncForDatatype failed"); 363 return false; 364 } 365 366 bool ProfileSyncServiceHarness::EnableSyncForAllDatatypes() { 367 DVLOG(1) << GetClientInfoString("EnableSyncForAllDatatypes"); 368 369 if (IsSyncDisabled()) 370 return SetupSync(); 371 372 if (service() == NULL) { 373 LOG(ERROR) << "EnableSyncForAllDatatypes(): service() is null."; 374 return false; 375 } 376 377 service()->OnUserChoseDatatypes(true, syncer::ModelTypeSet::All()); 378 if (AwaitSyncSetupCompletion(service())) { 379 DVLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes " 380 << "on " << profile_debug_name_ << "."; 381 return true; 382 } 383 384 DVLOG(0) << GetClientInfoString("EnableSyncForAllDatatypes failed"); 385 return false; 386 } 387 388 bool ProfileSyncServiceHarness::DisableSyncForAllDatatypes() { 389 DVLOG(1) << GetClientInfoString("DisableSyncForAllDatatypes"); 390 391 if (service() == NULL) { 392 LOG(ERROR) << "DisableSyncForAllDatatypes(): service() is null."; 393 return false; 394 } 395 396 service()->DisableForUser(); 397 398 DVLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all " 399 << "datatypes on " << profile_debug_name_; 400 return true; 401 } 402 403 // TODO(sync): Clean up this method in a separate CL. Remove all snapshot fields 404 // and log shorter, more meaningful messages. 405 std::string ProfileSyncServiceHarness::GetClientInfoString( 406 const std::string& message) const { 407 std::stringstream os; 408 os << profile_debug_name_ << ": " << message << ": "; 409 if (service()) { 410 const SyncSessionSnapshot& snap = GetLastSessionSnapshot(); 411 ProfileSyncService::Status status; 412 service()->QueryDetailedSyncStatus(&status); 413 // Capture select info from the sync session snapshot and syncer status. 414 os << ", has_unsynced_items: " 415 << (service()->sync_initialized() ? service()->HasUnsyncedItems() : 0) 416 << ", did_commit: " 417 << (snap.model_neutral_state().num_successful_commits == 0 && 418 snap.model_neutral_state().commit_result == syncer::SYNCER_OK) 419 << ", encryption conflicts: " 420 << snap.num_encryption_conflicts() 421 << ", hierarchy conflicts: " 422 << snap.num_hierarchy_conflicts() 423 << ", server conflicts: " 424 << snap.num_server_conflicts() 425 << ", num_updates_downloaded : " 426 << snap.model_neutral_state().num_updates_downloaded_total 427 << ", passphrase_required_reason: " 428 << syncer::PassphraseRequiredReasonToString( 429 service()->passphrase_required_reason()) 430 << ", notifications_enabled: " 431 << status.notifications_enabled 432 << ", service_is_pushing_changes: " 433 << service()->ShouldPushChanges(); 434 } else { 435 os << "Sync service not available"; 436 } 437 return os.str(); 438 } 439 440 bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) { 441 return service()->GetPreferredDataTypes().Has(type); 442 } 443 444 std::string ProfileSyncServiceHarness::GetServiceStatus() { 445 scoped_ptr<base::DictionaryValue> value( 446 sync_ui_util::ConstructAboutInformation(service())); 447 std::string service_status; 448 base::JSONWriter::WriteWithOptions(value.get(), 449 base::JSONWriter::OPTIONS_PRETTY_PRINT, 450 &service_status); 451 return service_status; 452 } 453