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