| Index: chrome/browser/sync/test/integration/quiesce_status_change_checker.cc
|
| diff --git a/chrome/browser/sync/test/integration/quiesce_status_change_checker.cc b/chrome/browser/sync/test/integration/quiesce_status_change_checker.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..92b8654ec65e275c779aa2aa430c56de225dc30c
|
| --- /dev/null
|
| +++ b/chrome/browser/sync/test/integration/quiesce_status_change_checker.cc
|
| @@ -0,0 +1,223 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
|
| +
|
| +#include "base/format_macros.h"
|
| +#include "base/scoped_observer.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "chrome/browser/sync/profile_sync_service.h"
|
| +#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
|
| +#include "sync/internal_api/public/sessions/sync_session_snapshot.h"
|
| +
|
| +namespace {
|
| +
|
| +// Returns true if this service is disabled.
|
| +bool IsSyncDisabled(ProfileSyncService* service) {
|
| + return !service->setup_in_progress() && !service->HasSyncSetupCompleted();
|
| +}
|
| +
|
| +// Returns true if these services have matching progress markers.
|
| +bool ProgressMarkersMatch(const ProfileSyncService* service1,
|
| + const ProfileSyncService* service2) {
|
| + const syncer::ModelTypeSet common_types =
|
| + Intersection(service1->GetActiveDataTypes(),
|
| + service2->GetActiveDataTypes());
|
| +
|
| + const syncer::sessions::SyncSessionSnapshot& snap1 =
|
| + service1->GetLastSessionSnapshot();
|
| + const syncer::sessions::SyncSessionSnapshot& snap2 =
|
| + service2->GetLastSessionSnapshot();
|
| +
|
| + for (syncer::ModelTypeSet::Iterator type_it = common_types.First();
|
| + type_it.Good(); type_it.Inc()) {
|
| + // Look up the progress markers. Fail if either one is missing.
|
| + syncer::ProgressMarkerMap::const_iterator pm_it1 =
|
| + snap1.download_progress_markers().find(type_it.Get());
|
| + if (pm_it1 == snap1.download_progress_markers().end()) {
|
| + return false;
|
| + }
|
| +
|
| + syncer::ProgressMarkerMap::const_iterator pm_it2 =
|
| + snap2.download_progress_markers().find(type_it.Get());
|
| + if (pm_it2 == snap2.download_progress_markers().end()) {
|
| + return false;
|
| + }
|
| +
|
| + // Fail if any of them don't match.
|
| + if (pm_it1->second != pm_it2->second) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// A helper class to keep an eye on a particular ProfileSyncService's
|
| +// "HasLatestProgressMarkers()" state.
|
| +//
|
| +// This is a work-around for the HasLatestProgressMarkers check's inherent
|
| +// flakiness. It's not safe to check that condition whenever we want. The
|
| +// safest time to check it is when the ProfileSyncService emits an
|
| +// OnStateChanged() event. This class waits for those events and updates its
|
| +// cached HasLatestProgressMarkers state every time that event occurs.
|
| +//
|
| +// See the comments in UpdatedProgressMarkerChecker for more details.
|
| +//
|
| +// The long-term plan is to deprecate this hack by replacing all its usees with
|
| +// more reliable status checkers.
|
| +class ProgressMarkerWatcher : public ProfileSyncServiceObserver {
|
| + public:
|
| + ProgressMarkerWatcher(
|
| + ProfileSyncService* service,
|
| + QuiesceStatusChangeChecker* quiesce_checker);
|
| + virtual ~ProgressMarkerWatcher();
|
| + virtual void OnStateChanged() OVERRIDE;
|
| +
|
| + bool HasLatestProgressMarkers();
|
| + bool IsSyncDisabled();
|
| +
|
| + private:
|
| + void UpdateHasLatestProgressMarkers();
|
| +
|
| + ProfileSyncService* service_;
|
| + QuiesceStatusChangeChecker* quiesce_checker_;
|
| + ScopedObserver<ProfileSyncService, ProgressMarkerWatcher> scoped_observer_;
|
| + bool probably_has_latest_progress_markers_;
|
| +};
|
| +
|
| +ProgressMarkerWatcher::ProgressMarkerWatcher(
|
| + ProfileSyncService* service,
|
| + QuiesceStatusChangeChecker* quiesce_checker)
|
| + : service_(service),
|
| + quiesce_checker_(quiesce_checker),
|
| + scoped_observer_(this),
|
| + probably_has_latest_progress_markers_(false) {
|
| + scoped_observer_.Add(service);
|
| + UpdateHasLatestProgressMarkers();
|
| +}
|
| +
|
| +ProgressMarkerWatcher::~ProgressMarkerWatcher() { }
|
| +
|
| +void ProgressMarkerWatcher::OnStateChanged() {
|
| + UpdateHasLatestProgressMarkers();
|
| + quiesce_checker_->OnServiceStateChanged(service_);
|
| +}
|
| +
|
| +void ProgressMarkerWatcher::UpdateHasLatestProgressMarkers() {
|
| + if (IsSyncDisabled()) {
|
| + probably_has_latest_progress_markers_ = false;
|
| + return;
|
| + }
|
| +
|
| + // This is the same progress marker check as used by the
|
| + // UpdatedProgressMarkerChecker. It has the samed drawbacks and potential for
|
| + // flakiness. See the comment in
|
| + // UpdatedProgressMarkerChecker::IsExitConditionSatisfied() for more
|
| + // information.
|
| + //
|
| + // The QuiesceStatusChangeChecker attempts to work around the limitations of
|
| + // this progress marker checking method. It tries to update the progress
|
| + // marker status only in the OnStateChanged() callback, where the snapshot is
|
| + // freshest.
|
| + //
|
| + // It also checks the progress marker status when it is first initialized, and
|
| + // that's where it's most likely that we could return a false positive. We
|
| + // need to check these service at startup, since not every service is
|
| + // guaranteed to generate OnStateChanged() events while we're waiting for
|
| + // quiescence.
|
| + const syncer::sessions::SyncSessionSnapshot& snap =
|
| + service_->GetLastSessionSnapshot();
|
| + probably_has_latest_progress_markers_ =
|
| + snap.model_neutral_state().num_successful_commits == 0 &&
|
| + !service_->HasUnsyncedItems();
|
| +}
|
| +
|
| +bool ProgressMarkerWatcher::HasLatestProgressMarkers() {
|
| + return probably_has_latest_progress_markers_;
|
| +}
|
| +
|
| +bool ProgressMarkerWatcher::IsSyncDisabled() {
|
| + return ::IsSyncDisabled(service_);
|
| +}
|
| +
|
| +QuiesceStatusChangeChecker::QuiesceStatusChangeChecker(
|
| + std::vector<ProfileSyncService*> services)
|
| + : services_(services), harness_(NULL) {
|
| + DCHECK_LE(1U, services_.size());
|
| + for (size_t i = 0; i < services_.size(); ++i) {
|
| + observers_.push_back(new ProgressMarkerWatcher(services[i], this));
|
| + }
|
| +}
|
| +
|
| +QuiesceStatusChangeChecker::~QuiesceStatusChangeChecker() {}
|
| +
|
| +bool QuiesceStatusChangeChecker::IsExitConditionSatisfied() {
|
| + // Check that all progress markers are up to date.
|
| + for (ScopedVector<ProgressMarkerWatcher>::const_iterator it =
|
| + observers_.begin(); it != observers_.end(); ++it) {
|
| + if ((*it)->IsSyncDisabled()) {
|
| + continue; // Skip disabled services.
|
| + }
|
| +
|
| + if (!(*it)->HasLatestProgressMarkers()) {
|
| + VLOG(1) << "Not quiesced: Progress markers are old.";
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + std::vector<ProfileSyncService*> enabled_services;
|
| + for (std::vector<ProfileSyncService*>::const_iterator it = services_.begin();
|
| + it != services_.end(); ++it) {
|
| + if (!IsSyncDisabled(*it)) {
|
| + enabled_services.push_back(*it);
|
| + }
|
| + }
|
| +
|
| + // Return true if we have nothing to compare against.
|
| + if (enabled_services.size() <= 1) {
|
| + return true;
|
| + }
|
| +
|
| + std::vector<ProfileSyncService*>::const_iterator it1 =
|
| + enabled_services.begin();
|
| + std::vector<ProfileSyncService*>::const_iterator it2 =
|
| + enabled_services.begin();
|
| + it2++;
|
| +
|
| + while (it2 != enabled_services.end()) {
|
| + // Return false if there is a progress marker mismatch.
|
| + if (!ProgressMarkersMatch(*it1, *it2)) {
|
| + VLOG(1) << "Not quiesced: Progress marker mismatch.";
|
| + return false;
|
| + }
|
| + it1++;
|
| + it2++;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +std::string QuiesceStatusChangeChecker::GetDebugMessage() const {
|
| + return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients",
|
| + services_.size());
|
| +}
|
| +
|
| +
|
| +void QuiesceStatusChangeChecker::InitObserver(
|
| + ProfileSyncServiceHarness* harness) {
|
| + harness_ = harness;
|
| +}
|
| +
|
| +void QuiesceStatusChangeChecker::UninitObserver(
|
| + ProfileSyncServiceHarness* harness) {
|
| + harness_ = NULL;
|
| +}
|
| +
|
| +void QuiesceStatusChangeChecker::OnServiceStateChanged(
|
| + ProfileSyncService* service) {
|
| + harness_->OnStateChanged();
|
| +}
|
|
|