1 // Copyright 2014 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.
5 #include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
7 #include "base/format_macros.h"
8 #include "base/scoped_observer.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/sync/profile_sync_service.h"
12 #include "sync/internal_api/public/sessions/sync_session_snapshot.h"
16 // Returns true if this service is disabled.
17 bool IsSyncDisabled(ProfileSyncService
* service
) {
18 return !service
->setup_in_progress() && !service
->HasSyncSetupCompleted();
21 // Returns true if these services have matching progress markers.
22 bool ProgressMarkersMatch(const ProfileSyncService
* service1
,
23 const ProfileSyncService
* service2
) {
24 const syncer::ModelTypeSet common_types
=
25 Intersection(service1
->GetActiveDataTypes(),
26 service2
->GetActiveDataTypes());
28 const syncer::sessions::SyncSessionSnapshot
& snap1
=
29 service1
->GetLastSessionSnapshot();
30 const syncer::sessions::SyncSessionSnapshot
& snap2
=
31 service2
->GetLastSessionSnapshot();
33 for (syncer::ModelTypeSet::Iterator type_it
= common_types
.First();
34 type_it
.Good(); type_it
.Inc()) {
35 // Look up the progress markers. Fail if either one is missing.
36 syncer::ProgressMarkerMap::const_iterator pm_it1
=
37 snap1
.download_progress_markers().find(type_it
.Get());
38 if (pm_it1
== snap1
.download_progress_markers().end()) {
42 syncer::ProgressMarkerMap::const_iterator pm_it2
=
43 snap2
.download_progress_markers().find(type_it
.Get());
44 if (pm_it2
== snap2
.download_progress_markers().end()) {
48 // Fail if any of them don't match.
49 if (pm_it1
->second
!= pm_it2
->second
) {
58 // A helper class to keep an eye on a particular ProfileSyncService's
59 // "HasLatestProgressMarkers()" state.
61 // This is a work-around for the HasLatestProgressMarkers check's inherent
62 // flakiness. It's not safe to check that condition whenever we want. The
63 // safest time to check it is when the ProfileSyncService emits an
64 // OnStateChanged() event. This class waits for those events and updates its
65 // cached HasLatestProgressMarkers state every time that event occurs.
67 // See the comments in UpdatedProgressMarkerChecker for more details.
69 // The long-term plan is to deprecate this hack by replacing all its usees with
70 // more reliable status checkers.
71 class ProgressMarkerWatcher
: public sync_driver::SyncServiceObserver
{
73 ProgressMarkerWatcher(
74 ProfileSyncService
* service
,
75 QuiesceStatusChangeChecker
* quiesce_checker
);
76 ~ProgressMarkerWatcher() override
;
77 void OnStateChanged() override
;
79 bool HasLatestProgressMarkers();
80 bool IsSyncDisabled();
83 void UpdateHasLatestProgressMarkers();
85 ProfileSyncService
* service_
;
86 QuiesceStatusChangeChecker
* quiesce_checker_
;
87 ScopedObserver
<ProfileSyncService
, ProgressMarkerWatcher
> scoped_observer_
;
88 bool probably_has_latest_progress_markers_
;
91 ProgressMarkerWatcher::ProgressMarkerWatcher(
92 ProfileSyncService
* service
,
93 QuiesceStatusChangeChecker
* quiesce_checker
)
95 quiesce_checker_(quiesce_checker
),
96 scoped_observer_(this),
97 probably_has_latest_progress_markers_(false) {
98 scoped_observer_
.Add(service
);
99 UpdateHasLatestProgressMarkers();
102 ProgressMarkerWatcher::~ProgressMarkerWatcher() { }
104 void ProgressMarkerWatcher::OnStateChanged() {
105 UpdateHasLatestProgressMarkers();
106 quiesce_checker_
->OnServiceStateChanged(service_
);
109 void ProgressMarkerWatcher::UpdateHasLatestProgressMarkers() {
110 if (IsSyncDisabled()) {
111 probably_has_latest_progress_markers_
= false;
115 // This is the same progress marker check as used by the
116 // UpdatedProgressMarkerChecker. It has the samed drawbacks and potential for
117 // flakiness. See the comment in
118 // UpdatedProgressMarkerChecker::IsExitConditionSatisfied() for more
121 // The QuiesceStatusChangeChecker attempts to work around the limitations of
122 // this progress marker checking method. It tries to update the progress
123 // marker status only in the OnStateChanged() callback, where the snapshot is
126 // It also checks the progress marker status when it is first initialized, and
127 // that's where it's most likely that we could return a false positive. We
128 // need to check these service at startup, since not every service is
129 // guaranteed to generate OnStateChanged() events while we're waiting for
131 const syncer::sessions::SyncSessionSnapshot
& snap
=
132 service_
->GetLastSessionSnapshot();
133 probably_has_latest_progress_markers_
=
134 snap
.model_neutral_state().num_successful_commits
== 0 &&
135 !service_
->HasUnsyncedItems();
138 bool ProgressMarkerWatcher::HasLatestProgressMarkers() {
139 return probably_has_latest_progress_markers_
;
142 bool ProgressMarkerWatcher::IsSyncDisabled() {
143 return ::IsSyncDisabled(service_
);
146 QuiesceStatusChangeChecker::QuiesceStatusChangeChecker(
147 std::vector
<ProfileSyncService
*> services
)
148 : services_(services
) {
149 DCHECK_LE(1U, services_
.size());
150 for (size_t i
= 0; i
< services_
.size(); ++i
) {
151 observers_
.push_back(new ProgressMarkerWatcher(services
[i
], this));
155 QuiesceStatusChangeChecker::~QuiesceStatusChangeChecker() {}
157 void QuiesceStatusChangeChecker::Wait() {
158 DVLOG(1) << "Await: " << GetDebugMessage();
160 if (IsExitConditionSatisfied()) {
161 DVLOG(1) << "Await -> Exit before waiting: " << GetDebugMessage();
168 bool QuiesceStatusChangeChecker::IsExitConditionSatisfied() {
169 // Check that all progress markers are up to date.
170 for (ScopedVector
<ProgressMarkerWatcher
>::const_iterator it
=
171 observers_
.begin(); it
!= observers_
.end(); ++it
) {
172 if ((*it
)->IsSyncDisabled()) {
173 continue; // Skip disabled services.
176 if (!(*it
)->HasLatestProgressMarkers()) {
177 DVLOG(1) << "Not quiesced: Progress markers are old.";
182 std::vector
<ProfileSyncService
*> enabled_services
;
183 for (std::vector
<ProfileSyncService
*>::const_iterator it
= services_
.begin();
184 it
!= services_
.end(); ++it
) {
185 if (!IsSyncDisabled(*it
)) {
186 enabled_services
.push_back(*it
);
190 // Return true if we have nothing to compare against.
191 if (enabled_services
.size() <= 1) {
195 std::vector
<ProfileSyncService
*>::const_iterator it1
=
196 enabled_services
.begin();
197 std::vector
<ProfileSyncService
*>::const_iterator it2
=
198 enabled_services
.begin();
201 while (it2
!= enabled_services
.end()) {
202 // Return false if there is a progress marker mismatch.
203 if (!ProgressMarkersMatch(*it1
, *it2
)) {
204 DVLOG(1) << "Not quiesced: Progress marker mismatch.";
214 std::string
QuiesceStatusChangeChecker::GetDebugMessage() const {
215 return base::StringPrintf("Waiting for quiescence of %" PRIuS
" clients",
219 void QuiesceStatusChangeChecker::OnServiceStateChanged(
220 ProfileSyncService
* service
) {
221 CheckExitCondition();