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