blob: a351fa16bea58d068e5e2c3cffb9e1248e1b1d62 [file] [log] [blame]
// 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/bind.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/test/integration/updated_progress_marker_checker.h"
#include "components/sync/driver/profile_sync_service.h"
#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
#include "components/sync/test/fake_server/fake_server.h"
namespace {
// Compares two serialized progress markers for equivalence to determine client
// side progress. Some aspects of the progress markers like
// GarbageCollectionDirectives are irrelevant for this, as they can vary between
// requests -- for example a version_watermark could be based on request time.
bool AreProgressMarkersEquivalent(const std::string& serialized1,
const std::string& serialized2) {
sync_pb::DataTypeProgressMarker marker1;
sync_pb::DataTypeProgressMarker marker2;
CHECK(marker1.ParseFromString(serialized1));
CHECK(marker2.ParseFromString(serialized2));
marker1.clear_gc_directive();
marker2.clear_gc_directive();
DCHECK(marker1.data_type_id() == marker2.data_type_id());
if (syncer::GetModelTypeFromSpecificsFieldNumber(marker1.data_type_id()) ==
syncer::AUTOFILL_WALLET_DATA) {
return fake_server::AreWalletDataProgressMarkersEquivalent(marker1,
marker2);
}
return marker1.SerializeAsString() == marker2.SerializeAsString();
}
// Returns true if these services have matching progress markers.
bool ProgressMarkersMatch(const syncer::ProfileSyncService* service1,
const syncer::ProfileSyncService* service2) {
// GetActiveDataTypes() is always empty during configuration, so progress
// markers cannot be compared.
if (service1->GetTransportState() !=
syncer::SyncService::TransportState::ACTIVE ||
service2->GetTransportState() !=
syncer::SyncService::TransportState::ACTIVE) {
return false;
}
const syncer::ModelTypeSet common_types =
Intersection(service1->GetActiveDataTypes(),
service2->GetActiveDataTypes());
const syncer::SyncCycleSnapshot& snap1 =
service1->GetLastCycleSnapshotForDebugging();
const syncer::SyncCycleSnapshot& snap2 =
service2->GetLastCycleSnapshotForDebugging();
for (syncer::ModelType type : common_types) {
// Look up the progress markers. Fail if either one is missing.
auto pm_it1 = snap1.download_progress_markers().find(type);
if (pm_it1 == snap1.download_progress_markers().end()) {
return false;
}
auto pm_it2 = snap2.download_progress_markers().find(type);
if (pm_it2 == snap2.download_progress_markers().end()) {
return false;
}
// Fail if any of them don't match.
if (!AreProgressMarkersEquivalent(pm_it1->second, pm_it2->second)) {
return false;
}
}
return true;
}
} // namespace
// Variation of UpdateProgressMarkerChecker that intercepts calls to
// CheckExitCondition() and forwards them to a parent checker.
class QuiesceStatusChangeChecker::NestedUpdatedProgressMarkerChecker
: public UpdatedProgressMarkerChecker {
public:
NestedUpdatedProgressMarkerChecker(
syncer::ProfileSyncService* service,
const base::RepeatingClosure& check_exit_condition_cb)
: UpdatedProgressMarkerChecker(service),
check_exit_condition_cb_(check_exit_condition_cb) {}
~NestedUpdatedProgressMarkerChecker() override {}
protected:
void CheckExitCondition() override { check_exit_condition_cb_.Run(); }
private:
const base::RepeatingClosure check_exit_condition_cb_;
};
QuiesceStatusChangeChecker::QuiesceStatusChangeChecker(
std::vector<syncer::ProfileSyncService*> services)
: MultiClientStatusChangeChecker(services) {
DCHECK_LE(1U, services.size());
for (size_t i = 0; i < services.size(); ++i) {
checkers_.push_back(std::make_unique<NestedUpdatedProgressMarkerChecker>(
services[i],
base::BindRepeating(&QuiesceStatusChangeChecker::CheckExitCondition,
base::Unretained(this))));
}
}
QuiesceStatusChangeChecker::~QuiesceStatusChangeChecker() {}
bool QuiesceStatusChangeChecker::IsExitConditionSatisfied() {
// Check that all progress markers are up to date.
std::vector<syncer::ProfileSyncService*> enabled_services;
for (const auto& checker : checkers_) {
enabled_services.push_back(checker->service());
if (!checker->IsExitConditionSatisfied()) {
DVLOG(1) << "Not quiesced: Progress markers are old.";
return false;
}
}
for (size_t i = 1; i < enabled_services.size(); ++i) {
// Return false if there is a progress marker mismatch.
if (!ProgressMarkersMatch(enabled_services[i - 1], enabled_services[i])) {
DVLOG(1) << "Not quiesced: Progress marker mismatch.";
return false;
}
}
return true;
}
std::string QuiesceStatusChangeChecker::GetDebugMessage() const {
return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients",
checkers_.size());
}