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