1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
// 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 <stddef.h>
#include "base/format_macros.h"
#include "base/scoped_observer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
namespace {
// Returns true if this service is disabled.
bool IsSyncDisabled(browser_sync::ProfileSyncService* service) {
return !service->IsSetupInProgress() && !service->IsFirstSetupComplete();
}
// Returns true if these services have matching progress markers.
bool ProgressMarkersMatch(const browser_sync::ProfileSyncService* service1,
const browser_sync::ProfileSyncService* service2) {
const syncer::ModelTypeSet common_types =
Intersection(service1->GetActiveDataTypes(),
service2->GetActiveDataTypes());
const syncer::SyncCycleSnapshot& snap1 = service1->GetLastCycleSnapshot();
const syncer::SyncCycleSnapshot& snap2 = service2->GetLastCycleSnapshot();
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 syncer::SyncServiceObserver {
public:
ProgressMarkerWatcher(browser_sync::ProfileSyncService* service,
QuiesceStatusChangeChecker* quiesce_checker);
~ProgressMarkerWatcher() override;
void OnStateChanged() override;
bool HasLatestProgressMarkers();
bool IsSyncDisabled();
private:
void UpdateHasLatestProgressMarkers();
browser_sync::ProfileSyncService* service_;
QuiesceStatusChangeChecker* quiesce_checker_;
ScopedObserver<browser_sync::ProfileSyncService, ProgressMarkerWatcher>
scoped_observer_;
bool probably_has_latest_progress_markers_;
};
ProgressMarkerWatcher::ProgressMarkerWatcher(
browser_sync::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::SyncCycleSnapshot& snap = service_->GetLastCycleSnapshot();
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<browser_sync::ProfileSyncService*> services)
: services_(services) {
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()) {
DVLOG(1) << "Not quiesced: Progress markers are old.";
return false;
}
}
std::vector<browser_sync::ProfileSyncService*> enabled_services;
for (std::vector<browser_sync::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<browser_sync::ProfileSyncService*>::const_iterator it1 =
enabled_services.begin();
std::vector<browser_sync::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)) {
DVLOG(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::OnServiceStateChanged(
browser_sync::ProfileSyncService* service) {
CheckExitCondition();
}
|