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.
4
5 #include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
6
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"
13
14 namespace {
15
16 // Returns true if this service is disabled.
IsSyncDisabled(ProfileSyncService * service)17 bool IsSyncDisabled(ProfileSyncService* service) {
18 return !service->setup_in_progress() && !service->HasSyncSetupCompleted();
19 }
20
21 // Returns true if these services have matching progress markers.
ProgressMarkersMatch(const ProfileSyncService * service1,const ProfileSyncService * service2)22 bool ProgressMarkersMatch(const ProfileSyncService* service1,
23 const ProfileSyncService* service2) {
24 const syncer::ModelTypeSet common_types =
25 Intersection(service1->GetActiveDataTypes(),
26 service2->GetActiveDataTypes());
27
28 const syncer::sessions::SyncSessionSnapshot& snap1 =
29 service1->GetLastSessionSnapshot();
30 const syncer::sessions::SyncSessionSnapshot& snap2 =
31 service2->GetLastSessionSnapshot();
32
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()) {
39 return false;
40 }
41
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()) {
45 return false;
46 }
47
48 // Fail if any of them don't match.
49 if (pm_it1->second != pm_it2->second) {
50 return false;
51 }
52 }
53 return true;
54 }
55
56 } // namespace
57
58 // A helper class to keep an eye on a particular ProfileSyncService's
59 // "HasLatestProgressMarkers()" state.
60 //
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.
66 //
67 // See the comments in UpdatedProgressMarkerChecker for more details.
68 //
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 ProfileSyncServiceObserver {
72 public:
73 ProgressMarkerWatcher(
74 ProfileSyncService* service,
75 QuiesceStatusChangeChecker* quiesce_checker);
76 virtual ~ProgressMarkerWatcher();
77 virtual void OnStateChanged() OVERRIDE;
78
79 bool HasLatestProgressMarkers();
80 bool IsSyncDisabled();
81
82 private:
83 void UpdateHasLatestProgressMarkers();
84
85 ProfileSyncService* service_;
86 QuiesceStatusChangeChecker* quiesce_checker_;
87 ScopedObserver<ProfileSyncService, ProgressMarkerWatcher> scoped_observer_;
88 bool probably_has_latest_progress_markers_;
89 };
90
ProgressMarkerWatcher(ProfileSyncService * service,QuiesceStatusChangeChecker * quiesce_checker)91 ProgressMarkerWatcher::ProgressMarkerWatcher(
92 ProfileSyncService* service,
93 QuiesceStatusChangeChecker* quiesce_checker)
94 : service_(service),
95 quiesce_checker_(quiesce_checker),
96 scoped_observer_(this),
97 probably_has_latest_progress_markers_(false) {
98 scoped_observer_.Add(service);
99 UpdateHasLatestProgressMarkers();
100 }
101
~ProgressMarkerWatcher()102 ProgressMarkerWatcher::~ProgressMarkerWatcher() { }
103
OnStateChanged()104 void ProgressMarkerWatcher::OnStateChanged() {
105 UpdateHasLatestProgressMarkers();
106 quiesce_checker_->OnServiceStateChanged(service_);
107 }
108
UpdateHasLatestProgressMarkers()109 void ProgressMarkerWatcher::UpdateHasLatestProgressMarkers() {
110 if (IsSyncDisabled()) {
111 probably_has_latest_progress_markers_ = false;
112 return;
113 }
114
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
119 // information.
120 //
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
124 // freshest.
125 //
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
130 // quiescence.
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();
136 }
137
HasLatestProgressMarkers()138 bool ProgressMarkerWatcher::HasLatestProgressMarkers() {
139 return probably_has_latest_progress_markers_;
140 }
141
IsSyncDisabled()142 bool ProgressMarkerWatcher::IsSyncDisabled() {
143 return ::IsSyncDisabled(service_);
144 }
145
QuiesceStatusChangeChecker(std::vector<ProfileSyncService * > services)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));
152 }
153 }
154
~QuiesceStatusChangeChecker()155 QuiesceStatusChangeChecker::~QuiesceStatusChangeChecker() {}
156
Wait()157 void QuiesceStatusChangeChecker::Wait() {
158 DVLOG(1) << "Await: " << GetDebugMessage();
159
160 if (IsExitConditionSatisfied()) {
161 DVLOG(1) << "Await -> Exit before waiting: " << GetDebugMessage();
162 return;
163 }
164
165 StartBlockingWait();
166 }
167
IsExitConditionSatisfied()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.
174 }
175
176 if (!(*it)->HasLatestProgressMarkers()) {
177 VLOG(1) << "Not quiesced: Progress markers are old.";
178 return false;
179 }
180 }
181
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);
187 }
188 }
189
190 // Return true if we have nothing to compare against.
191 if (enabled_services.size() <= 1) {
192 return true;
193 }
194
195 std::vector<ProfileSyncService*>::const_iterator it1 =
196 enabled_services.begin();
197 std::vector<ProfileSyncService*>::const_iterator it2 =
198 enabled_services.begin();
199 it2++;
200
201 while (it2 != enabled_services.end()) {
202 // Return false if there is a progress marker mismatch.
203 if (!ProgressMarkersMatch(*it1, *it2)) {
204 VLOG(1) << "Not quiesced: Progress marker mismatch.";
205 return false;
206 }
207 it1++;
208 it2++;
209 }
210
211 return true;
212 }
213
GetDebugMessage() const214 std::string QuiesceStatusChangeChecker::GetDebugMessage() const {
215 return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients",
216 services_.size());
217 }
218
OnServiceStateChanged(ProfileSyncService * service)219 void QuiesceStatusChangeChecker::OnServiceStateChanged(
220 ProfileSyncService* service) {
221 CheckExitCondition();
222 }
223