Home | History | Annotate | Download | only in integration
      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