1 // Copyright (c) 2012 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/typed_urls_helper.h"
6
7 #include "base/compiler_specific.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/common/cancelable_request.h"
12 #include "chrome/browser/history/history_backend.h"
13 #include "chrome/browser/history/history_db_task.h"
14 #include "chrome/browser/history/history_service.h"
15 #include "chrome/browser/history/history_service_factory.h"
16 #include "chrome/browser/history/history_types.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h"
19 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
20 #include "chrome/browser/sync/test/integration/sync_test.h"
21
22 using sync_datatype_helper::test;
23
24 namespace {
25
26 class FlushHistoryDBQueueTask : public history::HistoryDBTask {
27 public:
FlushHistoryDBQueueTask(base::WaitableEvent * event)28 explicit FlushHistoryDBQueueTask(base::WaitableEvent* event)
29 : wait_event_(event) {}
RunOnDBThread(history::HistoryBackend * backend,history::HistoryDatabase * db)30 virtual bool RunOnDBThread(history::HistoryBackend* backend,
31 history::HistoryDatabase* db) OVERRIDE {
32 wait_event_->Signal();
33 return true;
34 }
35
DoneRunOnMainThread()36 virtual void DoneRunOnMainThread() OVERRIDE {}
37
38 private:
~FlushHistoryDBQueueTask()39 virtual ~FlushHistoryDBQueueTask() {}
40
41 base::WaitableEvent* wait_event_;
42 };
43
44 class GetTypedUrlsTask : public history::HistoryDBTask {
45 public:
GetTypedUrlsTask(history::URLRows * rows,base::WaitableEvent * event)46 GetTypedUrlsTask(history::URLRows* rows, base::WaitableEvent* event)
47 : rows_(rows), wait_event_(event) {}
48
RunOnDBThread(history::HistoryBackend * backend,history::HistoryDatabase * db)49 virtual bool RunOnDBThread(history::HistoryBackend* backend,
50 history::HistoryDatabase* db) OVERRIDE {
51 // Fetch the typed URLs.
52 backend->GetAllTypedURLs(rows_);
53 wait_event_->Signal();
54 return true;
55 }
56
DoneRunOnMainThread()57 virtual void DoneRunOnMainThread() OVERRIDE {}
58
59 private:
~GetTypedUrlsTask()60 virtual ~GetTypedUrlsTask() {}
61
62 history::URLRows* rows_;
63 base::WaitableEvent* wait_event_;
64 };
65
66 class GetUrlTask : public history::HistoryDBTask {
67 public:
GetUrlTask(const GURL & url,history::URLRow * row,bool * found,base::WaitableEvent * event)68 GetUrlTask(const GURL& url,
69 history::URLRow* row,
70 bool* found,
71 base::WaitableEvent* event)
72 : url_(url), row_(row), wait_event_(event), found_(found) {}
73
RunOnDBThread(history::HistoryBackend * backend,history::HistoryDatabase * db)74 virtual bool RunOnDBThread(history::HistoryBackend* backend,
75 history::HistoryDatabase* db) OVERRIDE {
76 // Fetch the typed URLs.
77 *found_ = backend->GetURL(url_, row_);
78 wait_event_->Signal();
79 return true;
80 }
81
DoneRunOnMainThread()82 virtual void DoneRunOnMainThread() OVERRIDE {}
83
84 private:
~GetUrlTask()85 virtual ~GetUrlTask() {}
86
87 GURL url_;
88 history::URLRow* row_;
89 base::WaitableEvent* wait_event_;
90 bool* found_;
91 };
92
93 class GetVisitsTask : public history::HistoryDBTask {
94 public:
GetVisitsTask(history::URLID id,history::VisitVector * visits,base::WaitableEvent * event)95 GetVisitsTask(history::URLID id,
96 history::VisitVector* visits,
97 base::WaitableEvent* event)
98 : id_(id), visits_(visits), wait_event_(event) {}
99
RunOnDBThread(history::HistoryBackend * backend,history::HistoryDatabase * db)100 virtual bool RunOnDBThread(history::HistoryBackend* backend,
101 history::HistoryDatabase* db) OVERRIDE {
102 // Fetch the visits.
103 backend->GetVisitsForURL(id_, visits_);
104 wait_event_->Signal();
105 return true;
106 }
107
DoneRunOnMainThread()108 virtual void DoneRunOnMainThread() OVERRIDE {}
109
110 private:
~GetVisitsTask()111 virtual ~GetVisitsTask() {}
112
113 history::URLID id_;
114 history::VisitVector* visits_;
115 base::WaitableEvent* wait_event_;
116 };
117
118 class RemoveVisitsTask : public history::HistoryDBTask {
119 public:
RemoveVisitsTask(const history::VisitVector & visits,base::WaitableEvent * event)120 RemoveVisitsTask(const history::VisitVector& visits,
121 base::WaitableEvent* event)
122 : visits_(visits), wait_event_(event) {}
123
RunOnDBThread(history::HistoryBackend * backend,history::HistoryDatabase * db)124 virtual bool RunOnDBThread(history::HistoryBackend* backend,
125 history::HistoryDatabase* db) OVERRIDE {
126 // Fetch the visits.
127 backend->RemoveVisits(visits_);
128 wait_event_->Signal();
129 return true;
130 }
131
DoneRunOnMainThread()132 virtual void DoneRunOnMainThread() OVERRIDE {}
133
134 private:
~RemoveVisitsTask()135 virtual ~RemoveVisitsTask() {}
136
137 const history::VisitVector& visits_;
138 base::WaitableEvent* wait_event_;
139 };
140
141 // Waits for the history DB thread to finish executing its current set of
142 // tasks.
WaitForHistoryDBThread(int index)143 void WaitForHistoryDBThread(int index) {
144 CancelableRequestConsumer cancelable_consumer;
145 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
146 test()->GetProfile(index));
147 base::WaitableEvent wait_event(true, false);
148 service->ScheduleDBTask(new FlushHistoryDBQueueTask(&wait_event),
149 &cancelable_consumer);
150 wait_event.Wait();
151 }
152
153 // Creates a URLRow in the specified HistoryService with the passed transition
154 // type.
AddToHistory(HistoryService * service,const GURL & url,content::PageTransition transition,history::VisitSource source,const base::Time & timestamp)155 void AddToHistory(HistoryService* service,
156 const GURL& url,
157 content::PageTransition transition,
158 history::VisitSource source,
159 const base::Time& timestamp) {
160 service->AddPage(url,
161 timestamp,
162 NULL, // scope
163 1234, // page_id
164 GURL(), // referrer
165 history::RedirectList(),
166 transition,
167 source,
168 false);
169 service->SetPageTitle(url, base::ASCIIToUTF16(url.spec() + " - title"));
170 }
171
GetTypedUrlsFromHistoryService(HistoryService * service)172 history::URLRows GetTypedUrlsFromHistoryService(HistoryService* service) {
173 CancelableRequestConsumer cancelable_consumer;
174 history::URLRows rows;
175 base::WaitableEvent wait_event(true, false);
176 service->ScheduleDBTask(new GetTypedUrlsTask(&rows, &wait_event),
177 &cancelable_consumer);
178 wait_event.Wait();
179 return rows;
180 }
181
GetUrlFromHistoryService(HistoryService * service,const GURL & url,history::URLRow * row)182 bool GetUrlFromHistoryService(HistoryService* service,
183 const GURL& url, history::URLRow* row) {
184 CancelableRequestConsumer cancelable_consumer;
185 base::WaitableEvent wait_event(true, false);
186 bool found;
187 service->ScheduleDBTask(new GetUrlTask(url, row, &found, &wait_event),
188 &cancelable_consumer);
189 wait_event.Wait();
190 return found;
191 }
192
GetVisitsFromHistoryService(HistoryService * service,history::URLID id)193 history::VisitVector GetVisitsFromHistoryService(HistoryService* service,
194 history::URLID id) {
195 CancelableRequestConsumer cancelable_consumer;
196 base::WaitableEvent wait_event(true, false);
197 history::VisitVector visits;
198 service->ScheduleDBTask(new GetVisitsTask(id, &visits, &wait_event),
199 &cancelable_consumer);
200 wait_event.Wait();
201 return visits;
202 }
203
RemoveVisitsFromHistoryService(HistoryService * service,const history::VisitVector & visits)204 void RemoveVisitsFromHistoryService(HistoryService* service,
205 const history::VisitVector& visits) {
206 CancelableRequestConsumer cancelable_consumer;
207 base::WaitableEvent wait_event(true, false);
208 service->ScheduleDBTask(new RemoveVisitsTask(visits, &wait_event),
209 &cancelable_consumer);
210 wait_event.Wait();
211 }
212
213 static base::Time* timestamp = NULL;
214
215 } // namespace
216
217 namespace typed_urls_helper {
218
GetTypedUrlsFromClient(int index)219 history::URLRows GetTypedUrlsFromClient(int index) {
220 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
221 test()->GetProfile(index));
222 return GetTypedUrlsFromHistoryService(service);
223 }
224
GetUrlFromClient(int index,const GURL & url,history::URLRow * row)225 bool GetUrlFromClient(int index, const GURL& url, history::URLRow* row) {
226 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
227 test()->GetProfile(index));
228 return GetUrlFromHistoryService(service, url, row);
229 }
230
GetVisitsFromClient(int index,history::URLID id)231 history::VisitVector GetVisitsFromClient(int index, history::URLID id) {
232 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
233 test()->GetProfile(index));
234 return GetVisitsFromHistoryService(service, id);
235 }
236
RemoveVisitsFromClient(int index,const history::VisitVector & visits)237 void RemoveVisitsFromClient(int index, const history::VisitVector& visits) {
238 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
239 test()->GetProfile(index));
240 RemoveVisitsFromHistoryService(service, visits);
241 }
242
GetTimestamp()243 base::Time GetTimestamp() {
244 // The history subsystem doesn't like identical timestamps for page visits,
245 // and it will massage the visit timestamps if we try to use identical
246 // values, which can lead to spurious errors. So make sure all timestamps
247 // are unique.
248 if (!::timestamp)
249 ::timestamp = new base::Time(base::Time::Now());
250 base::Time original = *::timestamp;
251 *::timestamp += base::TimeDelta::FromMilliseconds(1);
252 return original;
253 }
254
AddUrlToHistory(int index,const GURL & url)255 void AddUrlToHistory(int index, const GURL& url) {
256 AddUrlToHistoryWithTransition(index, url, content::PAGE_TRANSITION_TYPED,
257 history::SOURCE_BROWSED);
258 }
AddUrlToHistoryWithTransition(int index,const GURL & url,content::PageTransition transition,history::VisitSource source)259 void AddUrlToHistoryWithTransition(int index,
260 const GURL& url,
261 content::PageTransition transition,
262 history::VisitSource source) {
263 base::Time timestamp = GetTimestamp();
264 AddUrlToHistoryWithTimestamp(index, url, transition, source, timestamp);
265 }
AddUrlToHistoryWithTimestamp(int index,const GURL & url,content::PageTransition transition,history::VisitSource source,const base::Time & timestamp)266 void AddUrlToHistoryWithTimestamp(int index,
267 const GURL& url,
268 content::PageTransition transition,
269 history::VisitSource source,
270 const base::Time& timestamp) {
271 AddToHistory(HistoryServiceFactory::GetForProfileWithoutCreating(
272 test()->GetProfile(index)),
273 url,
274 transition,
275 source,
276 timestamp);
277 if (test()->use_verifier())
278 AddToHistory(
279 HistoryServiceFactory::GetForProfile(test()->verifier(),
280 Profile::IMPLICIT_ACCESS),
281 url,
282 transition,
283 source,
284 timestamp);
285
286 // Wait until the AddPage() request has completed so we know the change has
287 // filtered down to the sync observers (don't need to wait for the
288 // verifier profile since it doesn't sync).
289 WaitForHistoryDBThread(index);
290 }
291
DeleteUrlFromHistory(int index,const GURL & url)292 void DeleteUrlFromHistory(int index, const GURL& url) {
293 HistoryServiceFactory::GetForProfileWithoutCreating(
294 test()->GetProfile(index))->DeleteURL(url);
295 if (test()->use_verifier())
296 HistoryServiceFactory::GetForProfile(test()->verifier(),
297 Profile::IMPLICIT_ACCESS)->
298 DeleteURL(url);
299 WaitForHistoryDBThread(index);
300 }
301
DeleteUrlsFromHistory(int index,const std::vector<GURL> & urls)302 void DeleteUrlsFromHistory(int index, const std::vector<GURL>& urls) {
303 HistoryServiceFactory::GetForProfileWithoutCreating(
304 test()->GetProfile(index))->DeleteURLsForTest(urls);
305 if (test()->use_verifier())
306 HistoryServiceFactory::GetForProfile(test()->verifier(),
307 Profile::IMPLICIT_ACCESS)->
308 DeleteURLsForTest(urls);
309 WaitForHistoryDBThread(index);
310 }
311
CheckURLRowVectorsAreEqual(const history::URLRows & left,const history::URLRows & right)312 bool CheckURLRowVectorsAreEqual(const history::URLRows& left,
313 const history::URLRows& right) {
314 if (left.size() != right.size())
315 return false;
316 for (size_t i = 0; i < left.size(); ++i) {
317 // URLs could be out-of-order, so look for a matching URL in the second
318 // array.
319 bool found = false;
320 for (size_t j = 0; j < right.size(); ++j) {
321 if (left[i].url() == right[j].url()) {
322 if (CheckURLRowsAreEqual(left[i], right[j])) {
323 found = true;
324 break;
325 }
326 }
327 }
328 if (!found)
329 return false;
330 }
331 return true;
332 }
333
AreVisitsEqual(const history::VisitVector & visit1,const history::VisitVector & visit2)334 bool AreVisitsEqual(const history::VisitVector& visit1,
335 const history::VisitVector& visit2) {
336 if (visit1.size() != visit2.size())
337 return false;
338 for (size_t i = 0; i < visit1.size(); ++i) {
339 if (visit1[i].transition != visit2[i].transition)
340 return false;
341 if (visit1[i].visit_time != visit2[i].visit_time)
342 return false;
343 }
344 return true;
345 }
346
AreVisitsUnique(const history::VisitVector & visits)347 bool AreVisitsUnique(const history::VisitVector& visits) {
348 base::Time t = base::Time::FromInternalValue(0);
349 for (size_t i = 0; i < visits.size(); ++i) {
350 if (t == visits[i].visit_time)
351 return false;
352 t = visits[i].visit_time;
353 }
354 return true;
355 }
356
CheckURLRowsAreEqual(const history::URLRow & left,const history::URLRow & right)357 bool CheckURLRowsAreEqual(
358 const history::URLRow& left, const history::URLRow& right) {
359 return (left.url() == right.url()) &&
360 (left.title() == right.title()) &&
361 (left.visit_count() == right.visit_count()) &&
362 (left.typed_count() == right.typed_count()) &&
363 (left.last_visit() == right.last_visit()) &&
364 (left.hidden() == right.hidden());
365 }
366
CheckAllProfilesHaveSameURLsAsVerifier()367 bool CheckAllProfilesHaveSameURLsAsVerifier() {
368 HistoryService* verifier_service =
369 HistoryServiceFactory::GetForProfile(test()->verifier(),
370 Profile::IMPLICIT_ACCESS);
371 history::URLRows verifier_urls =
372 GetTypedUrlsFromHistoryService(verifier_service);
373 for (int i = 0; i < test()->num_clients(); ++i) {
374 history::URLRows urls = GetTypedUrlsFromClient(i);
375 if (!CheckURLRowVectorsAreEqual(verifier_urls, urls))
376 return false;
377 }
378 return true;
379 }
380
381 namespace {
382
383 // Helper class used in the implementation of
384 // AwaitCheckAllProfilesHaveSameURLsAsVerifier.
385 class ProfilesHaveSameURLsChecker : public MultiClientStatusChangeChecker {
386 public:
387 ProfilesHaveSameURLsChecker();
388 virtual ~ProfilesHaveSameURLsChecker();
389
390 virtual bool IsExitConditionSatisfied() OVERRIDE;
391 virtual std::string GetDebugMessage() const OVERRIDE;
392 };
393
ProfilesHaveSameURLsChecker()394 ProfilesHaveSameURLsChecker::ProfilesHaveSameURLsChecker()
395 : MultiClientStatusChangeChecker(
396 sync_datatype_helper::test()->GetSyncServices()) {}
397
~ProfilesHaveSameURLsChecker()398 ProfilesHaveSameURLsChecker::~ProfilesHaveSameURLsChecker() {}
399
IsExitConditionSatisfied()400 bool ProfilesHaveSameURLsChecker::IsExitConditionSatisfied() {
401 return CheckAllProfilesHaveSameURLsAsVerifier();
402 }
403
GetDebugMessage() const404 std::string ProfilesHaveSameURLsChecker::GetDebugMessage() const {
405 return "Waiting for matching typed urls profiles";
406 }
407
408 } // namespace
409
AwaitCheckAllProfilesHaveSameURLsAsVerifier()410 bool AwaitCheckAllProfilesHaveSameURLsAsVerifier() {
411 ProfilesHaveSameURLsChecker checker;
412 checker.Wait();
413 return !checker.TimedOut();
414 }
415
416 } // namespace typed_urls_helper
417