profile_sync_service_harness.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
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 <set>
11#include <sstream>
12#include <vector>
13
14#include "base/bind.h"
15#include "base/command_line.h"
16#include "base/compiler_specific.h"
17#include "base/json/json_writer.h"
18#include "base/location.h"
19#include "base/logging.h"
20#include "base/message_loop/message_loop.h"
21#include "base/prefs/pref_service.h"
22#include "base/strings/stringprintf.h"
23#include "base/timer/timer.h"
24#include "chrome/browser/chrome_notification_types.h"
25#include "chrome/browser/invalidation/p2p_invalidation_service.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28#include "chrome/browser/signin/signin_manager_base.h"
29#include "chrome/browser/sync/about_sync_util.h"
30#include "chrome/browser/sync/backend_migrator.h"
31#include "chrome/browser/sync/profile_sync_service_factory.h"
32#include "chrome/browser/sync/test/integration/p2p_invalidation_forwarder.h"
33#include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
34#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
35#include "chrome/browser/sync/test/integration/status_change_checker.h"
36#include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
37#include "chrome/common/chrome_switches.h"
38#include "chrome/common/pref_names.h"
39#include "components/signin/core/profile_oauth2_token_service.h"
40#include "components/sync_driver/data_type_controller.h"
41#include "content/public/browser/notification_service.h"
42#include "google_apis/gaia/gaia_constants.h"
43#include "sync/internal_api/public/base/progress_marker_map.h"
44#include "sync/internal_api/public/sessions/sync_session_snapshot.h"
45#include "sync/internal_api/public/util/sync_string_conversions.h"
46
47#if defined(ENABLE_MANAGED_USERS)
48#include "chrome/browser/managed_mode/managed_user_constants.h"
49#endif
50
51using invalidation::P2PInvalidationService;
52using syncer::sessions::SyncSessionSnapshot;
53
54// The amount of time for which we wait for a sync operation to complete.
55// TODO(sync): This timeout must eventually be made less than the default 45
56// second timeout for integration tests so that in case a sync operation times
57// out, it is able to log a useful failure message before the test is killed.
58static const int kSyncOperationTimeoutMs = 45000;
59
60namespace {
61
62// Checks if a desired change in the state of the sync engine has taken place by
63// running the callback passed to it.
64class CallbackStatusChecker : public SingleClientStatusChangeChecker {
65 public:
66  CallbackStatusChecker(ProfileSyncService* service,
67                        base::Callback<bool()> callback,
68                        const std::string& debug_message)
69      : SingleClientStatusChangeChecker(service),
70        callback_(callback),
71        debug_message_(debug_message) {
72  }
73
74  virtual ~CallbackStatusChecker() {
75  }
76
77  virtual bool IsExitConditionSatisfied() OVERRIDE {
78    return callback_.Run();
79  }
80
81  virtual std::string GetDebugMessage() const OVERRIDE {
82    return debug_message_;
83  }
84
85 private:
86  // Callback that evaluates whether the condition we are waiting on has been
87  // satisfied.
88  base::Callback<bool()> callback_;
89
90  const std::string debug_message_;
91
92  DISALLOW_COPY_AND_ASSIGN(CallbackStatusChecker);
93};
94
95// Helper function which returns true if the sync backend has been initialized,
96// or if backend initialization was blocked for some reason.
97bool DoneWaitingForBackendInitialization(
98    const ProfileSyncServiceHarness* harness) {
99  DCHECK(harness);
100  // Backend is initialized.
101  if (harness->service()->sync_initialized())
102    return true;
103  // Backend initialization is blocked by an auth error.
104  if (harness->HasAuthError())
105    return true;
106  // Backend initialization is blocked by a failure to fetch Oauth2 tokens.
107  if (harness->service()->IsRetryingAccessTokenFetchForTest())
108    return true;
109  // Still waiting on backend initialization.
110  return false;
111}
112
113// Helper function which returns true if sync setup is complete, or in case
114// it is blocked for some reason.
115bool DoneWaitingForSyncSetup(const ProfileSyncServiceHarness* harness) {
116  DCHECK(harness);
117  // Sync setup is complete, and the client is ready to sync new changes.
118  if (harness->ServiceIsPushingChanges())
119    return true;
120  // Sync is blocked because a custom passphrase is required.
121  if (harness->service()->passphrase_required_reason() ==
122      syncer::REASON_DECRYPTION) {
123    return true;
124  }
125  // Sync is blocked by an auth error.
126  if (harness->HasAuthError())
127    return true;
128  // Still waiting on sync setup.
129  return false;
130}
131
132// Helper function which returns true if the sync client requires a custom
133// passphrase to be entered for decryption.
134bool IsPassphraseRequired(const ProfileSyncServiceHarness* harness) {
135  DCHECK(harness);
136  return harness->service()->IsPassphraseRequired();
137}
138
139// Helper function which returns true if the custom passphrase entered was
140// accepted.
141bool IsPassphraseAccepted(const ProfileSyncServiceHarness* harness) {
142  DCHECK(harness);
143  return (!harness->service()->IsPassphraseRequired() &&
144          harness->service()->IsUsingSecondaryPassphrase());
145}
146
147}  // namespace
148
149// static
150ProfileSyncServiceHarness* ProfileSyncServiceHarness::Create(
151    Profile* profile,
152    const std::string& username,
153    const std::string& password) {
154  return new ProfileSyncServiceHarness(profile, username, password, NULL);
155}
156
157// static
158ProfileSyncServiceHarness* ProfileSyncServiceHarness::CreateForIntegrationTest(
159    Profile* profile,
160    const std::string& username,
161    const std::string& password,
162    P2PInvalidationService* p2p_invalidation_service) {
163  return new ProfileSyncServiceHarness(profile,
164                                       username,
165                                       password,
166                                       p2p_invalidation_service);
167}
168
169ProfileSyncServiceHarness::ProfileSyncServiceHarness(
170    Profile* profile,
171    const std::string& username,
172    const std::string& password,
173    P2PInvalidationService* p2p_invalidation_service)
174    : profile_(profile),
175      service_(ProfileSyncServiceFactory::GetForProfile(profile)),
176      username_(username),
177      password_(password),
178      oauth2_refesh_token_number_(0),
179      profile_debug_name_(profile->GetDebugName()),
180      status_change_checker_(NULL) {
181  // Start listening for and emitting notifications of commits.
182  p2p_invalidation_forwarder_.reset(
183      new P2PInvalidationForwarder(service_, p2p_invalidation_service));
184}
185
186ProfileSyncServiceHarness::~ProfileSyncServiceHarness() { }
187
188void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
189                                               const std::string& password) {
190  username_ = username;
191  password_ = password;
192}
193
194bool ProfileSyncServiceHarness::SetupSync() {
195  bool result = SetupSync(syncer::ModelTypeSet::All());
196  if (result == false) {
197    std::string status = GetServiceStatus();
198    LOG(ERROR) << profile_debug_name_
199               << ": SetupSync failed. Syncer status:\n" << status;
200  } else {
201    DVLOG(1) << profile_debug_name_ << ": SetupSync successful.";
202  }
203  return result;
204}
205
206bool ProfileSyncServiceHarness::SetupSync(
207    syncer::ModelTypeSet synced_datatypes) {
208  // Initialize the sync client's profile sync service object.
209  if (service() == NULL) {
210    LOG(ERROR) << "SetupSync(): service() is null.";
211    return false;
212  }
213
214  // Tell the sync service that setup is in progress so we don't start syncing
215  // until we've finished configuration.
216  service()->SetSetupInProgress(true);
217
218  // Authenticate sync client using GAIA credentials.
219  service()->signin()->SetAuthenticatedUsername(username_);
220  GoogleServiceSigninSuccessDetails details(username_, password_);
221  content::NotificationService::current()->Notify(
222      chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
223      content::Source<Profile>(profile_),
224      content::Details<const GoogleServiceSigninSuccessDetails>(&details));
225
226#if defined(ENABLE_MANAGED_USERS)
227  std::string account_id = profile_->IsManaged() ?
228      managed_users::kManagedUserPseudoEmail : username_;
229#else
230  std::string account_id = username_;
231#endif
232  DCHECK(!account_id.empty());
233  ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
234      UpdateCredentials(account_id, GenerateFakeOAuth2RefreshTokenString());
235
236  // Wait for the OnBackendInitialized() callback.
237  if (!AwaitBackendInitialized()) {
238    LOG(ERROR) << "OnBackendInitialized() not seen after "
239               << kSyncOperationTimeoutMs / 1000
240               << " seconds.";
241    return false;
242  }
243
244  // Make sure that initial sync wasn't blocked by a missing passphrase.
245  if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
246    LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
247                  " until SetDecryptionPassphrase is called.";
248    return false;
249  }
250
251  // Make sure that initial sync wasn't blocked by rejected credentials.
252  if (HasAuthError()) {
253    LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
254    return false;
255  }
256
257  // Choose the datatypes to be synced. If all datatypes are to be synced,
258  // set sync_everything to true; otherwise, set it to false.
259  bool sync_everything =
260      synced_datatypes.Equals(syncer::ModelTypeSet::All());
261  service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
262
263  // Notify ProfileSyncService that we are done with configuration.
264  FinishSyncSetup();
265
266  // Set an implicit passphrase for encryption if an explicit one hasn't already
267  // been set. If an explicit passphrase has been set, immediately return false,
268  // since a decryption passphrase is required.
269  if (!service()->IsUsingSecondaryPassphrase()) {
270    service()->SetEncryptionPassphrase(password_, ProfileSyncService::IMPLICIT);
271  } else {
272    LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
273                  " until SetDecryptionPassphrase is called.";
274    return false;
275  }
276
277  // Wait for initial sync cycle to be completed.
278  DCHECK(service()->sync_initialized());
279  if (!AwaitSyncSetupCompletion()) {
280    LOG(ERROR) << "Initial sync cycle did not complete after "
281               << kSyncOperationTimeoutMs / 1000
282               << " seconds.";
283    return false;
284  }
285
286  // Make sure that initial sync wasn't blocked by a missing passphrase.
287  if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
288    LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
289                  " until SetDecryptionPassphrase is called.";
290    return false;
291  }
292
293  // Make sure that initial sync wasn't blocked by rejected credentials.
294  if (service()->GetAuthError().state() ==
295      GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
296    LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
297    return false;
298  }
299
300  return true;
301}
302
303void ProfileSyncServiceHarness::QuitMessageLoop() {
304  base::MessageLoop::current()->QuitWhenIdle();
305}
306
307void ProfileSyncServiceHarness::OnStateChanged() {
308  if (!status_change_checker_)
309    return;
310
311  DVLOG(1) << GetClientInfoString(status_change_checker_->GetDebugMessage());
312  if (status_change_checker_->IsExitConditionSatisfied())
313    QuitMessageLoop();
314}
315
316void ProfileSyncServiceHarness::OnSyncCycleCompleted() {
317  OnStateChanged();
318}
319
320bool ProfileSyncServiceHarness::AwaitPassphraseRequired() {
321  DVLOG(1) << GetClientInfoString("AwaitPassphraseRequired");
322  CallbackStatusChecker passphrase_required_checker(
323      service(),
324      base::Bind(&::IsPassphraseRequired, base::Unretained(this)),
325      "IsPassphraseRequired");
326  return AwaitStatusChange(&passphrase_required_checker);
327}
328
329bool ProfileSyncServiceHarness::AwaitPassphraseAccepted() {
330  CallbackStatusChecker passphrase_accepted_checker(
331      service(),
332      base::Bind(&::IsPassphraseAccepted, base::Unretained(this)),
333      "IsPassphraseAccepted");
334  bool return_value = AwaitStatusChange(&passphrase_accepted_checker);
335  if (return_value)
336    FinishSyncSetup();
337  return return_value;
338}
339
340bool ProfileSyncServiceHarness::AwaitBackendInitialized() {
341  DVLOG(1) << GetClientInfoString("AwaitBackendInitialized");
342  CallbackStatusChecker backend_initialized_checker(
343      service(),
344      base::Bind(&DoneWaitingForBackendInitialization,
345                 base::Unretained(this)),
346      "DoneWaitingForBackendInitialization");
347  AwaitStatusChange(&backend_initialized_checker);
348  return service()->sync_initialized();
349}
350
351// TODO(sync): As of today, we wait for a client to finish its commit activity
352// by checking if its progress markers are up to date. In future, once we have
353// an in-process C++ server, this function can be reimplemented without relying
354// on progress markers.
355bool ProfileSyncServiceHarness::AwaitCommitActivityCompletion() {
356  UpdatedProgressMarkerChecker progress_marker_checker(service());
357  return AwaitStatusChange(&progress_marker_checker);
358}
359
360bool ProfileSyncServiceHarness::AwaitSyncDisabled() {
361  DCHECK(service()->HasSyncSetupCompleted());
362  DCHECK(!IsSyncDisabled());
363  CallbackStatusChecker sync_disabled_checker(
364      service(),
365      base::Bind(&ProfileSyncServiceHarness::IsSyncDisabled,
366                 base::Unretained(this)),
367      "IsSyncDisabled");
368  return AwaitStatusChange(&sync_disabled_checker);
369}
370
371bool ProfileSyncServiceHarness::AwaitSyncSetupCompletion() {
372  CallbackStatusChecker sync_setup_complete_checker(
373      service(),
374      base::Bind(&DoneWaitingForSyncSetup, base::Unretained(this)),
375      "DoneWaitingForSyncSetup");
376  return AwaitStatusChange(&sync_setup_complete_checker);
377}
378
379bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
380    ProfileSyncServiceHarness* partner) {
381  std::vector<ProfileSyncServiceHarness*> harnesses;
382  harnesses.push_back(this);
383  harnesses.push_back(partner);
384  return AwaitQuiescence(harnesses);
385}
386
387bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
388    std::vector<ProfileSyncServiceHarness*>& partners) {
389  return AwaitQuiescence(partners);
390}
391
392// static
393bool ProfileSyncServiceHarness::AwaitQuiescence(
394    std::vector<ProfileSyncServiceHarness*>& clients) {
395  std::vector<ProfileSyncService*> services;
396  if (clients.empty()) {
397    return true;
398  }
399
400  for (std::vector<ProfileSyncServiceHarness*>::iterator it = clients.begin();
401       it != clients.end(); ++it) {
402    services.push_back((*it)->service());
403  }
404  QuiesceStatusChangeChecker checker(services);
405  return clients[0]->AwaitStatusChange(&checker);
406}
407
408bool ProfileSyncServiceHarness::AwaitStatusChange(
409    StatusChangeChecker* checker) {
410  DVLOG(1) << GetClientInfoString("AwaitStatusChange");
411
412  DCHECK(checker);
413  if (checker->IsExitConditionSatisfied()) {
414    DVLOG(1) << GetClientInfoString("AwaitStatusChange exiting early because "
415                                    "condition is already satisfied");
416    return true;
417  }
418
419  DCHECK(status_change_checker_ == NULL);
420  status_change_checker_ = checker;
421  status_change_checker_->InitObserver(this);
422
423  base::OneShotTimer<ProfileSyncServiceHarness> timer;
424  timer.Start(FROM_HERE,
425              base::TimeDelta::FromMilliseconds(kSyncOperationTimeoutMs),
426              base::Bind(&ProfileSyncServiceHarness::QuitMessageLoop,
427                         base::Unretained(this)));
428  {
429    base::MessageLoop* loop = base::MessageLoop::current();
430    base::MessageLoop::ScopedNestableTaskAllower allow(loop);
431    loop->Run();
432  }
433
434  status_change_checker_->UninitObserver(this);
435  status_change_checker_ = NULL;
436
437  if (timer.IsRunning()) {
438    DVLOG(1) << GetClientInfoString("AwaitStatusChange succeeded");
439    return true;
440  } else {
441    LOG(ERROR) << GetClientInfoString(base::StringPrintf(
442        "AwaitStatusChange called from %s timed out",
443        checker->GetDebugMessage().c_str()));
444    CHECK(false) << "Ending test because of timeout.";
445    return false;
446  }
447}
448
449std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
450  return base::StringPrintf("oauth2_refresh_token_%d",
451                            ++oauth2_refesh_token_number_);
452}
453
454ProfileSyncService::Status ProfileSyncServiceHarness::GetStatus() const {
455  DCHECK(service() != NULL) << "GetStatus(): service() is NULL.";
456  ProfileSyncService::Status result;
457  service()->QueryDetailedSyncStatus(&result);
458  return result;
459}
460
461bool ProfileSyncServiceHarness::IsSyncDisabled() const {
462  return !service()->setup_in_progress() &&
463         !service()->HasSyncSetupCompleted();
464}
465
466bool ProfileSyncServiceHarness::HasAuthError() const {
467  return service()->GetAuthError().state() ==
468             GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
469         service()->GetAuthError().state() ==
470             GoogleServiceAuthError::SERVICE_ERROR ||
471         service()->GetAuthError().state() ==
472             GoogleServiceAuthError::REQUEST_CANCELED;
473}
474
475void ProfileSyncServiceHarness::FinishSyncSetup() {
476  service()->SetSetupInProgress(false);
477  service()->SetSyncSetupCompleted();
478}
479
480bool ProfileSyncServiceHarness::AutoStartEnabled() {
481  return service()->auto_start_enabled();
482}
483
484SyncSessionSnapshot ProfileSyncServiceHarness::GetLastSessionSnapshot() const {
485  DCHECK(service() != NULL) << "Sync service has not yet been set up.";
486  if (service()->sync_initialized()) {
487    return service()->GetLastSessionSnapshot();
488  }
489  return SyncSessionSnapshot();
490}
491
492bool ProfileSyncServiceHarness::EnableSyncForDatatype(
493    syncer::ModelType datatype) {
494  DVLOG(1) << GetClientInfoString(
495      "EnableSyncForDatatype("
496      + std::string(syncer::ModelTypeToString(datatype)) + ")");
497
498  if (IsSyncDisabled())
499    return SetupSync(syncer::ModelTypeSet(datatype));
500
501  if (service() == NULL) {
502    LOG(ERROR) << "EnableSyncForDatatype(): service() is null.";
503    return false;
504  }
505
506  syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
507  if (synced_datatypes.Has(datatype)) {
508    DVLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype "
509             << syncer::ModelTypeToString(datatype)
510             << " on " << profile_debug_name_ << ".";
511    return true;
512  }
513
514  synced_datatypes.Put(syncer::ModelTypeFromInt(datatype));
515  service()->OnUserChoseDatatypes(false, synced_datatypes);
516  if (AwaitSyncSetupCompletion()) {
517    DVLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype "
518             << syncer::ModelTypeToString(datatype)
519             << " on " << profile_debug_name_ << ".";
520    return true;
521  }
522
523  DVLOG(0) << GetClientInfoString("EnableSyncForDatatype failed");
524  return false;
525}
526
527bool ProfileSyncServiceHarness::DisableSyncForDatatype(
528    syncer::ModelType datatype) {
529  DVLOG(1) << GetClientInfoString(
530      "DisableSyncForDatatype("
531      + std::string(syncer::ModelTypeToString(datatype)) + ")");
532
533  if (service() == NULL) {
534    LOG(ERROR) << "DisableSyncForDatatype(): service() is null.";
535    return false;
536  }
537
538  syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
539  if (!synced_datatypes.Has(datatype)) {
540    DVLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype "
541             << syncer::ModelTypeToString(datatype)
542             << " on " << profile_debug_name_ << ".";
543    return true;
544  }
545
546  synced_datatypes.RetainAll(syncer::UserSelectableTypes());
547  synced_datatypes.Remove(datatype);
548  service()->OnUserChoseDatatypes(false, synced_datatypes);
549  if (AwaitSyncSetupCompletion()) {
550    DVLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype "
551             << syncer::ModelTypeToString(datatype)
552             << " on " << profile_debug_name_ << ".";
553    return true;
554  }
555
556  DVLOG(0) << GetClientInfoString("DisableSyncForDatatype failed");
557  return false;
558}
559
560bool ProfileSyncServiceHarness::EnableSyncForAllDatatypes() {
561  DVLOG(1) << GetClientInfoString("EnableSyncForAllDatatypes");
562
563  if (IsSyncDisabled())
564    return SetupSync();
565
566  if (service() == NULL) {
567    LOG(ERROR) << "EnableSyncForAllDatatypes(): service() is null.";
568    return false;
569  }
570
571  service()->OnUserChoseDatatypes(true, syncer::ModelTypeSet::All());
572  if (AwaitSyncSetupCompletion()) {
573    DVLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes "
574             << "on " << profile_debug_name_ << ".";
575    return true;
576  }
577
578  DVLOG(0) << GetClientInfoString("EnableSyncForAllDatatypes failed");
579  return false;
580}
581
582bool ProfileSyncServiceHarness::DisableSyncForAllDatatypes() {
583  DVLOG(1) << GetClientInfoString("DisableSyncForAllDatatypes");
584
585  if (service() == NULL) {
586    LOG(ERROR) << "DisableSyncForAllDatatypes(): service() is null.";
587    return false;
588  }
589
590  service()->DisableForUser();
591
592  DVLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all "
593           << "datatypes on " << profile_debug_name_;
594  return true;
595}
596
597std::string ProfileSyncServiceHarness::GetSerializedProgressMarker(
598    syncer::ModelType model_type) const {
599  const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
600  const syncer::ProgressMarkerMap& markers_map =
601      snap.download_progress_markers();
602
603  syncer::ProgressMarkerMap::const_iterator it =
604      markers_map.find(model_type);
605  return (it != markers_map.end()) ? it->second : std::string();
606}
607
608// TODO(sync): Clean up this method in a separate CL. Remove all snapshot fields
609// and log shorter, more meaningful messages.
610std::string ProfileSyncServiceHarness::GetClientInfoString(
611    const std::string& message) const {
612  std::stringstream os;
613  os << profile_debug_name_ << ": " << message << ": ";
614  if (service()) {
615    const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
616    const ProfileSyncService::Status& status = GetStatus();
617    // Capture select info from the sync session snapshot and syncer status.
618    os << ", has_unsynced_items: "
619       << (service()->sync_initialized() ? service()->HasUnsyncedItems() : 0)
620       << ", did_commit: "
621       << (snap.model_neutral_state().num_successful_commits == 0 &&
622           snap.model_neutral_state().commit_result == syncer::SYNCER_OK)
623       << ", encryption conflicts: "
624       << snap.num_encryption_conflicts()
625       << ", hierarchy conflicts: "
626       << snap.num_hierarchy_conflicts()
627       << ", server conflicts: "
628       << snap.num_server_conflicts()
629       << ", num_updates_downloaded : "
630       << snap.model_neutral_state().num_updates_downloaded_total
631       << ", passphrase_required_reason: "
632       << syncer::PassphraseRequiredReasonToString(
633           service()->passphrase_required_reason())
634       << ", notifications_enabled: "
635       << status.notifications_enabled
636       << ", service_is_pushing_changes: "
637       << ServiceIsPushingChanges();
638  } else {
639    os << "Sync service not available";
640  }
641  return os.str();
642}
643
644bool ProfileSyncServiceHarness::EnableEncryption() {
645  if (IsEncryptionComplete())
646    return true;
647  service()->EnableEncryptEverything();
648
649  // In order to kick off the encryption we have to reconfigure. Just grab the
650  // currently synced types and use them.
651  const syncer::ModelTypeSet synced_datatypes =
652      service()->GetPreferredDataTypes();
653  bool sync_everything =
654      synced_datatypes.Equals(syncer::ModelTypeSet::All());
655  service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
656
657  // Wait some time to let the enryption finish.
658  return WaitForEncryption();
659}
660
661bool ProfileSyncServiceHarness::WaitForEncryption() {
662  if (IsEncryptionComplete()) {
663    // Encryption is already complete; do not wait.
664    return true;
665  }
666
667  CallbackStatusChecker encryption_complete_checker(
668      service(),
669      base::Bind(&ProfileSyncServiceHarness::IsEncryptionComplete,
670                 base::Unretained(this)),
671      "IsEncryptionComplete");
672  return AwaitStatusChange(&encryption_complete_checker);
673}
674
675bool ProfileSyncServiceHarness::IsEncryptionComplete() const {
676  bool is_encryption_complete = service()->EncryptEverythingEnabled() &&
677                                !service()->encryption_pending();
678  DVLOG(2) << "Encryption is "
679           << (is_encryption_complete ? "" : "not ")
680           << "complete; Encrypted types = "
681           << syncer::ModelTypeSetToString(service()->GetEncryptedDataTypes());
682  return is_encryption_complete;
683}
684
685bool ProfileSyncServiceHarness::IsTypeRunning(syncer::ModelType type) {
686  browser_sync::DataTypeController::StateMap state_map;
687  service()->GetDataTypeControllerStates(&state_map);
688  return (state_map.count(type) != 0 &&
689          state_map[type] == browser_sync::DataTypeController::RUNNING);
690}
691
692bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) {
693  return service()->GetPreferredDataTypes().Has(type);
694}
695
696std::string ProfileSyncServiceHarness::GetServiceStatus() {
697  scoped_ptr<base::DictionaryValue> value(
698      sync_ui_util::ConstructAboutInformation(service()));
699  std::string service_status;
700  base::JSONWriter::WriteWithOptions(value.get(),
701                                     base::JSONWriter::OPTIONS_PRETTY_PRINT,
702                                     &service_status);
703  return service_status;
704}
705