Home | History | Annotate | Download | only in history
      1 // Copyright (c) 2011 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 // History unit tests come in two flavors:
      6 //
      7 // 1. The more complicated style is that the unit test creates a full history
      8 //    service. This spawns a background thread for the history backend, and
      9 //    all communication is asynchronous. This is useful for testing more
     10 //    complicated things or end-to-end behavior.
     11 //
     12 // 2. The simpler style is to create a history backend on this thread and
     13 //    access it directly without a HistoryService object. This is much simpler
     14 //    because communication is synchronous. Generally, sets should go through
     15 //    the history backend (since there is a lot of logic) but gets can come
     16 //    directly from the HistoryDatabase. This is because the backend generally
     17 //    has no logic in the getter except threading stuff, which we don't want
     18 //    to run.
     19 
     20 #include <time.h>
     21 
     22 #include <algorithm>
     23 #include <string>
     24 
     25 #include "app/sql/connection.h"
     26 #include "app/sql/statement.h"
     27 #include "base/basictypes.h"
     28 #include "base/callback.h"
     29 #include "base/command_line.h"
     30 #include "base/file_path.h"
     31 #include "base/file_util.h"
     32 #include "base/memory/scoped_temp_dir.h"
     33 #include "base/memory/scoped_vector.h"
     34 #include "base/message_loop.h"
     35 #include "base/path_service.h"
     36 #include "base/string_util.h"
     37 #include "base/task.h"
     38 #include "base/utf_string_conversions.h"
     39 #include "chrome/browser/download/download_item.h"
     40 #include "chrome/browser/history/download_create_info.h"
     41 #include "chrome/browser/history/history.h"
     42 #include "chrome/browser/history/history_backend.h"
     43 #include "chrome/browser/history/history_database.h"
     44 #include "chrome/browser/history/history_notifications.h"
     45 #include "chrome/browser/history/in_memory_database.h"
     46 #include "chrome/browser/history/in_memory_history_backend.h"
     47 #include "chrome/browser/history/page_usage_data.h"
     48 #include "chrome/common/chrome_paths.h"
     49 #include "chrome/common/thumbnail_score.h"
     50 #include "chrome/tools/profiles/thumbnail-inl.h"
     51 #include "content/common/notification_details.h"
     52 #include "content/common/notification_source.h"
     53 #include "testing/gtest/include/gtest/gtest.h"
     54 #include "third_party/skia/include/core/SkBitmap.h"
     55 #include "ui/gfx/codec/jpeg_codec.h"
     56 
     57 using base::Time;
     58 using base::TimeDelta;
     59 
     60 namespace history {
     61 class HistoryTest;
     62 }
     63 
     64 // Specialize RunnableMethodTraits for HistoryTest so we can create callbacks.
     65 // None of these callbacks can outlast the test, so there is not need to retain
     66 // the HistoryTest object.
     67 DISABLE_RUNNABLE_METHOD_REFCOUNT(history::HistoryTest);
     68 
     69 namespace history {
     70 
     71 namespace {
     72 
     73 // The tracker uses RenderProcessHost pointers for scoping but never
     74 // dereferences them. We use ints because it's easier. This function converts
     75 // between the two.
     76 static void* MakeFakeHost(int id) {
     77   void* host = 0;
     78   memcpy(&host, &id, sizeof(id));
     79   return host;
     80 }
     81 
     82 }  // namespace
     83 
     84 // Delegate class for when we create a backend without a HistoryService.
     85 class BackendDelegate : public HistoryBackend::Delegate {
     86  public:
     87   explicit BackendDelegate(HistoryTest* history_test)
     88       : history_test_(history_test) {
     89   }
     90 
     91   virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
     92   virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE;
     93   virtual void BroadcastNotifications(NotificationType type,
     94                                       HistoryDetails* details) OVERRIDE;
     95   virtual void DBLoaded() OVERRIDE {}
     96   virtual void StartTopSitesMigration() OVERRIDE {}
     97  private:
     98   HistoryTest* history_test_;
     99 };
    100 
    101 // This must be outside the anonymous namespace for the friend statement in
    102 // HistoryBackend to work.
    103 class HistoryTest : public testing::Test {
    104  public:
    105   HistoryTest()
    106       : history_service_(NULL),
    107         got_thumbnail_callback_(false),
    108         redirect_query_success_(false),
    109         query_url_success_(false),
    110         db_(NULL) {
    111   }
    112   ~HistoryTest() {
    113   }
    114 
    115   // Creates the HistoryBackend and HistoryDatabase on the current thread,
    116   // assigning the values to backend_ and db_.
    117   void CreateBackendAndDatabase() {
    118     backend_ =
    119         new HistoryBackend(history_dir_, new BackendDelegate(this), NULL);
    120     backend_->Init(std::string(), false);
    121     db_ = backend_->db_.get();
    122     DCHECK(in_mem_backend_.get()) << "Mem backend should have been set by "
    123         "HistoryBackend::Init";
    124   }
    125 
    126   void OnSegmentUsageAvailable(CancelableRequestProvider::Handle handle,
    127                                std::vector<PageUsageData*>* data) {
    128     page_usage_data_->swap(*data);
    129     MessageLoop::current()->Quit();
    130   }
    131 
    132   void OnDeleteURLsDone(CancelableRequestProvider::Handle handle) {
    133     MessageLoop::current()->Quit();
    134   }
    135 
    136   void OnMostVisitedURLsAvailable(CancelableRequestProvider::Handle handle,
    137                                   MostVisitedURLList url_list) {
    138     most_visited_urls_.swap(url_list);
    139     MessageLoop::current()->Quit();
    140   }
    141 
    142  protected:
    143   friend class BackendDelegate;
    144 
    145   // testing::Test
    146   virtual void SetUp() {
    147     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    148     history_dir_ = temp_dir_.path().AppendASCII("HistoryTest");
    149     ASSERT_TRUE(file_util::CreateDirectory(history_dir_));
    150   }
    151 
    152   void DeleteBackend() {
    153     if (backend_) {
    154       backend_->Closing();
    155       backend_ = NULL;
    156     }
    157   }
    158 
    159   virtual void TearDown() {
    160     DeleteBackend();
    161 
    162     if (history_service_)
    163       CleanupHistoryService();
    164 
    165     // Make sure we don't have any event pending that could disrupt the next
    166     // test.
    167     MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
    168     MessageLoop::current()->Run();
    169   }
    170 
    171   void CleanupHistoryService() {
    172     DCHECK(history_service_.get());
    173 
    174     history_service_->NotifyRenderProcessHostDestruction(0);
    175     history_service_->SetOnBackendDestroyTask(new MessageLoop::QuitTask);
    176     history_service_->Cleanup();
    177     history_service_ = NULL;
    178 
    179     // Wait for the backend class to terminate before deleting the files and
    180     // moving to the next test. Note: if this never terminates, somebody is
    181     // probably leaking a reference to the history backend, so it never calls
    182     // our destroy task.
    183     MessageLoop::current()->Run();
    184   }
    185 
    186   int64 AddDownload(int32 state, const Time& time) {
    187     DownloadCreateInfo download(FilePath(FILE_PATH_LITERAL("foo-path")),
    188                                 GURL("foo-url"), time, 0, 512, state, 0, false);
    189     return db_->CreateDownload(download);
    190   }
    191 
    192   // Fills the query_url_row_ and query_url_visits_ structures with the
    193   // information about the given URL and returns true. If the URL was not
    194   // found, this will return false and those structures will not be changed.
    195   bool QueryURL(HistoryService* history, const GURL& url) {
    196     history->QueryURL(url, true, &consumer_,
    197                       NewCallback(this, &HistoryTest::SaveURLAndQuit));
    198     MessageLoop::current()->Run();  // Will be exited in SaveURLAndQuit.
    199     return query_url_success_;
    200   }
    201 
    202   // Callback for HistoryService::QueryURL.
    203   void SaveURLAndQuit(HistoryService::Handle handle,
    204                       bool success,
    205                       const URLRow* url_row,
    206                       VisitVector* visit_vector) {
    207     query_url_success_ = success;
    208     if (query_url_success_) {
    209       query_url_row_ = *url_row;
    210       query_url_visits_.swap(*visit_vector);
    211     } else {
    212       query_url_row_ = URLRow();
    213       query_url_visits_.clear();
    214     }
    215     MessageLoop::current()->Quit();
    216   }
    217 
    218   // Fills in saved_redirects_ with the redirect information for the given URL,
    219   // returning true on success. False means the URL was not found.
    220   bool QueryRedirectsFrom(HistoryService* history, const GURL& url) {
    221     history->QueryRedirectsFrom(url, &consumer_,
    222         NewCallback(this, &HistoryTest::OnRedirectQueryComplete));
    223     MessageLoop::current()->Run();  // Will be exited in *QueryComplete.
    224     return redirect_query_success_;
    225   }
    226 
    227   // Callback for QueryRedirects.
    228   void OnRedirectQueryComplete(HistoryService::Handle handle,
    229                                GURL url,
    230                                bool success,
    231                                history::RedirectList* redirects) {
    232     redirect_query_success_ = success;
    233     if (redirect_query_success_)
    234       saved_redirects_.swap(*redirects);
    235     else
    236       saved_redirects_.clear();
    237     MessageLoop::current()->Quit();
    238   }
    239 
    240   ScopedTempDir temp_dir_;
    241 
    242   MessageLoopForUI message_loop_;
    243 
    244   // PageUsageData vector to test segments.
    245   ScopedVector<PageUsageData> page_usage_data_;
    246 
    247   MostVisitedURLList most_visited_urls_;
    248 
    249   // When non-NULL, this will be deleted on tear down and we will block until
    250   // the backend thread has completed. This allows tests for the history
    251   // service to use this feature, but other tests to ignore this.
    252   scoped_refptr<HistoryService> history_service_;
    253 
    254   // names of the database files
    255   FilePath history_dir_;
    256 
    257   // Set by the thumbnail callback when we get data, you should be sure to
    258   // clear this before issuing a thumbnail request.
    259   bool got_thumbnail_callback_;
    260   std::vector<unsigned char> thumbnail_data_;
    261 
    262   // Set by the redirect callback when we get data. You should be sure to
    263   // clear this before issuing a redirect request.
    264   history::RedirectList saved_redirects_;
    265   bool redirect_query_success_;
    266 
    267   // For history requests.
    268   CancelableRequestConsumer consumer_;
    269 
    270   // For saving URL info after a call to QueryURL
    271   bool query_url_success_;
    272   URLRow query_url_row_;
    273   VisitVector query_url_visits_;
    274 
    275   // Created via CreateBackendAndDatabase.
    276   scoped_refptr<HistoryBackend> backend_;
    277   scoped_ptr<InMemoryHistoryBackend> in_mem_backend_;
    278   HistoryDatabase* db_;  // Cached reference to the backend's database.
    279 };
    280 
    281 void BackendDelegate::SetInMemoryBackend(InMemoryHistoryBackend* backend) {
    282   // Save the in-memory backend to the history test object, this happens
    283   // synchronously, so we don't have to do anything fancy.
    284   history_test_->in_mem_backend_.reset(backend);
    285 }
    286 
    287 void BackendDelegate::BroadcastNotifications(NotificationType type,
    288                                              HistoryDetails* details) {
    289   // Currently, just send the notifications directly to the in-memory database.
    290   // We may want do do something more fancy in the future.
    291   Details<HistoryDetails> det(details);
    292   history_test_->in_mem_backend_->Observe(type,
    293       Source<HistoryTest>(NULL), det);
    294 
    295   // The backend passes ownership of the details pointer to us.
    296   delete details;
    297 }
    298 
    299 TEST_F(HistoryTest, ClearBrowsingData_Downloads) {
    300   CreateBackendAndDatabase();
    301 
    302   Time now = Time::Now();
    303   TimeDelta one_day = TimeDelta::FromDays(1);
    304   Time month_ago = now - TimeDelta::FromDays(30);
    305 
    306   // Initially there should be nothing in the downloads database.
    307   std::vector<DownloadCreateInfo> downloads;
    308   db_->QueryDownloads(&downloads);
    309   EXPECT_EQ(0U, downloads.size());
    310 
    311   // Keep track of these as we need to update them later during the test.
    312   DownloadID in_progress, removing;
    313 
    314   // Create one with a 0 time.
    315   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, Time()));
    316   // Create one for now and +/- 1 day.
    317   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now - one_day));
    318   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now));
    319   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, now + one_day));
    320   // Try the other four states.
    321   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE, month_ago));
    322   EXPECT_NE(0, in_progress = AddDownload(DownloadItem::IN_PROGRESS, month_ago));
    323   EXPECT_NE(0, AddDownload(DownloadItem::CANCELLED, month_ago));
    324   EXPECT_NE(0, AddDownload(DownloadItem::INTERRUPTED, month_ago));
    325   EXPECT_NE(0, removing = AddDownload(DownloadItem::REMOVING, month_ago));
    326 
    327   // Test to see if inserts worked.
    328   db_->QueryDownloads(&downloads);
    329   EXPECT_EQ(9U, downloads.size());
    330 
    331   // Try removing from current timestamp. This should delete the one in the
    332   // future and one very recent one.
    333   db_->RemoveDownloadsBetween(now, Time());
    334   db_->QueryDownloads(&downloads);
    335   EXPECT_EQ(7U, downloads.size());
    336 
    337   // Try removing from two months ago. This should not delete items that are
    338   // 'in progress' or in 'removing' state.
    339   db_->RemoveDownloadsBetween(now - TimeDelta::FromDays(60), Time());
    340   db_->QueryDownloads(&downloads);
    341   EXPECT_EQ(3U, downloads.size());
    342 
    343   // Download manager converts to TimeT, which is lossy, so we do the same
    344   // for comparison.
    345   Time month_ago_lossy = Time::FromTimeT(month_ago.ToTimeT());
    346 
    347   // Make sure the right values remain.
    348   EXPECT_EQ(DownloadItem::COMPLETE, downloads[0].state);
    349   EXPECT_EQ(0, downloads[0].start_time.ToInternalValue());
    350   EXPECT_EQ(DownloadItem::IN_PROGRESS, downloads[1].state);
    351   EXPECT_EQ(month_ago_lossy.ToInternalValue(),
    352             downloads[1].start_time.ToInternalValue());
    353   EXPECT_EQ(DownloadItem::REMOVING, downloads[2].state);
    354   EXPECT_EQ(month_ago_lossy.ToInternalValue(),
    355             downloads[2].start_time.ToInternalValue());
    356 
    357   // Change state so we can delete the downloads.
    358   EXPECT_TRUE(db_->UpdateDownload(512, DownloadItem::COMPLETE, in_progress));
    359   EXPECT_TRUE(db_->UpdateDownload(512, DownloadItem::CANCELLED, removing));
    360 
    361   // Try removing from Time=0. This should delete all.
    362   db_->RemoveDownloadsBetween(Time(), Time());
    363   db_->QueryDownloads(&downloads);
    364   EXPECT_EQ(0U, downloads.size());
    365 
    366   // Check removal of downloads stuck in IN_PROGRESS state.
    367   EXPECT_NE(0, AddDownload(DownloadItem::COMPLETE,    month_ago));
    368   EXPECT_NE(0, AddDownload(DownloadItem::IN_PROGRESS, month_ago));
    369   db_->QueryDownloads(&downloads);
    370   EXPECT_EQ(2U, downloads.size());
    371   db_->RemoveDownloadsBetween(Time(), Time());
    372   db_->QueryDownloads(&downloads);
    373   // IN_PROGRESS download should remain. It it indicated as "Canceled"
    374   EXPECT_EQ(1U, downloads.size());
    375   db_->CleanUpInProgressEntries();
    376   db_->QueryDownloads(&downloads);
    377   EXPECT_EQ(1U, downloads.size());
    378   db_->RemoveDownloadsBetween(Time(), Time());
    379   db_->QueryDownloads(&downloads);
    380   EXPECT_EQ(0U, downloads.size());
    381 }
    382 
    383 TEST_F(HistoryTest, AddPage) {
    384   scoped_refptr<HistoryService> history(new HistoryService);
    385   history_service_ = history;
    386   ASSERT_TRUE(history->Init(history_dir_, NULL));
    387 
    388   // Add the page once from a child frame.
    389   const GURL test_url("http://www.google.com/");
    390   history->AddPage(test_url, NULL, 0, GURL(),
    391                    PageTransition::MANUAL_SUBFRAME,
    392                    history::RedirectList(),
    393                    history::SOURCE_BROWSED, false);
    394   EXPECT_TRUE(QueryURL(history, test_url));
    395   EXPECT_EQ(1, query_url_row_.visit_count());
    396   EXPECT_EQ(0, query_url_row_.typed_count());
    397   EXPECT_TRUE(query_url_row_.hidden());  // Hidden because of child frame.
    398 
    399   // Add the page once from the main frame (should unhide it).
    400   history->AddPage(test_url, NULL, 0, GURL(), PageTransition::LINK,
    401                    history::RedirectList(),
    402                    history::SOURCE_BROWSED, false);
    403   EXPECT_TRUE(QueryURL(history, test_url));
    404   EXPECT_EQ(2, query_url_row_.visit_count());  // Added twice.
    405   EXPECT_EQ(0, query_url_row_.typed_count());  // Never typed.
    406   EXPECT_FALSE(query_url_row_.hidden());  // Because loaded in main frame.
    407 }
    408 
    409 TEST_F(HistoryTest, AddPageSameTimes) {
    410   scoped_refptr<HistoryService> history(new HistoryService);
    411   history_service_ = history;
    412   ASSERT_TRUE(history->Init(history_dir_, NULL));
    413 
    414   Time now = Time::Now();
    415   const GURL test_urls[] = {
    416     GURL("http://timer.first.page/"),
    417     GURL("http://timer.second.page/"),
    418     GURL("http://timer.third.page/"),
    419   };
    420 
    421   // Make sure that two pages added at the same time with no intervening
    422   // additions have different timestamps.
    423   history->AddPage(test_urls[0], now, NULL, 0, GURL(),
    424                    PageTransition::LINK,
    425                    history::RedirectList(),
    426                    history::SOURCE_BROWSED, false);
    427   EXPECT_TRUE(QueryURL(history, test_urls[0]));
    428   EXPECT_EQ(1, query_url_row_.visit_count());
    429   EXPECT_TRUE(now == query_url_row_.last_visit());  // gtest doesn't like Time
    430 
    431   history->AddPage(test_urls[1], now, NULL, 0, GURL(),
    432                    PageTransition::LINK,
    433                    history::RedirectList(),
    434                    history::SOURCE_BROWSED, false);
    435   EXPECT_TRUE(QueryURL(history, test_urls[1]));
    436   EXPECT_EQ(1, query_url_row_.visit_count());
    437   EXPECT_TRUE(now + TimeDelta::FromMicroseconds(1) ==
    438       query_url_row_.last_visit());
    439 
    440   // Make sure the next page, at a different time, is also correct.
    441   history->AddPage(test_urls[2], now + TimeDelta::FromMinutes(1),
    442                    NULL, 0, GURL(),
    443                    PageTransition::LINK,
    444                    history::RedirectList(),
    445                    history::SOURCE_BROWSED, false);
    446   EXPECT_TRUE(QueryURL(history, test_urls[2]));
    447   EXPECT_EQ(1, query_url_row_.visit_count());
    448   EXPECT_TRUE(now + TimeDelta::FromMinutes(1) ==
    449       query_url_row_.last_visit());
    450 }
    451 
    452 TEST_F(HistoryTest, AddRedirect) {
    453   scoped_refptr<HistoryService> history(new HistoryService);
    454   history_service_ = history;
    455   ASSERT_TRUE(history->Init(history_dir_, NULL));
    456 
    457   const char* first_sequence[] = {
    458     "http://first.page/",
    459     "http://second.page/"};
    460   int first_count = arraysize(first_sequence);
    461   history::RedirectList first_redirects;
    462   for (int i = 0; i < first_count; i++)
    463     first_redirects.push_back(GURL(first_sequence[i]));
    464 
    465   // Add the sequence of pages as a server with no referrer. Note that we need
    466   // to have a non-NULL page ID scope.
    467   history->AddPage(first_redirects.back(), MakeFakeHost(1), 0, GURL(),
    468                    PageTransition::LINK, first_redirects,
    469                    history::SOURCE_BROWSED,  true);
    470 
    471   // The first page should be added once with a link visit type (because we set
    472   // LINK when we added the original URL, and a referrer of nowhere (0).
    473   EXPECT_TRUE(QueryURL(history, first_redirects[0]));
    474   EXPECT_EQ(1, query_url_row_.visit_count());
    475   ASSERT_EQ(1U, query_url_visits_.size());
    476   int64 first_visit = query_url_visits_[0].visit_id;
    477   EXPECT_EQ(PageTransition::LINK |
    478             PageTransition::CHAIN_START, query_url_visits_[0].transition);
    479   EXPECT_EQ(0, query_url_visits_[0].referring_visit);  // No referrer.
    480 
    481   // The second page should be a server redirect type with a referrer of the
    482   // first page.
    483   EXPECT_TRUE(QueryURL(history, first_redirects[1]));
    484   EXPECT_EQ(1, query_url_row_.visit_count());
    485   ASSERT_EQ(1U, query_url_visits_.size());
    486   int64 second_visit = query_url_visits_[0].visit_id;
    487   EXPECT_EQ(PageTransition::SERVER_REDIRECT |
    488             PageTransition::CHAIN_END, query_url_visits_[0].transition);
    489   EXPECT_EQ(first_visit, query_url_visits_[0].referring_visit);
    490 
    491   // Check that the redirect finding function successfully reports it.
    492   saved_redirects_.clear();
    493   QueryRedirectsFrom(history, first_redirects[0]);
    494   ASSERT_EQ(1U, saved_redirects_.size());
    495   EXPECT_EQ(first_redirects[1], saved_redirects_[0]);
    496 
    497   // Now add a client redirect from that second visit to a third, client
    498   // redirects are tracked by the RenderView prior to updating history,
    499   // so we pass in a CLIENT_REDIRECT qualifier to mock that behavior.
    500   history::RedirectList second_redirects;
    501   second_redirects.push_back(first_redirects[1]);
    502   second_redirects.push_back(GURL("http://last.page/"));
    503   history->AddPage(second_redirects[1], MakeFakeHost(1), 1,
    504                    second_redirects[0],
    505                    static_cast<PageTransition::Type>(PageTransition::LINK |
    506                        PageTransition::CLIENT_REDIRECT),
    507                    second_redirects, history::SOURCE_BROWSED, true);
    508 
    509   // The last page (source of the client redirect) should NOT have an
    510   // additional visit added, because it was a client redirect (normally it
    511   // would). We should only have 1 left over from the first sequence.
    512   EXPECT_TRUE(QueryURL(history, second_redirects[0]));
    513   EXPECT_EQ(1, query_url_row_.visit_count());
    514 
    515   // The final page should be set as a client redirect from the previous visit.
    516   EXPECT_TRUE(QueryURL(history, second_redirects[1]));
    517   EXPECT_EQ(1, query_url_row_.visit_count());
    518   ASSERT_EQ(1U, query_url_visits_.size());
    519   EXPECT_EQ(PageTransition::CLIENT_REDIRECT |
    520             PageTransition::CHAIN_END, query_url_visits_[0].transition);
    521   EXPECT_EQ(second_visit, query_url_visits_[0].referring_visit);
    522 }
    523 
    524 TEST_F(HistoryTest, Typed) {
    525   scoped_refptr<HistoryService> history(new HistoryService);
    526   history_service_ = history;
    527   ASSERT_TRUE(history->Init(history_dir_, NULL));
    528 
    529   // Add the page once as typed.
    530   const GURL test_url("http://www.google.com/");
    531   history->AddPage(test_url, NULL, 0, GURL(), PageTransition::TYPED,
    532                    history::RedirectList(),
    533                    history::SOURCE_BROWSED, false);
    534   EXPECT_TRUE(QueryURL(history, test_url));
    535 
    536   // We should have the same typed & visit count.
    537   EXPECT_EQ(1, query_url_row_.visit_count());
    538   EXPECT_EQ(1, query_url_row_.typed_count());
    539 
    540   // Add the page again not typed.
    541   history->AddPage(test_url, NULL, 0, GURL(), PageTransition::LINK,
    542                    history::RedirectList(),
    543                    history::SOURCE_BROWSED, false);
    544   EXPECT_TRUE(QueryURL(history, test_url));
    545 
    546   // The second time should not have updated the typed count.
    547   EXPECT_EQ(2, query_url_row_.visit_count());
    548   EXPECT_EQ(1, query_url_row_.typed_count());
    549 
    550   // Add the page again as a generated URL.
    551   history->AddPage(test_url, NULL, 0, GURL(),
    552                    PageTransition::GENERATED, history::RedirectList(),
    553                    history::SOURCE_BROWSED, false);
    554   EXPECT_TRUE(QueryURL(history, test_url));
    555 
    556   // This should have worked like a link click.
    557   EXPECT_EQ(3, query_url_row_.visit_count());
    558   EXPECT_EQ(1, query_url_row_.typed_count());
    559 
    560   // Add the page again as a reload.
    561   history->AddPage(test_url, NULL, 0, GURL(),
    562                    PageTransition::RELOAD, history::RedirectList(),
    563                    history::SOURCE_BROWSED, false);
    564   EXPECT_TRUE(QueryURL(history, test_url));
    565 
    566   // This should not have incremented any visit counts.
    567   EXPECT_EQ(3, query_url_row_.visit_count());
    568   EXPECT_EQ(1, query_url_row_.typed_count());
    569 }
    570 
    571 TEST_F(HistoryTest, SetTitle) {
    572   scoped_refptr<HistoryService> history(new HistoryService);
    573   history_service_ = history;
    574   ASSERT_TRUE(history->Init(history_dir_, NULL));
    575 
    576   // Add a URL.
    577   const GURL existing_url("http://www.google.com/");
    578   history->AddPage(existing_url, history::SOURCE_BROWSED);
    579 
    580   // Set some title.
    581   const string16 existing_title = UTF8ToUTF16("Google");
    582   history->SetPageTitle(existing_url, existing_title);
    583 
    584   // Make sure the title got set.
    585   EXPECT_TRUE(QueryURL(history, existing_url));
    586   EXPECT_EQ(existing_title, query_url_row_.title());
    587 
    588   // set a title on a nonexistent page
    589   const GURL nonexistent_url("http://news.google.com/");
    590   const string16 nonexistent_title = UTF8ToUTF16("Google News");
    591   history->SetPageTitle(nonexistent_url, nonexistent_title);
    592 
    593   // Make sure nothing got written.
    594   EXPECT_FALSE(QueryURL(history, nonexistent_url));
    595   EXPECT_EQ(string16(), query_url_row_.title());
    596 
    597   // TODO(brettw) this should also test redirects, which get the title of the
    598   // destination page.
    599 }
    600 
    601 TEST_F(HistoryTest, Segments) {
    602   scoped_refptr<HistoryService> history(new HistoryService);
    603   history_service_ = history;
    604 
    605   ASSERT_TRUE(history->Init(history_dir_, NULL));
    606 
    607   static const void* scope = static_cast<void*>(this);
    608 
    609   // Add a URL.
    610   const GURL existing_url("http://www.google.com/");
    611   history->AddPage(existing_url, scope, 0, GURL(),
    612                    PageTransition::TYPED, history::RedirectList(),
    613                    history::SOURCE_BROWSED, false);
    614 
    615   // Make sure a segment was created.
    616   history->QuerySegmentUsageSince(
    617       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
    618       NewCallback(static_cast<HistoryTest*>(this),
    619                   &HistoryTest::OnSegmentUsageAvailable));
    620 
    621   // Wait for processing.
    622   MessageLoop::current()->Run();
    623 
    624   ASSERT_EQ(1U, page_usage_data_->size());
    625   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
    626   EXPECT_DOUBLE_EQ(3.0, page_usage_data_[0]->GetScore());
    627 
    628   // Add a URL which doesn't create a segment.
    629   const GURL link_url("http://yahoo.com/");
    630   history->AddPage(link_url, scope, 0, GURL(),
    631                    PageTransition::LINK, history::RedirectList(),
    632                    history::SOURCE_BROWSED, false);
    633 
    634   // Query again
    635   history->QuerySegmentUsageSince(
    636       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
    637       NewCallback(static_cast<HistoryTest*>(this),
    638                   &HistoryTest::OnSegmentUsageAvailable));
    639 
    640   // Wait for processing.
    641   MessageLoop::current()->Run();
    642 
    643   // Make sure we still have one segment.
    644   ASSERT_EQ(1U, page_usage_data_->size());
    645   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
    646 
    647   // Add a page linked from existing_url.
    648   history->AddPage(GURL("http://www.google.com/foo"), scope, 3, existing_url,
    649                    PageTransition::LINK, history::RedirectList(),
    650                    history::SOURCE_BROWSED, false);
    651 
    652   // Query again
    653   history->QuerySegmentUsageSince(
    654       &consumer_, Time::Now() - TimeDelta::FromDays(1), 10,
    655       NewCallback(static_cast<HistoryTest*>(this),
    656                   &HistoryTest::OnSegmentUsageAvailable));
    657 
    658   // Wait for processing.
    659   MessageLoop::current()->Run();
    660 
    661   // Make sure we still have one segment.
    662   ASSERT_EQ(1U, page_usage_data_->size());
    663   EXPECT_TRUE(page_usage_data_[0]->GetURL() == existing_url);
    664 
    665   // However, the score should have increased.
    666   EXPECT_GT(page_usage_data_[0]->GetScore(), 5.0);
    667 }
    668 
    669 TEST_F(HistoryTest, MostVisitedURLs) {
    670   scoped_refptr<HistoryService> history(new HistoryService);
    671   history_service_ = history;
    672   ASSERT_TRUE(history->Init(history_dir_, NULL));
    673 
    674   const GURL url0("http://www.google.com/url0/");
    675   const GURL url1("http://www.google.com/url1/");
    676   const GURL url2("http://www.google.com/url2/");
    677   const GURL url3("http://www.google.com/url3/");
    678   const GURL url4("http://www.google.com/url4/");
    679 
    680   static const void* scope = static_cast<void*>(this);
    681 
    682   // Add two pages.
    683   history->AddPage(url0, scope, 0, GURL(),
    684                    PageTransition::TYPED, history::RedirectList(),
    685                    history::SOURCE_BROWSED, false);
    686   history->AddPage(url1, scope, 0, GURL(),
    687                    PageTransition::TYPED, history::RedirectList(),
    688                    history::SOURCE_BROWSED, false);
    689   history->QueryMostVisitedURLs(20, 90, &consumer_,
    690                                 NewCallback(static_cast<HistoryTest*>(this),
    691                                     &HistoryTest::OnMostVisitedURLsAvailable));
    692   MessageLoop::current()->Run();
    693 
    694   EXPECT_EQ(2U, most_visited_urls_.size());
    695   EXPECT_EQ(url0, most_visited_urls_[0].url);
    696   EXPECT_EQ(url1, most_visited_urls_[1].url);
    697 
    698   // Add another page.
    699   history->AddPage(url2, scope, 0, GURL(),
    700                    PageTransition::TYPED, history::RedirectList(),
    701                    history::SOURCE_BROWSED, false);
    702   history->QueryMostVisitedURLs(20, 90, &consumer_,
    703                                 NewCallback(static_cast<HistoryTest*>(this),
    704                                     &HistoryTest::OnMostVisitedURLsAvailable));
    705   MessageLoop::current()->Run();
    706 
    707   EXPECT_EQ(3U, most_visited_urls_.size());
    708   EXPECT_EQ(url0, most_visited_urls_[0].url);
    709   EXPECT_EQ(url1, most_visited_urls_[1].url);
    710   EXPECT_EQ(url2, most_visited_urls_[2].url);
    711 
    712   // Revisit url2, making it the top URL.
    713   history->AddPage(url2, scope, 0, GURL(),
    714                    PageTransition::TYPED, history::RedirectList(),
    715                    history::SOURCE_BROWSED, false);
    716   history->QueryMostVisitedURLs(20, 90, &consumer_,
    717                                 NewCallback(static_cast<HistoryTest*>(this),
    718                                     &HistoryTest::OnMostVisitedURLsAvailable));
    719   MessageLoop::current()->Run();
    720 
    721   EXPECT_EQ(3U, most_visited_urls_.size());
    722   EXPECT_EQ(url2, most_visited_urls_[0].url);
    723   EXPECT_EQ(url0, most_visited_urls_[1].url);
    724   EXPECT_EQ(url1, most_visited_urls_[2].url);
    725 
    726   // Revisit url1, making it the top URL.
    727   history->AddPage(url1, scope, 0, GURL(),
    728                    PageTransition::TYPED, history::RedirectList(),
    729                    history::SOURCE_BROWSED, false);
    730   history->QueryMostVisitedURLs(20, 90, &consumer_,
    731                                 NewCallback(static_cast<HistoryTest*>(this),
    732                                     &HistoryTest::OnMostVisitedURLsAvailable));
    733   MessageLoop::current()->Run();
    734 
    735   EXPECT_EQ(3U, most_visited_urls_.size());
    736   EXPECT_EQ(url1, most_visited_urls_[0].url);
    737   EXPECT_EQ(url2, most_visited_urls_[1].url);
    738   EXPECT_EQ(url0, most_visited_urls_[2].url);
    739 
    740   // Redirects
    741   history::RedirectList redirects;
    742   redirects.push_back(url3);
    743   redirects.push_back(url4);
    744 
    745   // Visit url4 using redirects.
    746   history->AddPage(url4, scope, 0, GURL(),
    747                    PageTransition::TYPED, redirects,
    748                    history::SOURCE_BROWSED, false);
    749   history->QueryMostVisitedURLs(20, 90, &consumer_,
    750                                 NewCallback(static_cast<HistoryTest*>(this),
    751                                     &HistoryTest::OnMostVisitedURLsAvailable));
    752   MessageLoop::current()->Run();
    753 
    754   EXPECT_EQ(4U, most_visited_urls_.size());
    755   EXPECT_EQ(url1, most_visited_urls_[0].url);
    756   EXPECT_EQ(url2, most_visited_urls_[1].url);
    757   EXPECT_EQ(url0, most_visited_urls_[2].url);
    758   EXPECT_EQ(url3, most_visited_urls_[3].url);
    759   EXPECT_EQ(2U, most_visited_urls_[3].redirects.size());
    760 }
    761 
    762 // The version of the history database should be current in the "typical
    763 // history" example file or it will be imported on startup, throwing off timing
    764 // measurements.
    765 //
    766 // See test/data/profiles/typical_history/README.txt for instructions on
    767 // how to up the version.
    768 TEST(HistoryProfileTest, TypicalProfileVersion) {
    769   FilePath file;
    770   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &file));
    771   file = file.AppendASCII("profiles");
    772   file = file.AppendASCII("typical_history");
    773   file = file.AppendASCII("Default");
    774   file = file.AppendASCII("History");
    775 
    776   int cur_version = HistoryDatabase::GetCurrentVersion();
    777 
    778   sql::Connection db;
    779   ASSERT_TRUE(db.Open(file));
    780 
    781   {
    782     sql::Statement s(db.GetUniqueStatement(
    783         "SELECT value FROM meta WHERE key = 'version'"));
    784     EXPECT_TRUE(s.Step());
    785     int file_version = s.ColumnInt(0);
    786     EXPECT_EQ(cur_version, file_version);
    787   }
    788 }
    789 
    790 namespace {
    791 
    792 // A HistoryDBTask implementation. Each time RunOnDBThread is invoked
    793 // invoke_count is increment. When invoked kWantInvokeCount times, true is
    794 // returned from RunOnDBThread which should stop RunOnDBThread from being
    795 // invoked again. When DoneRunOnMainThread is invoked, done_invoked is set to
    796 // true.
    797 class HistoryDBTaskImpl : public HistoryDBTask {
    798  public:
    799   static const int kWantInvokeCount;
    800 
    801   HistoryDBTaskImpl() : invoke_count(0), done_invoked(false) {}
    802 
    803   virtual bool RunOnDBThread(HistoryBackend* backend, HistoryDatabase* db) {
    804     return (++invoke_count == kWantInvokeCount);
    805   }
    806 
    807   virtual void DoneRunOnMainThread() {
    808     done_invoked = true;
    809     MessageLoop::current()->Quit();
    810   }
    811 
    812   int invoke_count;
    813   bool done_invoked;
    814 
    815  private:
    816   virtual ~HistoryDBTaskImpl() {}
    817 
    818   DISALLOW_COPY_AND_ASSIGN(HistoryDBTaskImpl);
    819 };
    820 
    821 // static
    822 const int HistoryDBTaskImpl::kWantInvokeCount = 2;
    823 
    824 }  // namespace
    825 
    826 TEST_F(HistoryTest, HistoryDBTask) {
    827   CancelableRequestConsumerT<int, 0> request_consumer;
    828   HistoryService* history = new HistoryService();
    829   ASSERT_TRUE(history->Init(history_dir_, NULL));
    830   scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
    831   history_service_ = history;
    832   history->ScheduleDBTask(task.get(), &request_consumer);
    833   // Run the message loop. When HistoryDBTaskImpl::DoneRunOnMainThread runs,
    834   // it will stop the message loop. If the test hangs here, it means
    835   // DoneRunOnMainThread isn't being invoked correctly.
    836   MessageLoop::current()->Run();
    837   CleanupHistoryService();
    838   // WARNING: history has now been deleted.
    839   history = NULL;
    840   ASSERT_EQ(HistoryDBTaskImpl::kWantInvokeCount, task->invoke_count);
    841   ASSERT_TRUE(task->done_invoked);
    842 }
    843 
    844 TEST_F(HistoryTest, HistoryDBTaskCanceled) {
    845   CancelableRequestConsumerT<int, 0> request_consumer;
    846   HistoryService* history = new HistoryService();
    847   ASSERT_TRUE(history->Init(history_dir_, NULL));
    848   scoped_refptr<HistoryDBTaskImpl> task(new HistoryDBTaskImpl());
    849   history_service_ = history;
    850   history->ScheduleDBTask(task.get(), &request_consumer);
    851   request_consumer.CancelAllRequests();
    852   CleanupHistoryService();
    853   // WARNING: history has now been deleted.
    854   history = NULL;
    855   ASSERT_FALSE(task->done_invoked);
    856 }
    857 
    858 }  // namespace history
    859