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/profiles/profile.h" 17#include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h" 18#include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 19#include "chrome/browser/sync/test/integration/sync_test.h" 20#include "components/history/core/browser/history_types.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( 149 scoped_ptr<history::HistoryDBTask>( 150 new FlushHistoryDBQueueTask(&wait_event)), 151 &tracker); 152 wait_event.Wait(); 153} 154 155// Creates a URLRow in the specified HistoryService with the passed transition 156// type. 157void AddToHistory(HistoryService* service, 158 const GURL& url, 159 ui::PageTransition transition, 160 history::VisitSource source, 161 const base::Time& timestamp) { 162 service->AddPage(url, 163 timestamp, 164 NULL, // scope 165 1234, // page_id 166 GURL(), // referrer 167 history::RedirectList(), 168 transition, 169 source, 170 false); 171 service->SetPageTitle(url, base::ASCIIToUTF16(url.spec() + " - title")); 172} 173 174history::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)), 181 &tracker); 182 wait_event.Wait(); 183 return rows; 184} 185 186bool GetUrlFromHistoryService(HistoryService* service, 187 const GURL& url, history::URLRow* row) { 188 base::CancelableTaskTracker tracker; 189 base::WaitableEvent wait_event(true, false); 190 bool found = false; 191 service->ScheduleDBTask( 192 scoped_ptr<history::HistoryDBTask>( 193 new GetUrlTask(url, row, &found, &wait_event)), 194 &tracker); 195 wait_event.Wait(); 196 return found; 197} 198 199history::VisitVector GetVisitsFromHistoryService(HistoryService* service, 200 history::URLID id) { 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)), 207 &tracker); 208 wait_event.Wait(); 209 return visits; 210} 211 212void 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)), 219 &tracker); 220 wait_event.Wait(); 221} 222 223static base::Time* timestamp = NULL; 224 225} // namespace 226 227namespace typed_urls_helper { 228 229history::URLRows GetTypedUrlsFromClient(int index) { 230 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating( 231 test()->GetProfile(index)); 232 return GetTypedUrlsFromHistoryService(service); 233} 234 235bool GetUrlFromClient(int index, const GURL& url, history::URLRow* row) { 236 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating( 237 test()->GetProfile(index)); 238 return GetUrlFromHistoryService(service, url, row); 239} 240 241history::VisitVector GetVisitsFromClient(int index, history::URLID id) { 242 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating( 243 test()->GetProfile(index)); 244 return GetVisitsFromHistoryService(service, id); 245} 246 247void RemoveVisitsFromClient(int index, const history::VisitVector& visits) { 248 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating( 249 test()->GetProfile(index)); 250 RemoveVisitsFromHistoryService(service, visits); 251} 252 253base::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 257 // are unique. 258 if (!::timestamp) 259 ::timestamp = new base::Time(base::Time::Now()); 260 base::Time original = *::timestamp; 261 *::timestamp += base::TimeDelta::FromMilliseconds(1); 262 return original; 263} 264 265void AddUrlToHistory(int index, const GURL& url) { 266 AddUrlToHistoryWithTransition(index, url, ui::PAGE_TRANSITION_TYPED, 267 history::SOURCE_BROWSED); 268} 269void AddUrlToHistoryWithTransition(int index, 270 const GURL& url, 271 ui::PageTransition transition, 272 history::VisitSource source) { 273 base::Time timestamp = GetTimestamp(); 274 AddUrlToHistoryWithTimestamp(index, url, transition, source, timestamp); 275} 276void AddUrlToHistoryWithTimestamp(int index, 277 const GURL& url, 278 ui::PageTransition transition, 279 history::VisitSource source, 280 const base::Time& timestamp) { 281 AddToHistory(HistoryServiceFactory::GetForProfileWithoutCreating( 282 test()->GetProfile(index)), 283 url, 284 transition, 285 source, 286 timestamp); 287 if (test()->use_verifier()) 288 AddToHistory( 289 HistoryServiceFactory::GetForProfile(test()->verifier(), 290 Profile::IMPLICIT_ACCESS), 291 url, 292 transition, 293 source, 294 timestamp); 295 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); 300} 301 302void 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)-> 308 DeleteURL(url); 309 WaitForHistoryDBThread(index); 310} 311 312void 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); 320} 321 322bool CheckURLRowVectorsAreEqual(const history::URLRows& left, 323 const history::URLRows& right) { 324 if (left.size() != right.size()) 325 return false; 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 328 // array. 329 bool found = false; 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])) { 333 found = true; 334 break; 335 } 336 } 337 } 338 if (!found) 339 return false; 340 } 341 return true; 342} 343 344bool AreVisitsEqual(const history::VisitVector& visit1, 345 const history::VisitVector& visit2) { 346 if (visit1.size() != visit2.size()) 347 return false; 348 for (size_t i = 0; i < visit1.size(); ++i) { 349 if (visit1[i].transition != visit2[i].transition) 350 return false; 351 if (visit1[i].visit_time != visit2[i].visit_time) 352 return false; 353 } 354 return true; 355} 356 357bool 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) 361 return false; 362 t = visits[i].visit_time; 363 } 364 return true; 365} 366 367bool 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()); 375} 376 377bool 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)) 386 return false; 387 } 388 return true; 389} 390 391namespace { 392 393// Helper class used in the implementation of 394// AwaitCheckAllProfilesHaveSameURLsAsVerifier. 395class ProfilesHaveSameURLsChecker : public MultiClientStatusChangeChecker { 396 public: 397 ProfilesHaveSameURLsChecker(); 398 virtual ~ProfilesHaveSameURLsChecker(); 399 400 virtual bool IsExitConditionSatisfied() OVERRIDE; 401 virtual std::string GetDebugMessage() const OVERRIDE; 402}; 403 404ProfilesHaveSameURLsChecker::ProfilesHaveSameURLsChecker() 405 : MultiClientStatusChangeChecker( 406 sync_datatype_helper::test()->GetSyncServices()) {} 407 408ProfilesHaveSameURLsChecker::~ProfilesHaveSameURLsChecker() {} 409 410bool ProfilesHaveSameURLsChecker::IsExitConditionSatisfied() { 411 return CheckAllProfilesHaveSameURLsAsVerifier(); 412} 413 414std::string ProfilesHaveSameURLsChecker::GetDebugMessage() const { 415 return "Waiting for matching typed urls profiles"; 416} 417 418} // namespace 419 420bool AwaitCheckAllProfilesHaveSameURLsAsVerifier() { 421 ProfilesHaveSameURLsChecker checker; 422 checker.Wait(); 423 return !checker.TimedOut(); 424} 425 426} // namespace typed_urls_helper 427