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: 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 44 class 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 66 class 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 93 class 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 118 class 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. 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. 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 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 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 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 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 219 history::URLRows GetTypedUrlsFromClient(int index) { 220 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating( 221 test()->GetProfile(index)); 222 return GetTypedUrlsFromHistoryService(service); 223 } 224 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 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 237 void RemoveVisitsFromClient(int index, const history::VisitVector& visits) { 238 HistoryService* service = HistoryServiceFactory::GetForProfileWithoutCreating( 239 test()->GetProfile(index)); 240 RemoveVisitsFromHistoryService(service, visits); 241 } 242 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 255 void AddUrlToHistory(int index, const GURL& url) { 256 AddUrlToHistoryWithTransition(index, url, content::PAGE_TRANSITION_TYPED, 257 history::SOURCE_BROWSED); 258 } 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 } 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 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 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 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 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 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 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 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 394 ProfilesHaveSameURLsChecker::ProfilesHaveSameURLsChecker() 395 : MultiClientStatusChangeChecker( 396 sync_datatype_helper::test()->GetSyncServices()) {} 397 398 ProfilesHaveSameURLsChecker::~ProfilesHaveSameURLsChecker() {} 399 400 bool ProfilesHaveSameURLsChecker::IsExitConditionSatisfied() { 401 return CheckAllProfilesHaveSameURLsAsVerifier(); 402 } 403 404 std::string ProfilesHaveSameURLsChecker::GetDebugMessage() const { 405 return "Waiting for matching typed urls profiles"; 406 } 407 408 } // namespace 409 410 bool AwaitCheckAllProfilesHaveSameURLsAsVerifier() { 411 ProfilesHaveSameURLsChecker checker; 412 checker.Wait(); 413 return !checker.TimedOut(); 414 } 415 416 } // namespace typed_urls_helper 417