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