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. 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. 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 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 102 ProgressMarkerWatcher::~ProgressMarkerWatcher() { } 103 104 void ProgressMarkerWatcher::OnStateChanged() { 105 UpdateHasLatestProgressMarkers(); 106 quiesce_checker_->OnServiceStateChanged(service_); 107 } 108 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 138 bool ProgressMarkerWatcher::HasLatestProgressMarkers() { 139 return probably_has_latest_progress_markers_; 140 } 141 142 bool ProgressMarkerWatcher::IsSyncDisabled() { 143 return ::IsSyncDisabled(service_); 144 } 145 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 155 QuiesceStatusChangeChecker::~QuiesceStatusChangeChecker() {} 156 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 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 214 std::string QuiesceStatusChangeChecker::GetDebugMessage() const { 215 return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients", 216 services_.size()); 217 } 218 219 void QuiesceStatusChangeChecker::OnServiceStateChanged( 220 ProfileSyncService* service) { 221 CheckExitCondition(); 222 } 223