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/memory/ref_counted.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/prefs/pref_service.h"
24 #include "base/strings/stringprintf.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/glue/data_type_controller.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 "content/public/browser/notification_service.h"
38 #include "google_apis/gaia/gaia_constants.h"
39 #include "sync/internal_api/public/base/progress_marker_map.h"
40 #include "sync/internal_api/public/sessions/sync_session_snapshot.h"
41 #include "sync/internal_api/public/util/sync_string_conversions.h"
42
43 #if defined(ENABLE_MANAGED_USERS)
44 #include "chrome/browser/managed_mode/managed_user_constants.h"
45 #endif
46
47 using syncer::sessions::SyncSessionSnapshot;
48 using invalidation::P2PInvalidationService;
49
50 // The amount of time for which we wait for a sync operation to complete.
51 static const int kSyncOperationTimeoutMs = 45000;
52
53 // Simple class to implement a timeout using PostDelayedTask. If it is not
54 // aborted before picked up by a message queue, then it asserts. This class is
55 // not thread safe.
56 class StateChangeTimeoutEvent
57 : public base::RefCountedThreadSafe<StateChangeTimeoutEvent> {
58 public:
59 explicit StateChangeTimeoutEvent(ProfileSyncServiceHarness* caller);
60
61 // The entry point to the class from PostDelayedTask.
62 void Callback();
63
64 // Cancels the actions of the callback. Returns true if success, false
65 // if the callback has already timed out.
66 bool Abort();
67
68 private:
69 friend class base::RefCountedThreadSafe<StateChangeTimeoutEvent>;
70
71 ~StateChangeTimeoutEvent();
72
73 bool aborted_;
74 bool did_timeout_;
75
76 // Due to synchronization of the IO loop, the caller will always be alive
77 // if the class is not aborted.
78 ProfileSyncServiceHarness* caller_;
79
80 DISALLOW_COPY_AND_ASSIGN(StateChangeTimeoutEvent);
81 };
82
StateChangeTimeoutEvent(ProfileSyncServiceHarness * caller)83 StateChangeTimeoutEvent::StateChangeTimeoutEvent(
84 ProfileSyncServiceHarness* caller)
85 : aborted_(false), did_timeout_(false), caller_(caller) {
86 }
87
~StateChangeTimeoutEvent()88 StateChangeTimeoutEvent::~StateChangeTimeoutEvent() {
89 }
90
Callback()91 void StateChangeTimeoutEvent::Callback() {
92 if (!aborted_) {
93 DCHECK(caller_->status_change_checker_);
94 // TODO(rsimha): Simply return false on timeout instead of repeating the
95 // exit condition check.
96 if (!caller_->status_change_checker_->IsExitConditionSatisfied()) {
97 did_timeout_ = true;
98 DCHECK(!aborted_);
99 caller_->SignalStateComplete();
100 }
101 }
102 }
103
Abort()104 bool StateChangeTimeoutEvent::Abort() {
105 aborted_ = true;
106 caller_ = NULL;
107 return !did_timeout_;
108 }
109
110 namespace {
111
112 // Helper function which returns true if the sync backend has been initialized,
113 // or if backend initialization was blocked for some reason.
DoneWaitingForBackendInitialization(const ProfileSyncServiceHarness * harness)114 bool DoneWaitingForBackendInitialization(
115 const ProfileSyncServiceHarness* harness) {
116 DCHECK(harness);
117 // Backend is initialized.
118 if (harness->service()->sync_initialized())
119 return true;
120 // Backend initialization is blocked by an auth error.
121 if (harness->HasAuthError())
122 return true;
123 // Backend initialization is blocked by a failure to fetch Oauth2 tokens.
124 if (harness->service()->IsRetryingAccessTokenFetchForTest())
125 return true;
126 // Still waiting on backend initialization.
127 return false;
128 }
129
130 // Helper function which returns true if initial sync is complete, or if the
131 // initial sync is blocked for some reason.
DoneWaitingForInitialSync(const ProfileSyncServiceHarness * harness)132 bool DoneWaitingForInitialSync(const ProfileSyncServiceHarness* harness) {
133 DCHECK(harness);
134 // Initial sync is complete.
135 if (harness->IsFullySynced())
136 return true;
137 // Initial sync is blocked because custom passphrase is required.
138 if (harness->service()->passphrase_required_reason() ==
139 syncer::REASON_DECRYPTION) {
140 return true;
141 }
142 // Initial sync is blocked by an auth error.
143 if (harness->HasAuthError())
144 return true;
145 // Still waiting on initial sync.
146 return false;
147 }
148
149 // Helper function which returns true if the sync client is fully synced, or if
150 // sync is blocked for some reason.
DoneWaitingForFullSync(const ProfileSyncServiceHarness * harness)151 bool DoneWaitingForFullSync(const ProfileSyncServiceHarness* harness) {
152 DCHECK(harness);
153 // Sync is complete.
154 if (harness->IsFullySynced())
155 return true;
156 // Sync is blocked by an auth error.
157 if (harness->HasAuthError())
158 return true;
159 // Sync is blocked by a failure to fetch Oauth2 tokens.
160 if (harness->service()->IsRetryingAccessTokenFetchForTest())
161 return true;
162 // Still waiting on sync.
163 return false;
164 }
165
166 // Helper function which returns true if the sync client requires a custom
167 // passphrase to be entered for decryption.
IsPassphraseRequired(const ProfileSyncServiceHarness * harness)168 bool IsPassphraseRequired(const ProfileSyncServiceHarness* harness) {
169 DCHECK(harness);
170 return harness->service()->IsPassphraseRequired();
171 }
172
173 // Helper function which returns true if the custom passphrase entered was
174 // accepted.
IsPassphraseAccepted(const ProfileSyncServiceHarness * harness)175 bool IsPassphraseAccepted(const ProfileSyncServiceHarness* harness) {
176 DCHECK(harness);
177 return (!harness->service()->IsPassphraseRequired() &&
178 harness->service()->IsUsingSecondaryPassphrase());
179 }
180
181 // Helper function which returns true if the sync client no longer has a pending
182 // backend migration.
NoPendingBackendMigration(const ProfileSyncServiceHarness * harness)183 bool NoPendingBackendMigration(const ProfileSyncServiceHarness* harness) {
184 DCHECK(harness);
185 return !harness->HasPendingBackendMigration();
186 }
187
188 // Helper function which returns true if the client has detected an actionable
189 // error.
HasActionableError(const ProfileSyncServiceHarness * harness)190 bool HasActionableError(const ProfileSyncServiceHarness* harness) {
191 DCHECK(harness);
192 ProfileSyncService::Status status = harness->GetStatus();
193 return (status.sync_protocol_error.action != syncer::UNKNOWN_ACTION &&
194 harness->service()->HasUnrecoverableError() == true);
195 }
196
197 } // namespace
198
199 // static
Create(Profile * profile,const std::string & username,const std::string & password)200 ProfileSyncServiceHarness* ProfileSyncServiceHarness::Create(
201 Profile* profile,
202 const std::string& username,
203 const std::string& password) {
204 return new ProfileSyncServiceHarness(profile, username, password, NULL);
205 }
206
207 // static
CreateForIntegrationTest(Profile * profile,const std::string & username,const std::string & password,P2PInvalidationService * p2p_invalidation_service)208 ProfileSyncServiceHarness* ProfileSyncServiceHarness::CreateForIntegrationTest(
209 Profile* profile,
210 const std::string& username,
211 const std::string& password,
212 P2PInvalidationService* p2p_invalidation_service) {
213 return new ProfileSyncServiceHarness(profile,
214 username,
215 password,
216 p2p_invalidation_service);
217 }
218
ProfileSyncServiceHarness(Profile * profile,const std::string & username,const std::string & password,P2PInvalidationService * p2p_invalidation_service)219 ProfileSyncServiceHarness::ProfileSyncServiceHarness(
220 Profile* profile,
221 const std::string& username,
222 const std::string& password,
223 P2PInvalidationService* p2p_invalidation_service)
224 : profile_(profile),
225 service_(ProfileSyncServiceFactory::GetForProfile(profile)),
226 p2p_invalidation_service_(p2p_invalidation_service),
227 progress_marker_partner_(NULL),
228 username_(username),
229 password_(password),
230 oauth2_refesh_token_number_(0),
231 profile_debug_name_(profile->GetDebugName()),
232 status_change_checker_(NULL) {
233 }
234
~ProfileSyncServiceHarness()235 ProfileSyncServiceHarness::~ProfileSyncServiceHarness() {
236 if (service()->HasObserver(this))
237 service()->RemoveObserver(this);
238 }
239
SetCredentials(const std::string & username,const std::string & password)240 void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
241 const std::string& password) {
242 username_ = username;
243 password_ = password;
244 }
245
SetupSync()246 bool ProfileSyncServiceHarness::SetupSync() {
247 bool result = SetupSync(syncer::ModelTypeSet::All());
248 if (result == false) {
249 std::string status = GetServiceStatus();
250 LOG(ERROR) << profile_debug_name_
251 << ": SetupSync failed. Syncer status:\n" << status;
252 } else {
253 DVLOG(1) << profile_debug_name_ << ": SetupSync successful.";
254 }
255 return result;
256 }
257
SetupSync(syncer::ModelTypeSet synced_datatypes)258 bool ProfileSyncServiceHarness::SetupSync(
259 syncer::ModelTypeSet synced_datatypes) {
260 // Initialize the sync client's profile sync service object.
261 if (service() == NULL) {
262 LOG(ERROR) << "SetupSync(): service() is null.";
263 return false;
264 }
265
266 // Subscribe sync client to notifications from the profile sync service.
267 if (!service()->HasObserver(this))
268 service()->AddObserver(this);
269
270 // Tell the sync service that setup is in progress so we don't start syncing
271 // until we've finished configuration.
272 service()->SetSetupInProgress(true);
273
274 // Authenticate sync client using GAIA credentials.
275 service()->signin()->SetAuthenticatedUsername(username_);
276 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
277 username_);
278 GoogleServiceSigninSuccessDetails details(username_, password_);
279 content::NotificationService::current()->Notify(
280 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
281 content::Source<Profile>(profile_),
282 content::Details<const GoogleServiceSigninSuccessDetails>(&details));
283
284 #if defined(ENABLE_MANAGED_USERS)
285 std::string account_id = profile_->IsManaged() ?
286 managed_users::kManagedUserPseudoEmail : username_;
287 #else
288 std::string account_id = username_;
289 #endif
290 DCHECK(!account_id.empty());
291 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
292 UpdateCredentials(account_id, GenerateFakeOAuth2RefreshTokenString());
293
294 // Wait for the OnBackendInitialized() callback.
295 if (!AwaitBackendInitialized()) {
296 LOG(ERROR) << "OnBackendInitialized() not seen after "
297 << kSyncOperationTimeoutMs / 1000
298 << " seconds.";
299 return false;
300 }
301
302 // Make sure that initial sync wasn't blocked by a missing passphrase.
303 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
304 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
305 " until SetDecryptionPassphrase is called.";
306 return false;
307 }
308
309 // Make sure that initial sync wasn't blocked by rejected credentials.
310 if (HasAuthError()) {
311 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
312 return false;
313 }
314
315 // Choose the datatypes to be synced. If all datatypes are to be synced,
316 // set sync_everything to true; otherwise, set it to false.
317 bool sync_everything =
318 synced_datatypes.Equals(syncer::ModelTypeSet::All());
319 service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
320
321 // Notify ProfileSyncService that we are done with configuration.
322 FinishSyncSetup();
323
324 // Subscribe sync client to notifications from the backend migrator
325 // (possible only after choosing data types).
326 if (!TryListeningToMigrationEvents()) {
327 NOTREACHED();
328 return false;
329 }
330
331 // Set an implicit passphrase for encryption if an explicit one hasn't already
332 // been set. If an explicit passphrase has been set, immediately return false,
333 // since a decryption passphrase is required.
334 if (!service()->IsUsingSecondaryPassphrase()) {
335 service()->SetEncryptionPassphrase(password_, ProfileSyncService::IMPLICIT);
336 } else {
337 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
338 " until SetDecryptionPassphrase is called.";
339 return false;
340 }
341
342 // Wait for initial sync cycle to be completed.
343 DCHECK(service()->sync_initialized());
344 StatusChangeChecker initial_sync_checker(
345 base::Bind(&DoneWaitingForInitialSync, base::Unretained(this)),
346 "DoneWaitingForInitialSync");
347 if (!AwaitStatusChange(&initial_sync_checker, "SetupSync")) {
348 LOG(ERROR) << "Initial sync cycle did not complete after "
349 << kSyncOperationTimeoutMs / 1000
350 << " seconds.";
351 return false;
352 }
353
354 // Make sure that initial sync wasn't blocked by a missing passphrase.
355 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
356 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
357 " until SetDecryptionPassphrase is called.";
358 return false;
359 }
360
361 // Make sure that initial sync wasn't blocked by rejected credentials.
362 if (service()->GetAuthError().state() ==
363 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
364 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
365 return false;
366 }
367
368 return true;
369 }
370
TryListeningToMigrationEvents()371 bool ProfileSyncServiceHarness::TryListeningToMigrationEvents() {
372 browser_sync::BackendMigrator* migrator =
373 service()->GetBackendMigratorForTest();
374 if (migrator && !migrator->HasMigrationObserver(this)) {
375 migrator->AddMigrationObserver(this);
376 return true;
377 }
378 return false;
379 }
380
SignalStateComplete()381 void ProfileSyncServiceHarness::SignalStateComplete() {
382 base::MessageLoop::current()->QuitWhenIdle();
383 }
384
OnStateChanged()385 void ProfileSyncServiceHarness::OnStateChanged() {
386 if (!status_change_checker_)
387 return;
388
389 DVLOG(1) << GetClientInfoString(status_change_checker_->callback_name());
390 if (status_change_checker_->IsExitConditionSatisfied())
391 base::MessageLoop::current()->QuitWhenIdle();
392 }
393
OnSyncCycleCompleted()394 void ProfileSyncServiceHarness::OnSyncCycleCompleted() {
395 // Integration tests still use p2p notifications.
396 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
397 bool is_notifiable_commit =
398 (snap.model_neutral_state().num_successful_commits > 0);
399 if (is_notifiable_commit && p2p_invalidation_service_) {
400 syncer::ModelTypeSet model_types =
401 snap.model_neutral_state().commit_request_types;
402 syncer::ObjectIdSet ids = ModelTypeSetToObjectIdSet(model_types);
403 p2p_invalidation_service_->SendInvalidation(ids);
404 }
405 OnStateChanged();
406 }
407
OnMigrationStateChange()408 void ProfileSyncServiceHarness::OnMigrationStateChange() {
409 // Update migration state.
410 if (HasPendingBackendMigration()) {
411 // Merge current pending migration types into
412 // |pending_migration_types_|.
413 pending_migration_types_.PutAll(
414 service()->GetBackendMigratorForTest()->
415 GetPendingMigrationTypesForTest());
416 DVLOG(1) << profile_debug_name_ << ": new pending migration types "
417 << syncer::ModelTypeSetToString(pending_migration_types_);
418 } else {
419 // Merge just-finished pending migration types into
420 // |migration_types_|.
421 migrated_types_.PutAll(pending_migration_types_);
422 pending_migration_types_.Clear();
423 DVLOG(1) << profile_debug_name_ << ": new migrated types "
424 << syncer::ModelTypeSetToString(migrated_types_);
425 }
426 OnStateChanged();
427 }
428
AwaitPassphraseRequired()429 bool ProfileSyncServiceHarness::AwaitPassphraseRequired() {
430 DVLOG(1) << GetClientInfoString("AwaitPassphraseRequired");
431 if (IsSyncDisabled()) {
432 LOG(ERROR) << "Sync disabled for " << profile_debug_name_ << ".";
433 return false;
434 }
435
436 if (service()->IsPassphraseRequired()) {
437 // It's already true that a passphrase is required; don't wait.
438 return true;
439 }
440
441 StatusChangeChecker passphrase_required_checker(
442 base::Bind(&::IsPassphraseRequired, base::Unretained(this)),
443 "IsPassphraseRequired");
444 return AwaitStatusChange(&passphrase_required_checker,
445 "AwaitPassphraseRequired");
446 }
447
AwaitPassphraseAccepted()448 bool ProfileSyncServiceHarness::AwaitPassphraseAccepted() {
449 DVLOG(1) << GetClientInfoString("AwaitPassphraseAccepted");
450 if (IsSyncDisabled()) {
451 LOG(ERROR) << "Sync disabled for " << profile_debug_name_ << ".";
452 return false;
453 }
454
455 if (!service()->IsPassphraseRequired() &&
456 service()->IsUsingSecondaryPassphrase()) {
457 // Passphrase is already accepted; don't wait.
458 FinishSyncSetup();
459 return true;
460 }
461
462 StatusChangeChecker passphrase_accepted_checker(
463 base::Bind(&::IsPassphraseAccepted, base::Unretained(this)),
464 "IsPassphraseAccepted");
465 bool return_value = AwaitStatusChange(&passphrase_accepted_checker,
466 "AwaitPassphraseAccepted");
467 if (return_value)
468 FinishSyncSetup();
469 return return_value;
470 }
471
AwaitBackendInitialized()472 bool ProfileSyncServiceHarness::AwaitBackendInitialized() {
473 DVLOG(1) << GetClientInfoString("AwaitBackendInitialized");
474 if (service()->sync_initialized()) {
475 // The sync backend host has already been initialized; don't wait.
476 return true;
477 }
478
479 StatusChangeChecker backend_initialized_checker(
480 base::Bind(&DoneWaitingForBackendInitialization,
481 base::Unretained(this)),
482 "DoneWaitingForBackendInitialization");
483 AwaitStatusChange(&backend_initialized_checker, "AwaitBackendInitialized");
484 return service()->sync_initialized();
485 }
486
AwaitDataSyncCompletion()487 bool ProfileSyncServiceHarness::AwaitDataSyncCompletion() {
488 DVLOG(1) << GetClientInfoString("AwaitDataSyncCompletion");
489
490 DCHECK(service()->sync_initialized());
491 DCHECK(!IsSyncDisabled());
492
493 if (IsDataSynced()) {
494 // Client is already synced; don't wait.
495 return true;
496 }
497
498 StatusChangeChecker data_synced_checker(
499 base::Bind(&ProfileSyncServiceHarness::IsDataSynced,
500 base::Unretained(this)),
501 "IsDataSynced");
502 return AwaitStatusChange(&data_synced_checker, "AwaitDataSyncCompletion");
503 }
504
AwaitFullSyncCompletion()505 bool ProfileSyncServiceHarness::AwaitFullSyncCompletion() {
506 DVLOG(1) << GetClientInfoString("AwaitFullSyncCompletion");
507 if (IsSyncDisabled()) {
508 LOG(ERROR) << "Sync disabled for " << profile_debug_name_ << ".";
509 return false;
510 }
511
512 if (IsFullySynced()) {
513 // Client is already synced; don't wait.
514 return true;
515 }
516
517 DCHECK(service()->sync_initialized());
518 StatusChangeChecker fully_synced_checker(
519 base::Bind(&DoneWaitingForFullSync, base::Unretained(this)),
520 "DoneWaitingForFullSync");
521 AwaitStatusChange(&fully_synced_checker, "AwaitFullSyncCompletion");
522 return IsFullySynced();
523 }
524
AwaitSyncDisabled()525 bool ProfileSyncServiceHarness::AwaitSyncDisabled() {
526 DCHECK(service()->HasSyncSetupCompleted());
527 DCHECK(!IsSyncDisabled());
528 StatusChangeChecker sync_disabled_checker(
529 base::Bind(&ProfileSyncServiceHarness::IsSyncDisabled,
530 base::Unretained(this)),
531 "IsSyncDisabled");
532 return AwaitStatusChange(&sync_disabled_checker, "AwaitSyncDisabled");
533 }
534
535 // TODO(rsimha): Move this method from PSSHarness to the class that uses it.
AwaitExponentialBackoffVerification()536 bool ProfileSyncServiceHarness::AwaitExponentialBackoffVerification() {
537 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
538 retry_verifier_.reset(new RetryVerifier());
539 retry_verifier_->Initialize(snap);
540 StatusChangeChecker exponential_backoff_checker(
541 base::Bind(&ProfileSyncServiceHarness::IsExponentialBackoffDone,
542 base::Unretained(this)),
543 "IsExponentialBackoffDone");
544 AwaitStatusChange(&exponential_backoff_checker,
545 "AwaitExponentialBackoffVerification");
546 return retry_verifier_->Succeeded();
547 }
548
AwaitActionableError()549 bool ProfileSyncServiceHarness::AwaitActionableError() {
550 ProfileSyncService::Status status = GetStatus();
551 CHECK(status.sync_protocol_error.action == syncer::UNKNOWN_ACTION);
552 StatusChangeChecker actionable_error_checker(
553 base::Bind(&HasActionableError, base::Unretained(this)),
554 "HasActionableError");
555 return AwaitStatusChange(&actionable_error_checker, "AwaitActionableError");
556 }
557
AwaitMigration(syncer::ModelTypeSet expected_migrated_types)558 bool ProfileSyncServiceHarness::AwaitMigration(
559 syncer::ModelTypeSet expected_migrated_types) {
560 DVLOG(1) << GetClientInfoString("AwaitMigration");
561 DVLOG(1) << profile_debug_name_ << ": waiting until migration is done for "
562 << syncer::ModelTypeSetToString(expected_migrated_types);
563 while (true) {
564 bool migration_finished = migrated_types_.HasAll(expected_migrated_types);
565 DVLOG(1) << "Migrated types "
566 << syncer::ModelTypeSetToString(migrated_types_)
567 << (migration_finished ? " contains " : " does not contain ")
568 << syncer::ModelTypeSetToString(expected_migrated_types);
569 if (migration_finished) {
570 return true;
571 }
572
573 if (!HasPendingBackendMigration()) {
574 StatusChangeChecker pending_migration_checker(
575 base::Bind(&ProfileSyncServiceHarness::HasPendingBackendMigration,
576 base::Unretained(this)),
577 "HasPendingBackendMigration");
578 if (!AwaitStatusChange(&pending_migration_checker,
579 "AwaitMigration Start")) {
580 DVLOG(1) << profile_debug_name_ << ": Migration did not start";
581 return false;
582 }
583 }
584
585 StatusChangeChecker migration_finished_checker(
586 base::Bind(&::NoPendingBackendMigration, base::Unretained(this)),
587 "NoPendingBackendMigration");
588 if (!AwaitStatusChange(&migration_finished_checker,
589 "AwaitMigration Finish")) {
590 DVLOG(1) << profile_debug_name_ << ": Migration did not finish";
591 return false;
592 }
593
594 // We must use AwaitDataSyncCompletion rather than the more common
595 // AwaitFullSyncCompletion. As long as crbug.com/97780 is open, we will
596 // rely on self-notifications to ensure that progress markers are updated,
597 // which allows AwaitFullSyncCompletion to return. However, in some
598 // migration tests these notifications are completely disabled, so the
599 // progress markers do not get updated. This is why we must use the less
600 // strict condition, AwaitDataSyncCompletion.
601 if (!AwaitDataSyncCompletion())
602 return false;
603 }
604 }
605
AwaitMutualSyncCycleCompletion(ProfileSyncServiceHarness * partner)606 bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
607 ProfileSyncServiceHarness* partner) {
608 DVLOG(1) << GetClientInfoString("AwaitMutualSyncCycleCompletion");
609 if (!AwaitFullSyncCompletion())
610 return false;
611 return partner->WaitUntilProgressMarkersMatch(this);
612 }
613
AwaitGroupSyncCycleCompletion(std::vector<ProfileSyncServiceHarness * > & partners)614 bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
615 std::vector<ProfileSyncServiceHarness*>& partners) {
616 DVLOG(1) << GetClientInfoString("AwaitGroupSyncCycleCompletion");
617 if (!AwaitFullSyncCompletion())
618 return false;
619 bool return_value = true;
620 for (std::vector<ProfileSyncServiceHarness*>::iterator it =
621 partners.begin(); it != partners.end(); ++it) {
622 if ((this != *it) && (!(*it)->IsSyncDisabled())) {
623 return_value = return_value &&
624 (*it)->WaitUntilProgressMarkersMatch(this);
625 }
626 }
627 return return_value;
628 }
629
630 // static
AwaitQuiescence(std::vector<ProfileSyncServiceHarness * > & clients)631 bool ProfileSyncServiceHarness::AwaitQuiescence(
632 std::vector<ProfileSyncServiceHarness*>& clients) {
633 DVLOG(1) << "AwaitQuiescence.";
634 bool return_value = true;
635 for (std::vector<ProfileSyncServiceHarness*>::iterator it =
636 clients.begin(); it != clients.end(); ++it) {
637 if (!(*it)->IsSyncDisabled()) {
638 return_value = return_value &&
639 (*it)->AwaitGroupSyncCycleCompletion(clients);
640 }
641 }
642 return return_value;
643 }
644
WaitUntilProgressMarkersMatch(ProfileSyncServiceHarness * partner)645 bool ProfileSyncServiceHarness::WaitUntilProgressMarkersMatch(
646 ProfileSyncServiceHarness* partner) {
647 DVLOG(1) << GetClientInfoString("WaitUntilProgressMarkersMatch");
648 if (IsSyncDisabled()) {
649 LOG(ERROR) << "Sync disabled for " << profile_debug_name_ << ".";
650 return false;
651 }
652
653 // TODO(rsimha): Replace the mechanism of matching up progress markers with
654 // one that doesn't require every client to have the same progress markers.
655 DCHECK(!progress_marker_partner_);
656 progress_marker_partner_ = partner;
657 bool return_value = false;
658 if (MatchesPartnerClient()) {
659 // Progress markers already match; don't wait.
660 return_value = true;
661 } else {
662 partner->service()->AddObserver(this);
663 StatusChangeChecker matches_other_client_checker(
664 base::Bind(&ProfileSyncServiceHarness::MatchesPartnerClient,
665 base::Unretained(this)),
666 "MatchesPartnerClient");
667 return_value = AwaitStatusChange(&matches_other_client_checker,
668 "WaitUntilProgressMarkersMatch");
669 partner->service()->RemoveObserver(this);
670 }
671 progress_marker_partner_ = NULL;
672 return return_value;
673 }
674
AwaitStatusChange(StatusChangeChecker * checker,const std::string & source)675 bool ProfileSyncServiceHarness::AwaitStatusChange(
676 StatusChangeChecker* checker, const std::string& source) {
677 DVLOG(1) << GetClientInfoString("AwaitStatusChange");
678
679 if (IsSyncDisabled()) {
680 LOG(ERROR) << "Sync disabled for " << profile_debug_name_ << ".";
681 return false;
682 }
683
684 DCHECK(status_change_checker_ == NULL);
685 status_change_checker_ = checker;
686
687 scoped_refptr<StateChangeTimeoutEvent> timeout_signal(
688 new StateChangeTimeoutEvent(this));
689 {
690 base::MessageLoop* loop = base::MessageLoop::current();
691 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
692 loop->PostDelayedTask(
693 FROM_HERE,
694 base::Bind(&StateChangeTimeoutEvent::Callback,
695 timeout_signal.get()),
696 base::TimeDelta::FromMilliseconds(kSyncOperationTimeoutMs));
697 loop->Run();
698 }
699
700 status_change_checker_ = NULL;
701
702 if (timeout_signal->Abort()) {
703 DVLOG(1) << GetClientInfoString("AwaitStatusChange succeeded");
704 return true;
705 } else {
706 DVLOG(0) << GetClientInfoString(base::StringPrintf(
707 "AwaitStatusChange called from %s timed out", source.c_str()));
708 return false;
709 }
710 }
711
GenerateFakeOAuth2RefreshTokenString()712 std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
713 return base::StringPrintf("oauth2_refresh_token_%d",
714 ++oauth2_refesh_token_number_);
715 }
716
GetStatus() const717 ProfileSyncService::Status ProfileSyncServiceHarness::GetStatus() const {
718 DCHECK(service() != NULL) << "GetStatus(): service() is NULL.";
719 ProfileSyncService::Status result;
720 service()->QueryDetailedSyncStatus(&result);
721 return result;
722 }
723
IsExponentialBackoffDone() const724 bool ProfileSyncServiceHarness::IsExponentialBackoffDone() const {
725 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
726 retry_verifier_->VerifyRetryInterval(snap);
727 return (retry_verifier_->done());
728 }
729
IsSyncDisabled() const730 bool ProfileSyncServiceHarness::IsSyncDisabled() const {
731 return !service()->setup_in_progress() &&
732 !service()->HasSyncSetupCompleted();
733 }
734
HasAuthError() const735 bool ProfileSyncServiceHarness::HasAuthError() const {
736 return service()->GetAuthError().state() ==
737 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
738 service()->GetAuthError().state() ==
739 GoogleServiceAuthError::SERVICE_ERROR ||
740 service()->GetAuthError().state() ==
741 GoogleServiceAuthError::REQUEST_CANCELED;
742 }
743
744 // We use this function to share code between IsFullySynced and IsDataSynced.
IsDataSyncedImpl() const745 bool ProfileSyncServiceHarness::IsDataSyncedImpl() const {
746 return ServiceIsPushingChanges() &&
747 GetStatus().notifications_enabled &&
748 !service()->HasUnsyncedItems() &&
749 !HasPendingBackendMigration();
750 }
751
IsDataSynced() const752 bool ProfileSyncServiceHarness::IsDataSynced() const {
753 if (service() == NULL) {
754 DVLOG(1) << GetClientInfoString("IsDataSynced(): false");
755 return false;
756 }
757
758 bool is_data_synced = IsDataSyncedImpl();
759
760 DVLOG(1) << GetClientInfoString(
761 is_data_synced ? "IsDataSynced: true" : "IsDataSynced: false");
762 return is_data_synced;
763 }
764
IsFullySynced() const765 bool ProfileSyncServiceHarness::IsFullySynced() const {
766 if (service() == NULL) {
767 DVLOG(1) << GetClientInfoString("IsFullySynced: false");
768 return false;
769 }
770 // If we didn't try to commit anything in the previous cycle, there's a
771 // good chance that we're now fully up to date.
772 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
773 bool is_fully_synced =
774 snap.model_neutral_state().num_successful_commits == 0
775 && snap.model_neutral_state().commit_result == syncer::SYNCER_OK
776 && IsDataSyncedImpl();
777
778 DVLOG(1) << GetClientInfoString(
779 is_fully_synced ? "IsFullySynced: true" : "IsFullySynced: false");
780 return is_fully_synced;
781 }
782
FinishSyncSetup()783 void ProfileSyncServiceHarness::FinishSyncSetup() {
784 service()->SetSetupInProgress(false);
785 service()->SetSyncSetupCompleted();
786 }
787
HasPendingBackendMigration() const788 bool ProfileSyncServiceHarness::HasPendingBackendMigration() const {
789 browser_sync::BackendMigrator* migrator =
790 service()->GetBackendMigratorForTest();
791 return migrator && migrator->state() != browser_sync::BackendMigrator::IDLE;
792 }
793
AutoStartEnabled()794 bool ProfileSyncServiceHarness::AutoStartEnabled() {
795 return service()->auto_start_enabled();
796 }
797
MatchesPartnerClient() const798 bool ProfileSyncServiceHarness::MatchesPartnerClient() const {
799 // TODO(akalin): Shouldn't this belong with the intersection check?
800 // Otherwise, this function isn't symmetric.
801 DCHECK(progress_marker_partner_);
802 if (!IsFullySynced()) {
803 DVLOG(2) << profile_debug_name_ << ": not synced, assuming doesn't match";
804 return false;
805 }
806
807 // Only look for a match if we have at least one enabled datatype in
808 // common with the partner client.
809 const syncer::ModelTypeSet common_types =
810 Intersection(service()->GetActiveDataTypes(),
811 progress_marker_partner_->service()->GetActiveDataTypes());
812
813 DVLOG(2) << profile_debug_name_ << ", "
814 << progress_marker_partner_->profile_debug_name_
815 << ": common types are "
816 << syncer::ModelTypeSetToString(common_types);
817
818 if (!common_types.Empty() && !progress_marker_partner_->IsFullySynced()) {
819 DVLOG(2) << "non-empty common types and "
820 << progress_marker_partner_->profile_debug_name_
821 << " isn't synced";
822 return false;
823 }
824
825 for (syncer::ModelTypeSet::Iterator i = common_types.First();
826 i.Good(); i.Inc()) {
827 const std::string marker = GetSerializedProgressMarker(i.Get());
828 const std::string partner_marker =
829 progress_marker_partner_->GetSerializedProgressMarker(i.Get());
830 if (marker != partner_marker) {
831 if (VLOG_IS_ON(2)) {
832 std::string marker_base64, partner_marker_base64;
833 base::Base64Encode(marker, &marker_base64);
834 base::Base64Encode(partner_marker, &partner_marker_base64);
835 DVLOG(2) << syncer::ModelTypeToString(i.Get()) << ": "
836 << profile_debug_name_ << " progress marker = "
837 << marker_base64 << ", "
838 << progress_marker_partner_->profile_debug_name_
839 << " partner progress marker = "
840 << partner_marker_base64;
841 }
842 return false;
843 }
844 }
845 return true;
846 }
847
GetLastSessionSnapshot() const848 SyncSessionSnapshot ProfileSyncServiceHarness::GetLastSessionSnapshot() const {
849 DCHECK(service() != NULL) << "Sync service has not yet been set up.";
850 if (service()->sync_initialized()) {
851 return service()->GetLastSessionSnapshot();
852 }
853 return SyncSessionSnapshot();
854 }
855
EnableSyncForDatatype(syncer::ModelType datatype)856 bool ProfileSyncServiceHarness::EnableSyncForDatatype(
857 syncer::ModelType datatype) {
858 DVLOG(1) << GetClientInfoString(
859 "EnableSyncForDatatype("
860 + std::string(syncer::ModelTypeToString(datatype)) + ")");
861
862 if (IsSyncDisabled())
863 return SetupSync(syncer::ModelTypeSet(datatype));
864
865 if (service() == NULL) {
866 LOG(ERROR) << "EnableSyncForDatatype(): service() is null.";
867 return false;
868 }
869
870 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
871 if (synced_datatypes.Has(datatype)) {
872 DVLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype "
873 << syncer::ModelTypeToString(datatype)
874 << " on " << profile_debug_name_ << ".";
875 return true;
876 }
877
878 synced_datatypes.Put(syncer::ModelTypeFromInt(datatype));
879 service()->OnUserChoseDatatypes(false, synced_datatypes);
880 if (AwaitDataSyncCompletion()) {
881 DVLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype "
882 << syncer::ModelTypeToString(datatype)
883 << " on " << profile_debug_name_ << ".";
884 return true;
885 }
886
887 DVLOG(0) << GetClientInfoString("EnableSyncForDatatype failed");
888 return false;
889 }
890
DisableSyncForDatatype(syncer::ModelType datatype)891 bool ProfileSyncServiceHarness::DisableSyncForDatatype(
892 syncer::ModelType datatype) {
893 DVLOG(1) << GetClientInfoString(
894 "DisableSyncForDatatype("
895 + std::string(syncer::ModelTypeToString(datatype)) + ")");
896
897 if (service() == NULL) {
898 LOG(ERROR) << "DisableSyncForDatatype(): service() is null.";
899 return false;
900 }
901
902 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
903 if (!synced_datatypes.Has(datatype)) {
904 DVLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype "
905 << syncer::ModelTypeToString(datatype)
906 << " on " << profile_debug_name_ << ".";
907 return true;
908 }
909
910 synced_datatypes.RetainAll(syncer::UserSelectableTypes());
911 synced_datatypes.Remove(datatype);
912 service()->OnUserChoseDatatypes(false, synced_datatypes);
913 if (AwaitFullSyncCompletion()) {
914 DVLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype "
915 << syncer::ModelTypeToString(datatype)
916 << " on " << profile_debug_name_ << ".";
917 return true;
918 }
919
920 DVLOG(0) << GetClientInfoString("DisableSyncForDatatype failed");
921 return false;
922 }
923
EnableSyncForAllDatatypes()924 bool ProfileSyncServiceHarness::EnableSyncForAllDatatypes() {
925 DVLOG(1) << GetClientInfoString("EnableSyncForAllDatatypes");
926
927 if (IsSyncDisabled())
928 return SetupSync();
929
930 if (service() == NULL) {
931 LOG(ERROR) << "EnableSyncForAllDatatypes(): service() is null.";
932 return false;
933 }
934
935 service()->OnUserChoseDatatypes(true, syncer::ModelTypeSet::All());
936 if (AwaitFullSyncCompletion()) {
937 DVLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes "
938 << "on " << profile_debug_name_ << ".";
939 return true;
940 }
941
942 DVLOG(0) << GetClientInfoString("EnableSyncForAllDatatypes failed");
943 return false;
944 }
945
DisableSyncForAllDatatypes()946 bool ProfileSyncServiceHarness::DisableSyncForAllDatatypes() {
947 DVLOG(1) << GetClientInfoString("DisableSyncForAllDatatypes");
948
949 if (service() == NULL) {
950 LOG(ERROR) << "DisableSyncForAllDatatypes(): service() is null.";
951 return false;
952 }
953
954 service()->DisableForUser();
955
956 DVLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all "
957 << "datatypes on " << profile_debug_name_;
958 return true;
959 }
960
GetSerializedProgressMarker(syncer::ModelType model_type) const961 std::string ProfileSyncServiceHarness::GetSerializedProgressMarker(
962 syncer::ModelType model_type) const {
963 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
964 const syncer::ProgressMarkerMap& markers_map =
965 snap.download_progress_markers();
966
967 syncer::ProgressMarkerMap::const_iterator it =
968 markers_map.find(model_type);
969 return (it != markers_map.end()) ? it->second : std::string();
970 }
971
GetClientInfoString(const std::string & message) const972 std::string ProfileSyncServiceHarness::GetClientInfoString(
973 const std::string& message) const {
974 std::stringstream os;
975 os << profile_debug_name_ << ": " << message << ": ";
976 if (service()) {
977 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
978 const ProfileSyncService::Status& status = GetStatus();
979 // Capture select info from the sync session snapshot and syncer status.
980 // TODO(rsimha): Audit the list of fields below, and eventually eliminate
981 // the use of the sync session snapshot. See crbug.com/323380.
982 os << ", has_unsynced_items: "
983 << (service()->sync_initialized() ? service()->HasUnsyncedItems() : 0)
984 << ", did_commit: "
985 << (snap.model_neutral_state().num_successful_commits == 0 &&
986 snap.model_neutral_state().commit_result == syncer::SYNCER_OK)
987 << ", encryption conflicts: "
988 << snap.num_encryption_conflicts()
989 << ", hierarchy conflicts: "
990 << snap.num_hierarchy_conflicts()
991 << ", server conflicts: "
992 << snap.num_server_conflicts()
993 << ", num_updates_downloaded : "
994 << snap.model_neutral_state().num_updates_downloaded_total
995 << ", passphrase_required_reason: "
996 << syncer::PassphraseRequiredReasonToString(
997 service()->passphrase_required_reason())
998 << ", notifications_enabled: "
999 << status.notifications_enabled
1000 << ", service_is_pushing_changes: "
1001 << ServiceIsPushingChanges()
1002 << ", has_pending_backend_migration: "
1003 << HasPendingBackendMigration();
1004 } else {
1005 os << "Sync service not available";
1006 }
1007 return os.str();
1008 }
1009
EnableEncryption()1010 bool ProfileSyncServiceHarness::EnableEncryption() {
1011 if (IsEncryptionComplete())
1012 return true;
1013 service()->EnableEncryptEverything();
1014
1015 // In order to kick off the encryption we have to reconfigure. Just grab the
1016 // currently synced types and use them.
1017 const syncer::ModelTypeSet synced_datatypes =
1018 service()->GetPreferredDataTypes();
1019 bool sync_everything =
1020 synced_datatypes.Equals(syncer::ModelTypeSet::All());
1021 service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
1022
1023 // Wait some time to let the enryption finish.
1024 return WaitForEncryption();
1025 }
1026
WaitForEncryption()1027 bool ProfileSyncServiceHarness::WaitForEncryption() {
1028 if (IsEncryptionComplete()) {
1029 // Encryption is already complete; do not wait.
1030 return true;
1031 }
1032
1033 StatusChangeChecker encryption_complete_checker(
1034 base::Bind(&ProfileSyncServiceHarness::IsEncryptionComplete,
1035 base::Unretained(this)),
1036 "IsEncryptionComplete");
1037 return AwaitStatusChange(&encryption_complete_checker, "WaitForEncryption");
1038 }
1039
IsEncryptionComplete() const1040 bool ProfileSyncServiceHarness::IsEncryptionComplete() const {
1041 bool is_encryption_complete = service()->EncryptEverythingEnabled() &&
1042 !service()->encryption_pending();
1043 DVLOG(2) << "Encryption is "
1044 << (is_encryption_complete ? "" : "not ")
1045 << "complete; Encrypted types = "
1046 << syncer::ModelTypeSetToString(service()->GetEncryptedDataTypes());
1047 return is_encryption_complete;
1048 }
1049
IsTypeRunning(syncer::ModelType type)1050 bool ProfileSyncServiceHarness::IsTypeRunning(syncer::ModelType type) {
1051 browser_sync::DataTypeController::StateMap state_map;
1052 service()->GetDataTypeControllerStates(&state_map);
1053 return (state_map.count(type) != 0 &&
1054 state_map[type] == browser_sync::DataTypeController::RUNNING);
1055 }
1056
IsTypePreferred(syncer::ModelType type)1057 bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) {
1058 return service()->GetPreferredDataTypes().Has(type);
1059 }
1060
GetNumEntries() const1061 size_t ProfileSyncServiceHarness::GetNumEntries() const {
1062 return GetLastSessionSnapshot().num_entries();
1063 }
1064
GetNumDatatypes() const1065 size_t ProfileSyncServiceHarness::GetNumDatatypes() const {
1066 browser_sync::DataTypeController::StateMap state_map;
1067 service()->GetDataTypeControllerStates(&state_map);
1068 return state_map.size();
1069 }
1070
GetServiceStatus()1071 std::string ProfileSyncServiceHarness::GetServiceStatus() {
1072 scoped_ptr<DictionaryValue> value(
1073 sync_ui_util::ConstructAboutInformation(service()));
1074 std::string service_status;
1075 base::JSONWriter::WriteWithOptions(value.get(),
1076 base::JSONWriter::OPTIONS_PRETTY_PRINT,
1077 &service_status);
1078 return service_status;
1079 }
1080