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