Home | History | Annotate | Download | only in integration
      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