Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / test / integration / quiesce_status_change_checker.cc
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), timed_out_(false) {
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 base::TimeDelta QuiesceStatusChangeChecker::GetTimeoutDuration() {
158   return base::TimeDelta::FromSeconds(45);
159 }
160
161 void QuiesceStatusChangeChecker::Wait() {
162   DVLOG(1) << "Await: " << GetDebugMessage();
163
164   if (IsExitConditionSatisfied()) {
165     DVLOG(1) << "Await -> Exit before waiting: " << GetDebugMessage();
166     return;
167   }
168
169   base::OneShotTimer<QuiesceStatusChangeChecker> timer;
170   timer.Start(FROM_HERE,
171               GetTimeoutDuration(),
172               base::Bind(&QuiesceStatusChangeChecker::OnTimeout,
173                          base::Unretained(this)));
174
175   {
176     base::MessageLoop* loop = base::MessageLoop::current();
177     base::MessageLoop::ScopedNestableTaskAllower allow(loop);
178     loop->Run();
179   }
180 }
181
182 bool QuiesceStatusChangeChecker::IsExitConditionSatisfied() {
183   // Check that all progress markers are up to date.
184   for (ScopedVector<ProgressMarkerWatcher>::const_iterator it =
185        observers_.begin(); it != observers_.end(); ++it) {
186     if ((*it)->IsSyncDisabled()) {
187       continue;  // Skip disabled services.
188     }
189
190     if (!(*it)->HasLatestProgressMarkers()) {
191       VLOG(1) << "Not quiesced: Progress markers are old.";
192       return false;
193     }
194   }
195
196   std::vector<ProfileSyncService*> enabled_services;
197   for (std::vector<ProfileSyncService*>::const_iterator it = services_.begin();
198        it != services_.end(); ++it) {
199     if (!IsSyncDisabled(*it)) {
200       enabled_services.push_back(*it);
201     }
202   }
203
204   // Return true if we have nothing to compare against.
205   if (enabled_services.size() <= 1) {
206     return true;
207   }
208
209   std::vector<ProfileSyncService*>::const_iterator it1 =
210       enabled_services.begin();
211   std::vector<ProfileSyncService*>::const_iterator it2 =
212       enabled_services.begin();
213   it2++;
214
215   while (it2 != enabled_services.end()) {
216     // Return false if there is a progress marker mismatch.
217     if (!ProgressMarkersMatch(*it1, *it2)) {
218       VLOG(1) << "Not quiesced: Progress marker mismatch.";
219       return false;
220     }
221     it1++;
222     it2++;
223   }
224
225   return true;
226 }
227
228 std::string QuiesceStatusChangeChecker::GetDebugMessage() const {
229   return base::StringPrintf("Waiting for quiescence of %" PRIuS " clients",
230                             services_.size());
231 }
232
233 void QuiesceStatusChangeChecker::OnServiceStateChanged(
234     ProfileSyncService* service) {
235   if (IsExitConditionSatisfied()) {
236     DVLOG(1) << "Await -> Condition met: " << GetDebugMessage();
237     base::MessageLoop::current()->QuitWhenIdle();
238   }
239 }
240
241 void QuiesceStatusChangeChecker::OnTimeout() {
242   DVLOG(1) << "Await -> Timed out: " << GetDebugMessage();
243   timed_out_ = true;
244   base::MessageLoop::current()->QuitWhenIdle();
245 }
246
247 bool QuiesceStatusChangeChecker::TimedOut() const {
248   return timed_out_;
249 }