1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
6
7 #include <cstddef>
8 #include <iterator>
9 #include <ostream>
10 #include <sstream>
11 #include <vector>
12
13 #include "base/compiler_specific.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/timer/timer.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
20 #include "chrome/browser/sync/about_sync_util.h"
21 #include "chrome/browser/sync/profile_sync_service.h"
22 #include "chrome/browser/sync/profile_sync_service_factory.h"
23 #include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
24 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/pref_names.h"
27 #include "components/invalidation/p2p_invalidation_service.h"
28 #include "components/signin/core/browser/profile_oauth2_token_service.h"
29 #include "components/signin/core/browser/signin_manager_base.h"
30 #include "components/sync_driver/data_type_controller.h"
31 #include "google_apis/gaia/gaia_constants.h"
32 #include "sync/internal_api/public/base/progress_marker_map.h"
33 #include "sync/internal_api/public/util/sync_string_conversions.h"
34
35 #if defined(ENABLE_MANAGED_USERS)
36 #include "chrome/browser/supervised_user/supervised_user_constants.h"
37 #endif
38
39 using syncer::sessions::SyncSessionSnapshot;
40
41 namespace {
42
HasAuthError(ProfileSyncService * service)43 bool HasAuthError(ProfileSyncService* service) {
44 return service->GetAuthError().state() ==
45 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
46 service->GetAuthError().state() ==
47 GoogleServiceAuthError::SERVICE_ERROR ||
48 service->GetAuthError().state() ==
49 GoogleServiceAuthError::REQUEST_CANCELED;
50 }
51
52 class BackendInitializeChecker : public SingleClientStatusChangeChecker {
53 public:
BackendInitializeChecker(ProfileSyncService * service)54 explicit BackendInitializeChecker(ProfileSyncService* service)
55 : SingleClientStatusChangeChecker(service) {}
56
IsExitConditionSatisfied()57 virtual bool IsExitConditionSatisfied() OVERRIDE {
58 if (service()->sync_initialized())
59 return true;
60 // Backend initialization is blocked by an auth error.
61 if (HasAuthError(service()))
62 return true;
63 // Backend initialization is blocked by a failure to fetch Oauth2 tokens.
64 if (service()->IsRetryingAccessTokenFetchForTest())
65 return true;
66 // Still waiting on backend initialization.
67 return false;
68 }
69
GetDebugMessage() const70 virtual std::string GetDebugMessage() const OVERRIDE {
71 return "Backend Initialize";
72 }
73 };
74
75 class SyncSetupChecker : public SingleClientStatusChangeChecker {
76 public:
SyncSetupChecker(ProfileSyncService * service)77 explicit SyncSetupChecker(ProfileSyncService* service)
78 : SingleClientStatusChangeChecker(service) {}
79
IsExitConditionSatisfied()80 virtual bool IsExitConditionSatisfied() OVERRIDE {
81 // Sync setup is complete, and the client is ready to sync new changes.
82 if (service()->ShouldPushChanges())
83 return true;
84 // Sync is blocked because a custom passphrase is required.
85 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION)
86 return true;
87 // Sync is blocked by an auth error.
88 if (HasAuthError(service()))
89 return true;
90 // Still waiting on sync setup.
91 return false;
92 }
93
GetDebugMessage() const94 virtual std::string GetDebugMessage() const OVERRIDE {
95 return "Sync Setup";
96 }
97 };
98
AwaitSyncSetupCompletion(ProfileSyncService * service)99 bool AwaitSyncSetupCompletion(ProfileSyncService* service) {
100 SyncSetupChecker checker(service);
101 checker.Wait();
102 return !checker.TimedOut();
103 }
104
105 } // namespace
106
107 // static
Create(Profile * profile,const std::string & username,const std::string & password)108 ProfileSyncServiceHarness* ProfileSyncServiceHarness::Create(
109 Profile* profile,
110 const std::string& username,
111 const std::string& password) {
112 return new ProfileSyncServiceHarness(profile, username, password);
113 }
114
ProfileSyncServiceHarness(Profile * profile,const std::string & username,const std::string & password)115 ProfileSyncServiceHarness::ProfileSyncServiceHarness(
116 Profile* profile,
117 const std::string& username,
118 const std::string& password)
119 : profile_(profile),
120 service_(ProfileSyncServiceFactory::GetForProfile(profile)),
121 username_(username),
122 password_(password),
123 oauth2_refesh_token_number_(0),
124 profile_debug_name_(profile->GetDebugName()) {
125 }
126
~ProfileSyncServiceHarness()127 ProfileSyncServiceHarness::~ProfileSyncServiceHarness() { }
128
SetCredentials(const std::string & username,const std::string & password)129 void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
130 const std::string& password) {
131 username_ = username;
132 password_ = password;
133 }
134
SetupSync()135 bool ProfileSyncServiceHarness::SetupSync() {
136 bool result = SetupSync(syncer::ModelTypeSet::All());
137 if (result == false) {
138 std::string status = GetServiceStatus();
139 LOG(ERROR) << profile_debug_name_
140 << ": SetupSync failed. Syncer status:\n" << status;
141 } else {
142 DVLOG(1) << profile_debug_name_ << ": SetupSync successful.";
143 }
144 return result;
145 }
146
SetupSync(syncer::ModelTypeSet synced_datatypes)147 bool ProfileSyncServiceHarness::SetupSync(
148 syncer::ModelTypeSet synced_datatypes) {
149 // Initialize the sync client's profile sync service object.
150 if (service() == NULL) {
151 LOG(ERROR) << "SetupSync(): service() is null.";
152 return false;
153 }
154
155 // Tell the sync service that setup is in progress so we don't start syncing
156 // until we've finished configuration.
157 service()->SetSetupInProgress(true);
158
159 // Authenticate sync client using GAIA credentials.
160 service()->signin()->SetAuthenticatedUsername(username_);
161 service()->GoogleSigninSucceeded(username_, password_);
162
163 #if defined(ENABLE_MANAGED_USERS)
164 std::string account_id = profile_->IsSupervised() ?
165 supervised_users::kSupervisedUserPseudoEmail : username_;
166 #else
167 std::string account_id = username_;
168 #endif
169 DCHECK(!account_id.empty());
170 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
171 UpdateCredentials(account_id, GenerateFakeOAuth2RefreshTokenString());
172
173 // Wait for the OnBackendInitialized() callback.
174 BackendInitializeChecker checker(service());
175 checker.Wait();
176
177 if (checker.TimedOut()) {
178 LOG(ERROR) << "OnBackendInitialized() timed out.";
179 return false;
180 }
181
182 if (!service()->sync_initialized()) {
183 return false;
184 }
185
186 // Make sure that initial sync wasn't blocked by a missing passphrase.
187 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
188 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
189 " until SetDecryptionPassphrase is called.";
190 return false;
191 }
192
193 // Make sure that initial sync wasn't blocked by rejected credentials.
194 if (HasAuthError(service())) {
195 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
196 return false;
197 }
198
199 // Choose the datatypes to be synced. If all datatypes are to be synced,
200 // set sync_everything to true; otherwise, set it to false.
201 bool sync_everything =
202 synced_datatypes.Equals(syncer::ModelTypeSet::All());
203 service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
204
205 // Notify ProfileSyncService that we are done with configuration.
206 FinishSyncSetup();
207
208 // Set an implicit passphrase for encryption if an explicit one hasn't already
209 // been set. If an explicit passphrase has been set, immediately return false,
210 // since a decryption passphrase is required.
211 if (!service()->IsUsingSecondaryPassphrase()) {
212 service()->SetEncryptionPassphrase(password_, ProfileSyncService::IMPLICIT);
213 } else {
214 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
215 " until SetDecryptionPassphrase is called.";
216 return false;
217 }
218
219 // Wait for initial sync cycle to be completed.
220 DCHECK(service()->sync_initialized());
221 if (!AwaitSyncSetupCompletion(service())) {
222 LOG(ERROR) << "Initial sync cycle timed out.";
223 return false;
224 }
225
226 // Make sure that initial sync wasn't blocked by a missing passphrase.
227 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
228 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
229 " until SetDecryptionPassphrase is called.";
230 return false;
231 }
232
233 // Make sure that initial sync wasn't blocked by rejected credentials.
234 if (service()->GetAuthError().state() ==
235 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
236 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
237 return false;
238 }
239
240 return true;
241 }
242
AwaitMutualSyncCycleCompletion(ProfileSyncServiceHarness * partner)243 bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
244 ProfileSyncServiceHarness* partner) {
245 std::vector<ProfileSyncServiceHarness*> harnesses;
246 harnesses.push_back(this);
247 harnesses.push_back(partner);
248 return AwaitQuiescence(harnesses);
249 }
250
AwaitGroupSyncCycleCompletion(std::vector<ProfileSyncServiceHarness * > & partners)251 bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
252 std::vector<ProfileSyncServiceHarness*>& partners) {
253 return AwaitQuiescence(partners);
254 }
255
256 // static
AwaitQuiescence(std::vector<ProfileSyncServiceHarness * > & clients)257 bool ProfileSyncServiceHarness::AwaitQuiescence(
258 std::vector<ProfileSyncServiceHarness*>& clients) {
259 std::vector<ProfileSyncService*> services;
260 if (clients.empty()) {
261 return true;
262 }
263
264 for (std::vector<ProfileSyncServiceHarness*>::iterator it = clients.begin();
265 it != clients.end(); ++it) {
266 services.push_back((*it)->service());
267 }
268 QuiesceStatusChangeChecker checker(services);
269 checker.Wait();
270 return !checker.TimedOut();
271 }
272
GenerateFakeOAuth2RefreshTokenString()273 std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
274 return base::StringPrintf("oauth2_refresh_token_%d",
275 ++oauth2_refesh_token_number_);
276 }
277
IsSyncDisabled() const278 bool ProfileSyncServiceHarness::IsSyncDisabled() const {
279 return !service()->setup_in_progress() &&
280 !service()->HasSyncSetupCompleted();
281 }
282
FinishSyncSetup()283 void ProfileSyncServiceHarness::FinishSyncSetup() {
284 service()->SetSetupInProgress(false);
285 service()->SetSyncSetupCompleted();
286 }
287
GetLastSessionSnapshot() const288 SyncSessionSnapshot ProfileSyncServiceHarness::GetLastSessionSnapshot() const {
289 DCHECK(service() != NULL) << "Sync service has not yet been set up.";
290 if (service()->sync_initialized()) {
291 return service()->GetLastSessionSnapshot();
292 }
293 return SyncSessionSnapshot();
294 }
295
EnableSyncForDatatype(syncer::ModelType datatype)296 bool ProfileSyncServiceHarness::EnableSyncForDatatype(
297 syncer::ModelType datatype) {
298 DVLOG(1) << GetClientInfoString(
299 "EnableSyncForDatatype("
300 + std::string(syncer::ModelTypeToString(datatype)) + ")");
301
302 if (IsSyncDisabled())
303 return SetupSync(syncer::ModelTypeSet(datatype));
304
305 if (service() == NULL) {
306 LOG(ERROR) << "EnableSyncForDatatype(): service() is null.";
307 return false;
308 }
309
310 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
311 if (synced_datatypes.Has(datatype)) {
312 DVLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype "
313 << syncer::ModelTypeToString(datatype)
314 << " on " << profile_debug_name_ << ".";
315 return true;
316 }
317
318 synced_datatypes.Put(syncer::ModelTypeFromInt(datatype));
319 service()->OnUserChoseDatatypes(false, synced_datatypes);
320 if (AwaitSyncSetupCompletion(service())) {
321 DVLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype "
322 << syncer::ModelTypeToString(datatype)
323 << " on " << profile_debug_name_ << ".";
324 return true;
325 }
326
327 DVLOG(0) << GetClientInfoString("EnableSyncForDatatype failed");
328 return false;
329 }
330
DisableSyncForDatatype(syncer::ModelType datatype)331 bool ProfileSyncServiceHarness::DisableSyncForDatatype(
332 syncer::ModelType datatype) {
333 DVLOG(1) << GetClientInfoString(
334 "DisableSyncForDatatype("
335 + std::string(syncer::ModelTypeToString(datatype)) + ")");
336
337 if (service() == NULL) {
338 LOG(ERROR) << "DisableSyncForDatatype(): service() is null.";
339 return false;
340 }
341
342 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
343 if (!synced_datatypes.Has(datatype)) {
344 DVLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype "
345 << syncer::ModelTypeToString(datatype)
346 << " on " << profile_debug_name_ << ".";
347 return true;
348 }
349
350 synced_datatypes.RetainAll(syncer::UserSelectableTypes());
351 synced_datatypes.Remove(datatype);
352 service()->OnUserChoseDatatypes(false, synced_datatypes);
353 if (AwaitSyncSetupCompletion(service())) {
354 DVLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype "
355 << syncer::ModelTypeToString(datatype)
356 << " on " << profile_debug_name_ << ".";
357 return true;
358 }
359
360 DVLOG(0) << GetClientInfoString("DisableSyncForDatatype failed");
361 return false;
362 }
363
EnableSyncForAllDatatypes()364 bool ProfileSyncServiceHarness::EnableSyncForAllDatatypes() {
365 DVLOG(1) << GetClientInfoString("EnableSyncForAllDatatypes");
366
367 if (IsSyncDisabled())
368 return SetupSync();
369
370 if (service() == NULL) {
371 LOG(ERROR) << "EnableSyncForAllDatatypes(): service() is null.";
372 return false;
373 }
374
375 service()->OnUserChoseDatatypes(true, syncer::ModelTypeSet::All());
376 if (AwaitSyncSetupCompletion(service())) {
377 DVLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes "
378 << "on " << profile_debug_name_ << ".";
379 return true;
380 }
381
382 DVLOG(0) << GetClientInfoString("EnableSyncForAllDatatypes failed");
383 return false;
384 }
385
DisableSyncForAllDatatypes()386 bool ProfileSyncServiceHarness::DisableSyncForAllDatatypes() {
387 DVLOG(1) << GetClientInfoString("DisableSyncForAllDatatypes");
388
389 if (service() == NULL) {
390 LOG(ERROR) << "DisableSyncForAllDatatypes(): service() is null.";
391 return false;
392 }
393
394 service()->DisableForUser();
395
396 DVLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all "
397 << "datatypes on " << profile_debug_name_;
398 return true;
399 }
400
401 // TODO(sync): Clean up this method in a separate CL. Remove all snapshot fields
402 // and log shorter, more meaningful messages.
GetClientInfoString(const std::string & message) const403 std::string ProfileSyncServiceHarness::GetClientInfoString(
404 const std::string& message) const {
405 std::stringstream os;
406 os << profile_debug_name_ << ": " << message << ": ";
407 if (service()) {
408 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
409 ProfileSyncService::Status status;
410 service()->QueryDetailedSyncStatus(&status);
411 // Capture select info from the sync session snapshot and syncer status.
412 os << ", has_unsynced_items: "
413 << (service()->sync_initialized() ? service()->HasUnsyncedItems() : 0)
414 << ", did_commit: "
415 << (snap.model_neutral_state().num_successful_commits == 0 &&
416 snap.model_neutral_state().commit_result == syncer::SYNCER_OK)
417 << ", encryption conflicts: "
418 << snap.num_encryption_conflicts()
419 << ", hierarchy conflicts: "
420 << snap.num_hierarchy_conflicts()
421 << ", server conflicts: "
422 << snap.num_server_conflicts()
423 << ", num_updates_downloaded : "
424 << snap.model_neutral_state().num_updates_downloaded_total
425 << ", passphrase_required_reason: "
426 << syncer::PassphraseRequiredReasonToString(
427 service()->passphrase_required_reason())
428 << ", notifications_enabled: "
429 << status.notifications_enabled
430 << ", service_is_pushing_changes: "
431 << service()->ShouldPushChanges();
432 } else {
433 os << "Sync service not available";
434 }
435 return os.str();
436 }
437
IsTypePreferred(syncer::ModelType type)438 bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) {
439 return service()->GetPreferredDataTypes().Has(type);
440 }
441
GetServiceStatus()442 std::string ProfileSyncServiceHarness::GetServiceStatus() {
443 scoped_ptr<base::DictionaryValue> value(
444 sync_ui_util::ConstructAboutInformation(service()));
445 std::string service_status;
446 base::JSONWriter::WriteWithOptions(value.get(),
447 base::JSONWriter::OPTIONS_PRETTY_PRINT,
448 &service_status);
449 return service_status;
450 }
451