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