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()->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