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.
5 #include "chrome/browser/sync/test/integration/typed_urls_helper.h"
7 #include "base/compiler_specific.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/task/cancelable_task_tracker.h"
11 #include "base/time/time.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"
22 using sync_datatype_helper::test;
26 class FlushHistoryDBQueueTask : public history::HistoryDBTask {
28 explicit FlushHistoryDBQueueTask(base::WaitableEvent* event)
29 : wait_event_(event) {}
30 virtual bool RunOnDBThread(history::HistoryBackend* backend,
31 history::HistoryDatabase* db) OVERRIDE {
32 wait_event_->Signal();
36 virtual void DoneRunOnMainThread() OVERRIDE {}
39 virtual ~FlushHistoryDBQueueTask() {}
41 base::WaitableEvent* wait_event_;
44 class GetTypedUrlsTask : public history::HistoryDBTask {
46 GetTypedUrlsTask(history::URLRows* rows, base::WaitableEvent* event)
47 : rows_(rows), wait_event_(event) {}
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();
57 virtual void DoneRunOnMainThread() OVERRIDE {}
60 virtual ~GetTypedUrlsTask() {}
62 history::URLRows* rows_;
63 base::WaitableEvent* wait_event_;
66 class GetUrlTask : public history::HistoryDBTask {
68 GetUrlTask(const GURL& url,
71 base::WaitableEvent* event)
72 : url_(url), row_(row), wait_event_(event), found_(found) {}
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();
82 virtual void DoneRunOnMainThread() OVERRIDE {}
85 virtual ~GetUrlTask() {}
88 history::URLRow* row_;
89 base::WaitableEvent* wait_event_;
93 class GetVisitsTask : public history::HistoryDBTask {
95 GetVisitsTask(history::URLID id,
96 history::VisitVector* visits,
97 base::WaitableEvent* event)
98 : id_(id), visits_(visits), wait_event_(event) {}
100 virtual bool RunOnDBThread(history::HistoryBackend* backend,
101 history::HistoryDatabase* db) OVERRIDE {
103 backend->GetVisitsForURL(id_, visits_);
104 wait_event_->Signal();
108 virtual void DoneRunOnMainThread() OVERRIDE {}
111 virtual ~GetVisitsTask() {}
114 history::VisitVector* visits_;
115 base::WaitableEvent* wait_event_;
118 class RemoveVisitsTask : public history::HistoryDBTask {
120 RemoveVisitsTask(const history::VisitVector& visits,
121 base::WaitableEvent* event)
122 : visits_(visits), wait_event_(event) {}
124 virtual bool RunOnDBThread(history::HistoryBackend* backend,
125 history::HistoryDatabase* db) OVERRIDE {
127 backend->RemoveVisits(visits_);
128 wait_event_->Signal();
132 virtual void DoneRunOnMainThread() OVERRIDE {}
135 virtual ~RemoveVisitsTask() {}
137 const history::VisitVector& visits_;
138 base::WaitableEvent* wait_event_;
141 // Waits for the history DB thread to finish executing its current set of
143 void WaitForHistoryDBThread(int index) {
144 base::CancelableTaskTracker tracker;
145 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
146 test()->GetProfile(index));
147 base::WaitableEvent wait_event(true, false);
148 service->ScheduleDBTask(
149 scoped_ptr<history::HistoryDBTask>(
150 new FlushHistoryDBQueueTask(&wait_event)),
155 // Creates a URLRow in the specified HistoryService with the passed transition
157 void AddToHistory(HistoryService* service,
159 content::PageTransition transition,
160 history::VisitSource source,
161 const base::Time& timestamp) {
162 service->AddPage(url,
167 history::RedirectList(),
171 service->SetPageTitle(url, base::ASCIIToUTF16(url.spec() + " - title"));
174 history::URLRows GetTypedUrlsFromHistoryService(HistoryService* service) {
175 base::CancelableTaskTracker tracker;
176 history::URLRows rows;
177 base::WaitableEvent wait_event(true, false);
178 service->ScheduleDBTask(
179 scoped_ptr<history::HistoryDBTask>(
180 new GetTypedUrlsTask(&rows, &wait_event)),
186 bool GetUrlFromHistoryService(HistoryService* service,
187 const GURL& url, history::URLRow* row) {
188 base::CancelableTaskTracker tracker;
189 base::WaitableEvent wait_event(true, false);
191 service->ScheduleDBTask(
192 scoped_ptr<history::HistoryDBTask>(
193 new GetUrlTask(url, row, &found, &wait_event)),
199 history::VisitVector GetVisitsFromHistoryService(HistoryService* service,
201 base::CancelableTaskTracker tracker;
202 base::WaitableEvent wait_event(true, false);
203 history::VisitVector visits;
204 service->ScheduleDBTask(
205 scoped_ptr<history::HistoryDBTask>(
206 new GetVisitsTask(id, &visits, &wait_event)),
212 void RemoveVisitsFromHistoryService(HistoryService* service,
213 const history::VisitVector& visits) {
214 base::CancelableTaskTracker tracker;
215 base::WaitableEvent wait_event(true, false);
216 service->ScheduleDBTask(
217 scoped_ptr<history::HistoryDBTask>(
218 new RemoveVisitsTask(visits, &wait_event)),
223 static base::Time* timestamp = NULL;
227 namespace typed_urls_helper {
229 history::URLRows GetTypedUrlsFromClient(int index) {
230 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
231 test()->GetProfile(index));
232 return GetTypedUrlsFromHistoryService(service);
235 bool GetUrlFromClient(int index, const GURL& url, history::URLRow* row) {
236 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
237 test()->GetProfile(index));
238 return GetUrlFromHistoryService(service, url, row);
241 history::VisitVector GetVisitsFromClient(int index, history::URLID id) {
242 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
243 test()->GetProfile(index));
244 return GetVisitsFromHistoryService(service, id);
247 void RemoveVisitsFromClient(int index, const history::VisitVector& visits) {
248 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating(
249 test()->GetProfile(index));
250 RemoveVisitsFromHistoryService(service, visits);
253 base::Time GetTimestamp() {
254 // The history subsystem doesn't like identical timestamps for page visits,
255 // and it will massage the visit timestamps if we try to use identical
256 // values, which can lead to spurious errors. So make sure all timestamps
259 ::timestamp = new base::Time(base::Time::Now());
260 base::Time original = *::timestamp;
261 *::timestamp += base::TimeDelta::FromMilliseconds(1);
265 void AddUrlToHistory(int index, const GURL& url) {
266 AddUrlToHistoryWithTransition(index, url, content::PAGE_TRANSITION_TYPED,
267 history::SOURCE_BROWSED);
269 void AddUrlToHistoryWithTransition(int index,
271 content::PageTransition transition,
272 history::VisitSource source) {
273 base::Time timestamp = GetTimestamp();
274 AddUrlToHistoryWithTimestamp(index, url, transition, source, timestamp);
276 void AddUrlToHistoryWithTimestamp(int index,
278 content::PageTransition transition,
279 history::VisitSource source,
280 const base::Time& timestamp) {
281 AddToHistory(HistoryServiceFactory::GetForProfileWithoutCreating(
282 test()->GetProfile(index)),
287 if (test()->use_verifier())
289 HistoryServiceFactory::GetForProfile(test()->verifier(),
290 Profile::IMPLICIT_ACCESS),
296 // Wait until the AddPage() request has completed so we know the change has
297 // filtered down to the sync observers (don't need to wait for the
298 // verifier profile since it doesn't sync).
299 WaitForHistoryDBThread(index);
302 void DeleteUrlFromHistory(int index, const GURL& url) {
303 HistoryServiceFactory::GetForProfileWithoutCreating(
304 test()->GetProfile(index))->DeleteURL(url);
305 if (test()->use_verifier())
306 HistoryServiceFactory::GetForProfile(test()->verifier(),
307 Profile::IMPLICIT_ACCESS)->
309 WaitForHistoryDBThread(index);
312 void DeleteUrlsFromHistory(int index, const std::vector<GURL>& urls) {
313 HistoryServiceFactory::GetForProfileWithoutCreating(
314 test()->GetProfile(index))->DeleteURLsForTest(urls);
315 if (test()->use_verifier())
316 HistoryServiceFactory::GetForProfile(test()->verifier(),
317 Profile::IMPLICIT_ACCESS)->
318 DeleteURLsForTest(urls);
319 WaitForHistoryDBThread(index);
322 bool CheckURLRowVectorsAreEqual(const history::URLRows& left,
323 const history::URLRows& right) {
324 if (left.size() != right.size())
326 for (size_t i = 0; i < left.size(); ++i) {
327 // URLs could be out-of-order, so look for a matching URL in the second
330 for (size_t j = 0; j < right.size(); ++j) {
331 if (left[i].url() == right[j].url()) {
332 if (CheckURLRowsAreEqual(left[i], right[j])) {
344 bool AreVisitsEqual(const history::VisitVector& visit1,
345 const history::VisitVector& visit2) {
346 if (visit1.size() != visit2.size())
348 for (size_t i = 0; i < visit1.size(); ++i) {
349 if (visit1[i].transition != visit2[i].transition)
351 if (visit1[i].visit_time != visit2[i].visit_time)
357 bool AreVisitsUnique(const history::VisitVector& visits) {
358 base::Time t = base::Time::FromInternalValue(0);
359 for (size_t i = 0; i < visits.size(); ++i) {
360 if (t == visits[i].visit_time)
362 t = visits[i].visit_time;
367 bool CheckURLRowsAreEqual(
368 const history::URLRow& left, const history::URLRow& right) {
369 return (left.url() == right.url()) &&
370 (left.title() == right.title()) &&
371 (left.visit_count() == right.visit_count()) &&
372 (left.typed_count() == right.typed_count()) &&
373 (left.last_visit() == right.last_visit()) &&
374 (left.hidden() == right.hidden());
377 bool CheckAllProfilesHaveSameURLsAsVerifier() {
378 HistoryService* verifier_service =
379 HistoryServiceFactory::GetForProfile(test()->verifier(),
380 Profile::IMPLICIT_ACCESS);
381 history::URLRows verifier_urls =
382 GetTypedUrlsFromHistoryService(verifier_service);
383 for (int i = 0; i < test()->num_clients(); ++i) {
384 history::URLRows urls = GetTypedUrlsFromClient(i);
385 if (!CheckURLRowVectorsAreEqual(verifier_urls, urls))
393 // Helper class used in the implementation of
394 // AwaitCheckAllProfilesHaveSameURLsAsVerifier.
395 class ProfilesHaveSameURLsChecker : public MultiClientStatusChangeChecker {
397 ProfilesHaveSameURLsChecker();
398 virtual ~ProfilesHaveSameURLsChecker();
400 virtual bool IsExitConditionSatisfied() OVERRIDE;
401 virtual std::string GetDebugMessage() const OVERRIDE;
404 ProfilesHaveSameURLsChecker::ProfilesHaveSameURLsChecker()
405 : MultiClientStatusChangeChecker(
406 sync_datatype_helper::test()->GetSyncServices()) {}
408 ProfilesHaveSameURLsChecker::~ProfilesHaveSameURLsChecker() {}
410 bool ProfilesHaveSameURLsChecker::IsExitConditionSatisfied() {
411 return CheckAllProfilesHaveSameURLsAsVerifier();
414 std::string ProfilesHaveSameURLsChecker::GetDebugMessage() const {
415 return "Waiting for matching typed urls profiles";
420 bool AwaitCheckAllProfilesHaveSameURLsAsVerifier() {
421 ProfilesHaveSameURLsChecker checker;
423 return !checker.TimedOut();
426 } // namespace typed_urls_helper