quiesce_status_change_checker.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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
14namespace {
15
16// Returns true if this service is disabled.
17bool IsSyncDisabled(ProfileSyncService* service) {
18  return !service->setup_in_progress() && !service->HasSyncSetupCompleted();
19}
20
21// Returns true if these services have matching progress markers.
22bool 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.
71class 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
91ProgressMarkerWatcher::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
102ProgressMarkerWatcher::~ProgressMarkerWatcher() { }
103
104void ProgressMarkerWatcher::OnStateChanged() {
105  UpdateHasLatestProgressMarkers();
106  quiesce_checker_->OnServiceStateChanged(service_);
107}
108
109void 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
138bool ProgressMarkerWatcher::HasLatestProgressMarkers() {
139  return probably_has_latest_progress_markers_;
140}
141
142bool ProgressMarkerWatcher::IsSyncDisabled() {
143  return ::IsSyncDisabled(service_);
144}
145
146QuiesceStatusChangeChecker::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
155QuiesceStatusChangeChecker::~QuiesceStatusChangeChecker() {}
156
157void 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
168bool 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
214std::string QuiesceStatusChangeChecker::GetDebugMessage() const {
215  return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients",
216                            services_.size());
217}
218
219void QuiesceStatusChangeChecker::OnServiceStateChanged(
220    ProfileSyncService* service) {
221  CheckExitCondition();
222}
223