• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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