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()->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
77class 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
101bool AwaitSyncSetupCompletion(ProfileSyncService* service) {
102  SyncSetupChecker checker(service);
103  checker.Wait();
104  return !checker.TimedOut();
105}
106
107}  // namespace
108
109// static
110ProfileSyncServiceHarness* ProfileSyncServiceHarness::Create(
111    Profile* profile,
112    const std::string& username,
113    const std::string& password) {
114  return new ProfileSyncServiceHarness(profile, username, password);
115}
116
117ProfileSyncServiceHarness::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
129ProfileSyncServiceHarness::~ProfileSyncServiceHarness() { }
130
131void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
132                                               const std::string& password) {
133  username_ = username;
134  password_ = password;
135}
136
137bool 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
149bool 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
245bool 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
253bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
254    std::vector<ProfileSyncServiceHarness*>& partners) {
255  return AwaitQuiescence(partners);
256}
257
258// static
259bool 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
275std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
276  return base::StringPrintf("oauth2_refresh_token_%d",
277                            ++oauth2_refesh_token_number_);
278}
279
280bool ProfileSyncServiceHarness::IsSyncDisabled() const {
281  return !service()->setup_in_progress() &&
282         !service()->HasSyncSetupCompleted();
283}
284
285void ProfileSyncServiceHarness::FinishSyncSetup() {
286  service()->SetSetupInProgress(false);
287  service()->SetSyncSetupCompleted();
288}
289
290SyncSessionSnapshot 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
298bool 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
333bool 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
366bool 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
388bool 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.
405std::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
440bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) {
441  return service()->GetPreferredDataTypes().Has(type);
442}
443
444std::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