quiesce_status_change_checker.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
13#include "sync/internal_api/public/sessions/sync_session_snapshot.h"
14
15namespace {
16
17// Returns true if this service is disabled.
18bool IsSyncDisabled(ProfileSyncService* service) {
19  return !service->setup_in_progress() && !service->HasSyncSetupCompleted();
20}
21
22// Returns true if these services have matching progress markers.
23bool ProgressMarkersMatch(const ProfileSyncService* service1,
24                          const ProfileSyncService* service2) {
25  const syncer::ModelTypeSet common_types =
26      Intersection(service1->GetActiveDataTypes(),
27                   service2->GetActiveDataTypes());
28
29  const syncer::sessions::SyncSessionSnapshot& snap1 =
30      service1->GetLastSessionSnapshot();
31  const syncer::sessions::SyncSessionSnapshot& snap2 =
32      service2->GetLastSessionSnapshot();
33
34  for (syncer::ModelTypeSet::Iterator type_it = common_types.First();
35       type_it.Good(); type_it.Inc()) {
36    // Look up the progress markers.  Fail if either one is missing.
37    syncer::ProgressMarkerMap::const_iterator pm_it1 =
38        snap1.download_progress_markers().find(type_it.Get());
39    if (pm_it1 == snap1.download_progress_markers().end()) {
40      return false;
41    }
42
43    syncer::ProgressMarkerMap::const_iterator pm_it2 =
44        snap2.download_progress_markers().find(type_it.Get());
45    if (pm_it2 == snap2.download_progress_markers().end()) {
46      return false;
47    }
48
49    // Fail if any of them don't match.
50    if (pm_it1->second != pm_it2->second) {
51      return false;
52    }
53  }
54  return true;
55}
56
57}  // namespace
58
59// A helper class to keep an eye on a particular ProfileSyncService's
60// "HasLatestProgressMarkers()" state.
61//
62// This is a work-around for the HasLatestProgressMarkers check's inherent
63// flakiness.  It's not safe to check that condition whenever we want.  The
64// safest time to check it is when the ProfileSyncService emits an
65// OnStateChanged() event.  This class waits for those events and updates its
66// cached HasLatestProgressMarkers state every time that event occurs.
67//
68// See the comments in UpdatedProgressMarkerChecker for more details.
69//
70// The long-term plan is to deprecate this hack by replacing all its usees with
71// more reliable status checkers.
72class ProgressMarkerWatcher : public ProfileSyncServiceObserver {
73 public:
74  ProgressMarkerWatcher(
75      ProfileSyncService* service,
76      QuiesceStatusChangeChecker* quiesce_checker);
77  virtual ~ProgressMarkerWatcher();
78  virtual void OnStateChanged() OVERRIDE;
79
80  bool HasLatestProgressMarkers();
81  bool IsSyncDisabled();
82
83 private:
84  void UpdateHasLatestProgressMarkers();
85
86  ProfileSyncService* service_;
87  QuiesceStatusChangeChecker* quiesce_checker_;
88  ScopedObserver<ProfileSyncService, ProgressMarkerWatcher> scoped_observer_;
89  bool probably_has_latest_progress_markers_;
90};
91
92ProgressMarkerWatcher::ProgressMarkerWatcher(
93    ProfileSyncService* service,
94    QuiesceStatusChangeChecker* quiesce_checker)
95  : service_(service),
96    quiesce_checker_(quiesce_checker),
97    scoped_observer_(this),
98    probably_has_latest_progress_markers_(false) {
99  scoped_observer_.Add(service);
100  UpdateHasLatestProgressMarkers();
101}
102
103ProgressMarkerWatcher::~ProgressMarkerWatcher() { }
104
105void ProgressMarkerWatcher::OnStateChanged() {
106  UpdateHasLatestProgressMarkers();
107  quiesce_checker_->OnServiceStateChanged(service_);
108}
109
110void ProgressMarkerWatcher::UpdateHasLatestProgressMarkers() {
111  if (IsSyncDisabled()) {
112    probably_has_latest_progress_markers_ = false;
113    return;
114  }
115
116  // This is the same progress marker check as used by the
117  // UpdatedProgressMarkerChecker.  It has the samed drawbacks and potential for
118  // flakiness.  See the comment in
119  // UpdatedProgressMarkerChecker::IsExitConditionSatisfied() for more
120  // information.
121  //
122  // The QuiesceStatusChangeChecker attempts to work around the limitations of
123  // this progress marker checking method.  It tries to update the progress
124  // marker status only in the OnStateChanged() callback, where the snapshot is
125  // freshest.
126  //
127  // It also checks the progress marker status when it is first initialized, and
128  // that's where it's most likely that we could return a false positive.  We
129  // need to check these service at startup, since not every service is
130  // guaranteed to generate OnStateChanged() events while we're waiting for
131  // quiescence.
132  const syncer::sessions::SyncSessionSnapshot& snap =
133      service_->GetLastSessionSnapshot();
134  probably_has_latest_progress_markers_ =
135      snap.model_neutral_state().num_successful_commits == 0 &&
136      !service_->HasUnsyncedItems();
137}
138
139bool ProgressMarkerWatcher::HasLatestProgressMarkers() {
140  return probably_has_latest_progress_markers_;
141}
142
143bool ProgressMarkerWatcher::IsSyncDisabled() {
144  return ::IsSyncDisabled(service_);
145}
146
147QuiesceStatusChangeChecker::QuiesceStatusChangeChecker(
148    std::vector<ProfileSyncService*> services)
149  : services_(services), harness_(NULL) {
150  DCHECK_LE(1U, services_.size());
151  for (size_t i = 0; i < services_.size(); ++i) {
152    observers_.push_back(new ProgressMarkerWatcher(services[i], this));
153  }
154}
155
156QuiesceStatusChangeChecker::~QuiesceStatusChangeChecker() {}
157
158bool QuiesceStatusChangeChecker::IsExitConditionSatisfied() {
159  // Check that all progress markers are up to date.
160  for (ScopedVector<ProgressMarkerWatcher>::const_iterator it =
161       observers_.begin(); it != observers_.end(); ++it) {
162    if ((*it)->IsSyncDisabled()) {
163      continue;  // Skip disabled services.
164    }
165
166    if (!(*it)->HasLatestProgressMarkers()) {
167      VLOG(1) << "Not quiesced: Progress markers are old.";
168      return false;
169    }
170  }
171
172  std::vector<ProfileSyncService*> enabled_services;
173  for (std::vector<ProfileSyncService*>::const_iterator it = services_.begin();
174       it != services_.end(); ++it) {
175    if (!IsSyncDisabled(*it)) {
176      enabled_services.push_back(*it);
177    }
178  }
179
180  // Return true if we have nothing to compare against.
181  if (enabled_services.size() <= 1) {
182    return true;
183  }
184
185  std::vector<ProfileSyncService*>::const_iterator it1 =
186      enabled_services.begin();
187  std::vector<ProfileSyncService*>::const_iterator it2 =
188      enabled_services.begin();
189  it2++;
190
191  while (it2 != enabled_services.end()) {
192    // Return false if there is a progress marker mismatch.
193    if (!ProgressMarkersMatch(*it1, *it2)) {
194      VLOG(1) << "Not quiesced: Progress marker mismatch.";
195      return false;
196    }
197    it1++;
198    it2++;
199  }
200
201  return true;
202}
203
204std::string QuiesceStatusChangeChecker::GetDebugMessage() const {
205  return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients",
206                            services_.size());
207}
208
209
210void QuiesceStatusChangeChecker::InitObserver(
211    ProfileSyncServiceHarness* harness) {
212  harness_ = harness;
213}
214
215void QuiesceStatusChangeChecker::UninitObserver(
216    ProfileSyncServiceHarness* harness) {
217  harness_ = NULL;
218}
219
220void QuiesceStatusChangeChecker::OnServiceStateChanged(
221    ProfileSyncService* service) {
222  harness_->OnStateChanged();
223}
224