Home | History | Annotate | Download | only in integration
      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()->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 
     75 class 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 
     99 bool AwaitSyncSetupCompletion(ProfileSyncService* service) {
    100   SyncSetupChecker checker(service);
    101   checker.Wait();
    102   return !checker.TimedOut();
    103 }
    104 
    105 }  // namespace
    106 
    107 // static
    108 ProfileSyncServiceHarness* ProfileSyncServiceHarness::Create(
    109     Profile* profile,
    110     const std::string& username,
    111     const std::string& password) {
    112   return new ProfileSyncServiceHarness(profile, username, password);
    113 }
    114 
    115 ProfileSyncServiceHarness::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 
    127 ProfileSyncServiceHarness::~ProfileSyncServiceHarness() { }
    128 
    129 void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
    130                                                const std::string& password) {
    131   username_ = username;
    132   password_ = password;
    133 }
    134 
    135 bool 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 
    147 bool 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 
    243 bool 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 
    251 bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
    252     std::vector<ProfileSyncServiceHarness*>& partners) {
    253   return AwaitQuiescence(partners);
    254 }
    255 
    256 // static
    257 bool 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 
    273 std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
    274   return base::StringPrintf("oauth2_refresh_token_%d",
    275                             ++oauth2_refesh_token_number_);
    276 }
    277 
    278 bool ProfileSyncServiceHarness::IsSyncDisabled() const {
    279   return !service()->setup_in_progress() &&
    280          !service()->HasSyncSetupCompleted();
    281 }
    282 
    283 void ProfileSyncServiceHarness::FinishSyncSetup() {
    284   service()->SetSetupInProgress(false);
    285   service()->SetSyncSetupCompleted();
    286 }
    287 
    288 SyncSessionSnapshot 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 
    296 bool 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 
    331 bool 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 
    364 bool 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 
    386 bool 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.
    403 std::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 
    438 bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) {
    439   return service()->GetPreferredDataTypes().Has(type);
    440 }
    441 
    442 std::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