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.
5 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
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 "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
19 #include "chrome/browser/sync/about_sync_util.h"
20 #include "chrome/browser/sync/profile_sync_service.h"
21 #include "chrome/browser/sync/profile_sync_service_factory.h"
22 #include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
23 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_finder.h"
26 #include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "components/invalidation/p2p_invalidation_service.h"
29 #include "components/signin/core/browser/profile_oauth2_token_service.h"
30 #include "components/signin/core/browser/signin_manager_base.h"
31 #include "components/sync_driver/data_type_controller.h"
32 #include "google_apis/gaia/gaia_constants.h"
33 #include "sync/internal_api/public/base/progress_marker_map.h"
34 #include "sync/internal_api/public/util/sync_string_conversions.h"
36 using syncer::sessions::SyncSessionSnapshot
;
40 bool HasAuthError(ProfileSyncService
* service
) {
41 return service
->GetAuthError().state() ==
42 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
||
43 service
->GetAuthError().state() ==
44 GoogleServiceAuthError::SERVICE_ERROR
||
45 service
->GetAuthError().state() ==
46 GoogleServiceAuthError::REQUEST_CANCELED
;
49 class BackendInitializeChecker
: public SingleClientStatusChangeChecker
{
51 explicit BackendInitializeChecker(ProfileSyncService
* service
)
52 : SingleClientStatusChangeChecker(service
) {}
54 bool IsExitConditionSatisfied() override
{
55 if (service()->backend_mode() != ProfileSyncService::SYNC
)
57 if (service()->backend_initialized())
59 // Backend initialization is blocked by an auth error.
60 if (HasAuthError(service()))
62 // Backend initialization is blocked by a failure to fetch Oauth2 tokens.
63 if (service()->IsRetryingAccessTokenFetchForTest())
65 // Still waiting on backend initialization.
69 std::string
GetDebugMessage() const override
{ return "Backend Initialize"; }
72 class SyncSetupChecker
: public SingleClientStatusChangeChecker
{
74 explicit SyncSetupChecker(ProfileSyncService
* service
)
75 : SingleClientStatusChangeChecker(service
) {}
77 bool IsExitConditionSatisfied() override
{
78 if (!service()->SyncActive())
80 if (service()->ConfigurationDone())
82 // Sync is blocked because a custom passphrase is required.
83 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION
)
85 // Sync is blocked by an auth error.
86 if (HasAuthError(service()))
88 // Still waiting on sync setup.
92 std::string
GetDebugMessage() const override
{ return "Sync Setup"; }
98 ProfileSyncServiceHarness
* ProfileSyncServiceHarness::Create(
100 const std::string
& username
,
101 const std::string
& password
,
102 SigninType signin_type
) {
103 return new ProfileSyncServiceHarness(profile
,
109 ProfileSyncServiceHarness::ProfileSyncServiceHarness(
111 const std::string
& username
,
112 const std::string
& password
,
113 SigninType signin_type
)
115 service_(ProfileSyncServiceFactory::GetForProfile(profile
)),
118 signin_type_(signin_type
),
119 oauth2_refesh_token_number_(0),
120 profile_debug_name_(profile
->GetDebugName()) {
123 ProfileSyncServiceHarness::~ProfileSyncServiceHarness() { }
125 void ProfileSyncServiceHarness::SetCredentials(const std::string
& username
,
126 const std::string
& password
) {
127 username_
= username
;
128 password_
= password
;
131 bool ProfileSyncServiceHarness::SetupSync() {
132 bool result
= SetupSync(syncer::ModelTypeSet::All());
133 if (result
== false) {
134 std::string status
= GetServiceStatus();
135 LOG(ERROR
) << profile_debug_name_
136 << ": SetupSync failed. Syncer status:\n" << status
;
138 DVLOG(1) << profile_debug_name_
<< ": SetupSync successful.";
143 bool ProfileSyncServiceHarness::SetupSync(
144 syncer::ModelTypeSet synced_datatypes
) {
145 DCHECK(!profile_
->IsSupervised())
146 << "SetupSync should not be used for supervised users.";
148 // Initialize the sync client's profile sync service object.
149 if (service() == NULL
) {
150 LOG(ERROR
) << "SetupSync(): service() is null.";
154 // Tell the sync service that setup is in progress so we don't start syncing
155 // until we've finished configuration.
156 service()->SetSetupInProgress(true);
158 DCHECK(!username_
.empty());
159 if (signin_type_
== SigninType::UI_SIGNIN
) {
161 FindBrowserWithProfile(profile_
, chrome::GetActiveDesktop());
163 if (!login_ui_test_utils::SignInWithUI(browser
, username_
, password_
)) {
164 LOG(ERROR
) << "Could not sign in to GAIA servers.";
167 } else if (signin_type_
== SigninType::FAKE_SIGNIN
) {
168 // Authenticate sync client using GAIA credentials.
169 service()->signin()->SetAuthenticatedUsername(username_
);
170 service()->GoogleSigninSucceeded(username_
, username_
, password_
);
171 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_
)->
172 UpdateCredentials(username_
, GenerateFakeOAuth2RefreshTokenString());
174 LOG(ERROR
) << "Unsupported profile signin type.";
177 if (!AwaitBackendInitialization()) {
181 // Choose the datatypes to be synced. If all datatypes are to be synced,
182 // set sync_everything to true; otherwise, set it to false.
183 bool sync_everything
=
184 synced_datatypes
.Equals(syncer::ModelTypeSet::All());
185 service()->OnUserChoseDatatypes(sync_everything
, synced_datatypes
);
187 // Notify ProfileSyncService that we are done with configuration.
190 // Set an implicit passphrase for encryption if an explicit one hasn't already
191 // been set. If an explicit passphrase has been set, immediately return false,
192 // since a decryption passphrase is required.
193 if (!service()->IsUsingSecondaryPassphrase()) {
194 service()->SetEncryptionPassphrase(password_
, ProfileSyncService::IMPLICIT
);
196 LOG(ERROR
) << "A passphrase is required for decryption. Sync cannot proceed"
197 " until SetDecryptionPassphrase is called.";
201 // Wait for initial sync cycle to be completed.
202 if (!AwaitSyncSetupCompletion()) {
203 LOG(ERROR
) << "Initial sync cycle timed out.";
210 bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
211 ProfileSyncServiceHarness
* partner
) {
212 std::vector
<ProfileSyncServiceHarness
*> harnesses
;
213 harnesses
.push_back(this);
214 harnesses
.push_back(partner
);
215 return AwaitQuiescence(harnesses
);
218 bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
219 std::vector
<ProfileSyncServiceHarness
*>& partners
) {
220 return AwaitQuiescence(partners
);
224 bool ProfileSyncServiceHarness::AwaitQuiescence(
225 std::vector
<ProfileSyncServiceHarness
*>& clients
) {
226 std::vector
<ProfileSyncService
*> services
;
227 if (clients
.empty()) {
231 for (std::vector
<ProfileSyncServiceHarness
*>::iterator it
= clients
.begin();
232 it
!= clients
.end(); ++it
) {
233 services
.push_back((*it
)->service());
235 QuiesceStatusChangeChecker
checker(services
);
237 return !checker
.TimedOut();
240 bool ProfileSyncServiceHarness::AwaitBackendInitialization() {
241 BackendInitializeChecker
checker(service());
244 if (checker
.TimedOut()) {
245 LOG(ERROR
) << "BackendInitializeChecker timed out.";
249 if (!service()->backend_initialized()) {
250 LOG(ERROR
) << "Service backend not initialized.";
254 // Make sure that initial sync wasn't blocked by a missing passphrase.
255 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION
) {
256 LOG(ERROR
) << "A passphrase is required for decryption. Sync cannot proceed"
257 " until SetDecryptionPassphrase is called.";
261 if (HasAuthError(service())) {
262 LOG(ERROR
) << "Credentials were rejected. Sync cannot proceed.";
269 bool ProfileSyncServiceHarness::AwaitSyncSetupCompletion() {
270 SyncSetupChecker
checker(service());
273 if (checker
.TimedOut()) {
274 LOG(ERROR
) << "SyncSetupChecker timed out.";
278 // Make sure that initial sync wasn't blocked by a missing passphrase.
279 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION
) {
280 LOG(ERROR
) << "A passphrase is required for decryption. Sync cannot proceed"
281 " until SetDecryptionPassphrase is called.";
285 if (HasAuthError(service())) {
286 LOG(ERROR
) << "Credentials were rejected. Sync cannot proceed.";
293 std::string
ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
294 return base::StringPrintf("oauth2_refresh_token_%d",
295 ++oauth2_refesh_token_number_
);
298 bool ProfileSyncServiceHarness::IsSyncDisabled() const {
299 return !service()->setup_in_progress() &&
300 !service()->HasSyncSetupCompleted();
303 void ProfileSyncServiceHarness::FinishSyncSetup() {
304 service()->SetSetupInProgress(false);
305 service()->SetSyncSetupCompleted();
308 SyncSessionSnapshot
ProfileSyncServiceHarness::GetLastSessionSnapshot() const {
309 DCHECK(service() != NULL
) << "Sync service has not yet been set up.";
310 if (service()->SyncActive()) {
311 return service()->GetLastSessionSnapshot();
313 return SyncSessionSnapshot();
316 bool ProfileSyncServiceHarness::EnableSyncForDatatype(
317 syncer::ModelType datatype
) {
318 DVLOG(1) << GetClientInfoString(
319 "EnableSyncForDatatype("
320 + std::string(syncer::ModelTypeToString(datatype
)) + ")");
322 if (IsSyncDisabled())
323 return SetupSync(syncer::ModelTypeSet(datatype
));
325 if (service() == NULL
) {
326 LOG(ERROR
) << "EnableSyncForDatatype(): service() is null.";
330 syncer::ModelTypeSet synced_datatypes
= service()->GetPreferredDataTypes();
331 if (synced_datatypes
.Has(datatype
)) {
332 DVLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype "
333 << syncer::ModelTypeToString(datatype
)
334 << " on " << profile_debug_name_
<< ".";
338 synced_datatypes
.Put(syncer::ModelTypeFromInt(datatype
));
339 service()->OnUserChoseDatatypes(false, synced_datatypes
);
340 if (AwaitSyncSetupCompletion()) {
341 DVLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype "
342 << syncer::ModelTypeToString(datatype
)
343 << " on " << profile_debug_name_
<< ".";
347 DVLOG(0) << GetClientInfoString("EnableSyncForDatatype failed");
351 bool ProfileSyncServiceHarness::DisableSyncForDatatype(
352 syncer::ModelType datatype
) {
353 DVLOG(1) << GetClientInfoString(
354 "DisableSyncForDatatype("
355 + std::string(syncer::ModelTypeToString(datatype
)) + ")");
357 if (service() == NULL
) {
358 LOG(ERROR
) << "DisableSyncForDatatype(): service() is null.";
362 syncer::ModelTypeSet synced_datatypes
= service()->GetPreferredDataTypes();
363 if (!synced_datatypes
.Has(datatype
)) {
364 DVLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype "
365 << syncer::ModelTypeToString(datatype
)
366 << " on " << profile_debug_name_
<< ".";
370 synced_datatypes
.RetainAll(syncer::UserSelectableTypes());
371 synced_datatypes
.Remove(datatype
);
372 service()->OnUserChoseDatatypes(false, synced_datatypes
);
373 if (AwaitSyncSetupCompletion()) {
374 DVLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype "
375 << syncer::ModelTypeToString(datatype
)
376 << " on " << profile_debug_name_
<< ".";
380 DVLOG(0) << GetClientInfoString("DisableSyncForDatatype failed");
384 bool ProfileSyncServiceHarness::EnableSyncForAllDatatypes() {
385 DVLOG(1) << GetClientInfoString("EnableSyncForAllDatatypes");
387 if (IsSyncDisabled())
390 if (service() == NULL
) {
391 LOG(ERROR
) << "EnableSyncForAllDatatypes(): service() is null.";
395 service()->OnUserChoseDatatypes(true, syncer::ModelTypeSet::All());
396 if (AwaitSyncSetupCompletion()) {
397 DVLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes "
398 << "on " << profile_debug_name_
<< ".";
402 DVLOG(0) << GetClientInfoString("EnableSyncForAllDatatypes failed");
406 bool ProfileSyncServiceHarness::DisableSyncForAllDatatypes() {
407 DVLOG(1) << GetClientInfoString("DisableSyncForAllDatatypes");
409 if (service() == NULL
) {
410 LOG(ERROR
) << "DisableSyncForAllDatatypes(): service() is null.";
414 service()->DisableForUser();
416 DVLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all "
417 << "datatypes on " << profile_debug_name_
;
421 // TODO(sync): Clean up this method in a separate CL. Remove all snapshot fields
422 // and log shorter, more meaningful messages.
423 std::string
ProfileSyncServiceHarness::GetClientInfoString(
424 const std::string
& message
) const {
425 std::stringstream os
;
426 os
<< profile_debug_name_
<< ": " << message
<< ": ";
428 const SyncSessionSnapshot
& snap
= GetLastSessionSnapshot();
429 ProfileSyncService::Status status
;
430 service()->QueryDetailedSyncStatus(&status
);
431 // Capture select info from the sync session snapshot and syncer status.
432 os
<< ", has_unsynced_items: "
433 << (service()->SyncActive() ? service()->HasUnsyncedItems() : 0)
435 << (snap
.model_neutral_state().num_successful_commits
== 0 &&
436 snap
.model_neutral_state().commit_result
== syncer::SYNCER_OK
)
437 << ", encryption conflicts: "
438 << snap
.num_encryption_conflicts()
439 << ", hierarchy conflicts: "
440 << snap
.num_hierarchy_conflicts()
441 << ", server conflicts: "
442 << snap
.num_server_conflicts()
443 << ", num_updates_downloaded : "
444 << snap
.model_neutral_state().num_updates_downloaded_total
445 << ", passphrase_required_reason: "
446 << syncer::PassphraseRequiredReasonToString(
447 service()->passphrase_required_reason())
448 << ", notifications_enabled: "
449 << status
.notifications_enabled
450 << ", service_is_active: "
451 << service()->SyncActive();
453 os
<< "Sync service not available";
458 bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type
) {
459 return service()->GetPreferredDataTypes().Has(type
);
462 std::string
ProfileSyncServiceHarness::GetServiceStatus() {
463 scoped_ptr
<base::DictionaryValue
> value(
464 sync_ui_util::ConstructAboutInformation(service()));
465 std::string service_status
;
466 base::JSONWriter::WriteWithOptions(value
.get(),
467 base::JSONWriter::OPTIONS_PRETTY_PRINT
,
469 return service_status
;