Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / test / integration / profile_sync_service_harness.cc
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
48 using syncer::sessions::SyncSessionSnapshot;
49 using 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.
55 static const int kSyncOperationTimeoutMs = 45000;
56
57 namespace {
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.
61 class 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.
86 bool 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.
104 bool 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.
123 bool 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.
130 bool IsPassphraseAccepted(const ProfileSyncServiceHarness* harness) {
131   DCHECK(harness);
132   return (!harness->service()->IsPassphraseRequired() &&
133           harness->service()->IsUsingSecondaryPassphrase());
134 }
135
136 }  // namespace
137
138 // static
139 ProfileSyncServiceHarness* 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
147 ProfileSyncServiceHarness* 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
158 ProfileSyncServiceHarness::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
174 ProfileSyncServiceHarness::~ProfileSyncServiceHarness() {
175   if (service()->HasObserver(this))
176     service()->RemoveObserver(this);
177 }
178
179 void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
180                                                const std::string& password) {
181   username_ = username;
182   password_ = password;
183 }
184
185 bool 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
197 bool 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
300 void ProfileSyncServiceHarness::QuitMessageLoop() {
301   base::MessageLoop::current()->QuitWhenIdle();
302 }
303
304 void 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
313 void 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
327 bool 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
336 bool 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
347 bool 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.
361 bool 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
372 bool 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
382 bool 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
390 bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
391     ProfileSyncServiceHarness* partner) {
392   DVLOG(1) << GetClientInfoString("AwaitMutualSyncCycleCompletion");
393   if (!AwaitCommitActivityCompletion())
394     return false;
395   return partner->WaitUntilProgressMarkersMatch(this);
396 }
397
398 bool 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
415 bool 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
429 bool 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
455 bool 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
498 std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
499   return base::StringPrintf("oauth2_refresh_token_%d",
500                             ++oauth2_refesh_token_number_);
501 }
502
503 ProfileSyncService::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
510 bool ProfileSyncServiceHarness::IsSyncDisabled() const {
511   return !service()->setup_in_progress() &&
512          !service()->HasSyncSetupCompleted();
513 }
514
515 bool 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.
526 bool ProfileSyncServiceHarness::HasLatestProgressMarkers() const {
527   const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
528   return snap.model_neutral_state().num_successful_commits == 0 &&
529          !service()->HasUnsyncedItems();
530 }
531
532 void ProfileSyncServiceHarness::FinishSyncSetup() {
533   service()->SetSetupInProgress(false);
534   service()->SetSyncSetupCompleted();
535 }
536
537 bool ProfileSyncServiceHarness::AutoStartEnabled() {
538   return service()->auto_start_enabled();
539 }
540
541 bool 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
578 SyncSessionSnapshot 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
586 bool 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
621 bool 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
654 bool 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
676 bool 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
691 std::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.
704 std::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
738 bool 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
755 bool 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
768 bool 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
778 bool 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
785 bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) {
786   return service()->GetPreferredDataTypes().Has(type);
787 }
788
789 size_t ProfileSyncServiceHarness::GetNumEntries() const {
790   return GetLastSessionSnapshot().num_entries();
791 }
792
793 size_t ProfileSyncServiceHarness::GetNumDatatypes() const {
794   browser_sync::DataTypeController::StateMap state_map;
795   service()->GetDataTypeControllerStates(&state_map);
796   return state_map.size();
797 }
798
799 std::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 }