Home | History | Annotate | Download | only in history
      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 <algorithm>
      6 #include <string>
      7 #include <utility>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/file_util.h"
     12 #include "base/files/file_path.h"
     13 #include "base/files/scoped_temp_dir.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/path_service.h"
     16 #include "base/stl_util.h"
     17 #include "base/strings/string16.h"
     18 #include "base/strings/utf_string_conversions.h"
     19 #include "chrome/browser/bookmarks/bookmark_model.h"
     20 #include "chrome/browser/bookmarks/bookmark_utils.h"
     21 #include "chrome/browser/chrome_notification_types.h"
     22 #include "chrome/browser/history/archived_database.h"
     23 #include "chrome/browser/history/expire_history_backend.h"
     24 #include "chrome/browser/history/history_database.h"
     25 #include "chrome/browser/history/history_notifications.h"
     26 #include "chrome/browser/history/thumbnail_database.h"
     27 #include "chrome/browser/history/top_sites.h"
     28 #include "chrome/common/thumbnail_score.h"
     29 #include "chrome/test/base/testing_profile.h"
     30 #include "chrome/tools/profiles/thumbnail-inl.h"
     31 #include "content/public/test/test_browser_thread.h"
     32 #include "testing/gtest/include/gtest/gtest.h"
     33 #include "third_party/skia/include/core/SkBitmap.h"
     34 #include "ui/gfx/codec/jpeg_codec.h"
     35 
     36 using base::Time;
     37 using base::TimeDelta;
     38 using base::TimeTicks;
     39 using content::BrowserThread;
     40 
     41 // Filename constants.
     42 static const base::FilePath::CharType kHistoryFile[] =
     43     FILE_PATH_LITERAL("History");
     44 static const base::FilePath::CharType kArchivedHistoryFile[] =
     45     FILE_PATH_LITERAL("Archived History");
     46 static const base::FilePath::CharType kThumbnailFile[] =
     47     FILE_PATH_LITERAL("Thumbnails");
     48 
     49 // The test must be in the history namespace for the gtest forward declarations
     50 // to work. It also eliminates a bunch of ugly "history::".
     51 namespace history {
     52 
     53 // ExpireHistoryTest -----------------------------------------------------------
     54 
     55 class ExpireHistoryTest : public testing::Test,
     56                           public BroadcastNotificationDelegate {
     57  public:
     58   ExpireHistoryTest()
     59       : bookmark_model_(NULL),
     60         ui_thread_(BrowserThread::UI, &message_loop_),
     61         db_thread_(BrowserThread::DB, &message_loop_),
     62         expirer_(this, &bookmark_model_),
     63         now_(Time::Now()) {
     64   }
     65 
     66  protected:
     67   // Called by individual tests when they want data populated.
     68   void AddExampleData(URLID url_ids[3], Time visit_times[4]);
     69   // Add visits with source information.
     70   void AddExampleSourceData(const GURL& url, URLID* id);
     71 
     72   // Returns true if the given favicon/thumanil has an entry in the DB.
     73   bool HasFavicon(chrome::FaviconID favicon_id);
     74   bool HasThumbnail(URLID url_id);
     75 
     76   chrome::FaviconID GetFavicon(const GURL& page_url,
     77                                chrome::IconType icon_type);
     78 
     79   // EXPECTs that each URL-specific history thing (basically, everything but
     80   // favicons) is gone.
     81   void EnsureURLInfoGone(const URLRow& row);
     82 
     83   // Clears the list of notifications received.
     84   void ClearLastNotifications() {
     85     STLDeleteValues(&notifications_);
     86   }
     87 
     88   void StarURL(const GURL& url) {
     89     bookmark_model_.AddURL(
     90         bookmark_model_.bookmark_bar_node(), 0, string16(), url);
     91   }
     92 
     93   static bool IsStringInFile(const base::FilePath& filename, const char* str);
     94 
     95   // Returns the path the db files are created in.
     96   const base::FilePath& path() const { return tmp_dir_.path(); }
     97 
     98   // This must be destroyed last.
     99   base::ScopedTempDir tmp_dir_;
    100 
    101   BookmarkModel bookmark_model_;
    102 
    103   base::MessageLoopForUI message_loop_;
    104   content::TestBrowserThread ui_thread_;
    105   content::TestBrowserThread db_thread_;
    106 
    107   ExpireHistoryBackend expirer_;
    108 
    109   scoped_ptr<HistoryDatabase> main_db_;
    110   scoped_ptr<ArchivedDatabase> archived_db_;
    111   scoped_ptr<ThumbnailDatabase> thumb_db_;
    112   TestingProfile profile_;
    113   scoped_refptr<TopSites> top_sites_;
    114 
    115   // Time at the beginning of the test, so everybody agrees what "now" is.
    116   const Time now_;
    117 
    118   // Notifications intended to be broadcast, we can check these values to make
    119   // sure that the deletor is doing the correct broadcasts. We own the details
    120   // pointers.
    121   typedef std::vector< std::pair<int, HistoryDetails*> >
    122       NotificationList;
    123   NotificationList notifications_;
    124 
    125  private:
    126   virtual void SetUp() {
    127     ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
    128 
    129     base::FilePath history_name = path().Append(kHistoryFile);
    130     main_db_.reset(new HistoryDatabase);
    131     if (main_db_->Init(history_name) != sql::INIT_OK)
    132       main_db_.reset();
    133 
    134     base::FilePath archived_name = path().Append(kArchivedHistoryFile);
    135     archived_db_.reset(new ArchivedDatabase);
    136     if (!archived_db_->Init(archived_name))
    137       archived_db_.reset();
    138 
    139     base::FilePath thumb_name = path().Append(kThumbnailFile);
    140     thumb_db_.reset(new ThumbnailDatabase);
    141     if (thumb_db_->Init(thumb_name, NULL, main_db_.get()) != sql::INIT_OK)
    142       thumb_db_.reset();
    143 
    144     expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get());
    145     profile_.CreateTopSites();
    146     profile_.BlockUntilTopSitesLoaded();
    147     top_sites_ = profile_.GetTopSites();
    148   }
    149 
    150   virtual void TearDown() {
    151     top_sites_ = NULL;
    152 
    153     ClearLastNotifications();
    154 
    155     expirer_.SetDatabases(NULL, NULL, NULL);
    156 
    157     main_db_.reset();
    158     archived_db_.reset();
    159     thumb_db_.reset();
    160   }
    161 
    162   // BroadcastNotificationDelegate implementation.
    163   virtual void BroadcastNotifications(
    164       int type,
    165       HistoryDetails* details_deleted) OVERRIDE {
    166     // This gets called when there are notifications to broadcast. Instead, we
    167     // store them so we can tell that the correct notifications were sent.
    168     notifications_.push_back(std::make_pair(type, details_deleted));
    169   }
    170   virtual void NotifySyncURLsDeleted(
    171       bool all_history,
    172       bool archived,
    173       URLRows* rows) OVERRIDE {}
    174 };
    175 
    176 // The example data consists of 4 visits. The middle two visits are to the
    177 // same URL, while the first and last are for unique ones. This allows a test
    178 // for the oldest or newest to include both a URL that should get totally
    179 // deleted (the one on the end) with one that should only get a visit deleted
    180 // (with the one in the middle) when it picks the proper threshold time.
    181 //
    182 // Each visit has indexed data, each URL has thumbnail. The first two URLs will
    183 // share the same avicon, while the last one will have a unique favicon. The
    184 // second visit for the middle URL is typed.
    185 //
    186 // The IDs of the added URLs, and the times of the four added visits will be
    187 // added to the given arrays.
    188 void ExpireHistoryTest::AddExampleData(URLID url_ids[3], Time visit_times[4]) {
    189   if (!main_db_.get())
    190     return;
    191 
    192   // Four times for each visit.
    193   visit_times[3] = Time::Now();
    194   visit_times[2] = visit_times[3] - TimeDelta::FromDays(1);
    195   visit_times[1] = visit_times[3] - TimeDelta::FromDays(2);
    196   visit_times[0] = visit_times[3] - TimeDelta::FromDays(3);
    197 
    198   // Two favicons. The first two URLs will share the same one, while the last
    199   // one will have a unique favicon.
    200   chrome::FaviconID favicon1 = thumb_db_->AddFavicon(
    201       GURL("http://favicon/url1"), chrome::FAVICON);
    202   chrome::FaviconID favicon2 = thumb_db_->AddFavicon(
    203       GURL("http://favicon/url2"), chrome::FAVICON);
    204 
    205   // Three URLs.
    206   URLRow url_row1(GURL("http://www.google.com/1"));
    207   url_row1.set_last_visit(visit_times[0]);
    208   url_row1.set_visit_count(1);
    209   url_ids[0] = main_db_->AddURL(url_row1);
    210   thumb_db_->AddIconMapping(url_row1.url(), favicon1);
    211 
    212   URLRow url_row2(GURL("http://www.google.com/2"));
    213   url_row2.set_last_visit(visit_times[2]);
    214   url_row2.set_visit_count(2);
    215   url_row2.set_typed_count(1);
    216   url_ids[1] = main_db_->AddURL(url_row2);
    217   thumb_db_->AddIconMapping(url_row2.url(), favicon1);
    218 
    219   URLRow url_row3(GURL("http://www.google.com/3"));
    220   url_row3.set_last_visit(visit_times[3]);
    221   url_row3.set_visit_count(1);
    222   url_ids[2] = main_db_->AddURL(url_row3);
    223   thumb_db_->AddIconMapping(url_row3.url(), favicon2);
    224 
    225   // Thumbnails for each URL. |thumbnail| takes ownership of decoded SkBitmap.
    226   scoped_ptr<SkBitmap> thumbnail_bitmap(
    227       gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
    228   gfx::Image thumbnail = gfx::Image::CreateFrom1xBitmap(*thumbnail_bitmap);
    229   ThumbnailScore score(0.25, true, true, Time::Now());
    230 
    231   Time time;
    232   GURL gurl;
    233   top_sites_->SetPageThumbnail(url_row1.url(), thumbnail, score);
    234   top_sites_->SetPageThumbnail(url_row2.url(), thumbnail, score);
    235   top_sites_->SetPageThumbnail(url_row3.url(), thumbnail, score);
    236 
    237   // Four visits.
    238   VisitRow visit_row1;
    239   visit_row1.url_id = url_ids[0];
    240   visit_row1.visit_time = visit_times[0];
    241   main_db_->AddVisit(&visit_row1, SOURCE_BROWSED);
    242 
    243   VisitRow visit_row2;
    244   visit_row2.url_id = url_ids[1];
    245   visit_row2.visit_time = visit_times[1];
    246   main_db_->AddVisit(&visit_row2, SOURCE_BROWSED);
    247 
    248   VisitRow visit_row3;
    249   visit_row3.url_id = url_ids[1];
    250   visit_row3.visit_time = visit_times[2];
    251   visit_row3.transition = content::PAGE_TRANSITION_TYPED;
    252   main_db_->AddVisit(&visit_row3, SOURCE_BROWSED);
    253 
    254   VisitRow visit_row4;
    255   visit_row4.url_id = url_ids[2];
    256   visit_row4.visit_time = visit_times[3];
    257   main_db_->AddVisit(&visit_row4, SOURCE_BROWSED);
    258 }
    259 
    260 void ExpireHistoryTest::AddExampleSourceData(const GURL& url, URLID* id) {
    261   if (!main_db_)
    262     return;
    263 
    264   Time last_visit_time = Time::Now();
    265   // Add one URL.
    266   URLRow url_row1(url);
    267   url_row1.set_last_visit(last_visit_time);
    268   url_row1.set_visit_count(4);
    269   URLID url_id = main_db_->AddURL(url_row1);
    270   *id = url_id;
    271 
    272   // Four times for each visit.
    273   VisitRow visit_row1(url_id, last_visit_time - TimeDelta::FromDays(4), 0,
    274                       content::PAGE_TRANSITION_TYPED, 0);
    275   main_db_->AddVisit(&visit_row1, SOURCE_SYNCED);
    276 
    277   VisitRow visit_row2(url_id, last_visit_time - TimeDelta::FromDays(3), 0,
    278                       content::PAGE_TRANSITION_TYPED, 0);
    279   main_db_->AddVisit(&visit_row2, SOURCE_BROWSED);
    280 
    281   VisitRow visit_row3(url_id, last_visit_time - TimeDelta::FromDays(2), 0,
    282                       content::PAGE_TRANSITION_TYPED, 0);
    283   main_db_->AddVisit(&visit_row3, SOURCE_EXTENSION);
    284 
    285   VisitRow visit_row4(
    286       url_id, last_visit_time, 0, content::PAGE_TRANSITION_TYPED, 0);
    287   main_db_->AddVisit(&visit_row4, SOURCE_FIREFOX_IMPORTED);
    288 }
    289 
    290 bool ExpireHistoryTest::HasFavicon(chrome::FaviconID favicon_id) {
    291   if (!thumb_db_.get() || favicon_id == 0)
    292     return false;
    293   return thumb_db_->GetFaviconHeader(favicon_id, NULL, NULL);
    294 }
    295 
    296 chrome::FaviconID ExpireHistoryTest::GetFavicon(const GURL& page_url,
    297                                                 chrome::IconType icon_type) {
    298   std::vector<IconMapping> icon_mappings;
    299   if (thumb_db_->GetIconMappingsForPageURL(page_url, icon_type,
    300                                            &icon_mappings)) {
    301     return icon_mappings[0].icon_id;
    302   }
    303   return 0;
    304 }
    305 
    306 bool ExpireHistoryTest::HasThumbnail(URLID url_id) {
    307   // TODO(sky): fix this. This test isn't really valid for TopSites. For
    308   // TopSites we should be checking URL always, not the id.
    309   URLRow info;
    310   if (!main_db_->GetURLRow(url_id, &info))
    311     return false;
    312   GURL url = info.url();
    313   scoped_refptr<base::RefCountedMemory> data;
    314   return top_sites_->GetPageThumbnail(url, &data);
    315 }
    316 
    317 void ExpireHistoryTest::EnsureURLInfoGone(const URLRow& row) {
    318   // Verify the URL no longer exists.
    319   URLRow temp_row;
    320   EXPECT_FALSE(main_db_->GetURLRow(row.id(), &temp_row));
    321 
    322   // There should be no visits.
    323   VisitVector visits;
    324   main_db_->GetVisitsForURL(row.id(), &visits);
    325   EXPECT_EQ(0U, visits.size());
    326 
    327   // Thumbnail should be gone.
    328   // TODO(sky): fix this, see comment in HasThumbnail.
    329   // EXPECT_FALSE(HasThumbnail(row.id()));
    330 
    331   bool found_delete_notification = false;
    332   for (size_t i = 0; i < notifications_.size(); i++) {
    333     if (notifications_[i].first == chrome::NOTIFICATION_HISTORY_URLS_DELETED) {
    334       URLsDeletedDetails* details = reinterpret_cast<URLsDeletedDetails*>(
    335           notifications_[i].second);
    336       EXPECT_FALSE(details->archived);
    337       const history::URLRows& rows(details->rows);
    338       if (std::find_if(rows.begin(), rows.end(),
    339           history::URLRow::URLRowHasURL(row.url())) != rows.end()) {
    340         found_delete_notification = true;
    341       }
    342     } else {
    343       EXPECT_NE(notifications_[i].first,
    344                 chrome::NOTIFICATION_HISTORY_URL_VISITED);
    345       EXPECT_NE(notifications_[i].first,
    346                 chrome::NOTIFICATION_HISTORY_URLS_MODIFIED);
    347     }
    348   }
    349   EXPECT_TRUE(found_delete_notification);
    350 }
    351 
    352 TEST_F(ExpireHistoryTest, DeleteFaviconsIfPossible) {
    353   // Add a favicon record.
    354   const GURL favicon_url("http://www.google.com/favicon.ico");
    355   chrome::FaviconID icon_id = thumb_db_->AddFavicon(
    356       favicon_url, chrome::FAVICON);
    357   EXPECT_TRUE(icon_id);
    358   EXPECT_TRUE(HasFavicon(icon_id));
    359 
    360   // The favicon should be deletable with no users.
    361   std::set<chrome::FaviconID> favicon_set;
    362   std::set<GURL> expired_favicons;
    363   favicon_set.insert(icon_id);
    364   expirer_.DeleteFaviconsIfPossible(favicon_set, &expired_favicons);
    365   EXPECT_FALSE(HasFavicon(icon_id));
    366   EXPECT_EQ(1U, expired_favicons.size());
    367   EXPECT_EQ(1U, expired_favicons.count(favicon_url));
    368 
    369   // Add back the favicon.
    370   icon_id = thumb_db_->AddFavicon(
    371       favicon_url, chrome::TOUCH_ICON);
    372   EXPECT_TRUE(icon_id);
    373   EXPECT_TRUE(HasFavicon(icon_id));
    374 
    375   // Add a page that references the favicon.
    376   URLRow row(GURL("http://www.google.com/2"));
    377   row.set_visit_count(1);
    378   EXPECT_TRUE(main_db_->AddURL(row));
    379   thumb_db_->AddIconMapping(row.url(), icon_id);
    380 
    381   // Favicon should not be deletable.
    382   favicon_set.clear();
    383   favicon_set.insert(icon_id);
    384   expired_favicons.clear();
    385   expirer_.DeleteFaviconsIfPossible(favicon_set, &expired_favicons);
    386   EXPECT_TRUE(HasFavicon(icon_id));
    387   EXPECT_TRUE(expired_favicons.empty());
    388 }
    389 
    390 // static
    391 bool ExpireHistoryTest::IsStringInFile(const base::FilePath& filename,
    392                                        const char* str) {
    393   std::string contents;
    394   EXPECT_TRUE(file_util::ReadFileToString(filename, &contents));
    395   return contents.find(str) != std::string::npos;
    396 }
    397 
    398 // Deletes a URL with a favicon that it is the last referencer of, so that it
    399 // should also get deleted.
    400 // Fails near end of month. http://crbug.com/43586
    401 TEST_F(ExpireHistoryTest, DISABLED_DeleteURLAndFavicon) {
    402   URLID url_ids[3];
    403   Time visit_times[4];
    404   AddExampleData(url_ids, visit_times);
    405 
    406   // Verify things are the way we expect with a URL row, favicon, thumbnail.
    407   URLRow last_row;
    408   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &last_row));
    409   chrome::FaviconID favicon_id = GetFavicon(last_row.url(), chrome::FAVICON);
    410   EXPECT_TRUE(HasFavicon(favicon_id));
    411   // TODO(sky): fix this, see comment in HasThumbnail.
    412   // EXPECT_TRUE(HasThumbnail(url_ids[2]));
    413 
    414   VisitVector visits;
    415   main_db_->GetVisitsForURL(url_ids[2], &visits);
    416   ASSERT_EQ(1U, visits.size());
    417 
    418   // Delete the URL and its dependencies.
    419   expirer_.DeleteURL(last_row.url());
    420 
    421   // All the normal data + the favicon should be gone.
    422   EnsureURLInfoGone(last_row);
    423   EXPECT_FALSE(GetFavicon(last_row.url(), chrome::FAVICON));
    424   EXPECT_FALSE(HasFavicon(favicon_id));
    425 }
    426 
    427 // Deletes a URL with a favicon that other URLs reference, so that the favicon
    428 // should not get deleted. This also tests deleting more than one visit.
    429 TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) {
    430   URLID url_ids[3];
    431   Time visit_times[4];
    432   AddExampleData(url_ids, visit_times);
    433 
    434   // Verify things are the way we expect with a URL row, favicon, thumbnail.
    435   URLRow last_row;
    436   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &last_row));
    437   chrome::FaviconID favicon_id = GetFavicon(last_row.url(), chrome::FAVICON);
    438   EXPECT_TRUE(HasFavicon(favicon_id));
    439   // TODO(sky): fix this, see comment in HasThumbnail.
    440   // EXPECT_TRUE(HasThumbnail(url_ids[1]));
    441 
    442   VisitVector visits;
    443   main_db_->GetVisitsForURL(url_ids[1], &visits);
    444   EXPECT_EQ(2U, visits.size());
    445 
    446   // Delete the URL and its dependencies.
    447   expirer_.DeleteURL(last_row.url());
    448 
    449   // All the normal data except the favicon should be gone.
    450   EnsureURLInfoGone(last_row);
    451   EXPECT_TRUE(HasFavicon(favicon_id));
    452 }
    453 
    454 // DeleteURL should not delete starred urls.
    455 TEST_F(ExpireHistoryTest, DontDeleteStarredURL) {
    456   URLID url_ids[3];
    457   Time visit_times[4];
    458   AddExampleData(url_ids, visit_times);
    459 
    460   URLRow url_row;
    461   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row));
    462 
    463   // Star the last URL.
    464   StarURL(url_row.url());
    465 
    466   // Attempt to delete the url.
    467   expirer_.DeleteURL(url_row.url());
    468 
    469   // Because the url is starred, it shouldn't be deleted.
    470   GURL url = url_row.url();
    471   ASSERT_TRUE(main_db_->GetRowForURL(url, &url_row));
    472 
    473   // And the favicon should exist.
    474   chrome::FaviconID favicon_id = GetFavicon(url_row.url(), chrome::FAVICON);
    475   EXPECT_TRUE(HasFavicon(favicon_id));
    476 
    477   // And no visits.
    478   VisitVector visits;
    479   main_db_->GetVisitsForURL(url_row.id(), &visits);
    480   ASSERT_EQ(0U, visits.size());
    481 
    482   // Should still have the thumbnail.
    483   // TODO(sky): fix this, see comment in HasThumbnail.
    484   // ASSERT_TRUE(HasThumbnail(url_row.id()));
    485 
    486   // Unstar the URL and delete again.
    487   bookmark_utils::RemoveAllBookmarks(&bookmark_model_, url);
    488   expirer_.DeleteURL(url);
    489 
    490   // Now it should be completely deleted.
    491   EnsureURLInfoGone(url_row);
    492 }
    493 
    494 // Deletes multiple URLs at once.  The favicon for the third one but
    495 // not the first two should be deleted.
    496 TEST_F(ExpireHistoryTest, DeleteURLs) {
    497   URLID url_ids[3];
    498   Time visit_times[4];
    499   AddExampleData(url_ids, visit_times);
    500 
    501   // Verify things are the way we expect with URL rows, favicons,
    502   // thumbnails.
    503   URLRow rows[3];
    504   chrome::FaviconID favicon_ids[3];
    505   std::vector<GURL> urls;
    506   // Push back a bogus URL (which shouldn't change anything).
    507   urls.push_back(GURL());
    508   for (size_t i = 0; i < arraysize(rows); ++i) {
    509     ASSERT_TRUE(main_db_->GetURLRow(url_ids[i], &rows[i]));
    510     favicon_ids[i] = GetFavicon(rows[i].url(), chrome::FAVICON);
    511     EXPECT_TRUE(HasFavicon(favicon_ids[i]));
    512     // TODO(sky): fix this, see comment in HasThumbnail.
    513     // EXPECT_TRUE(HasThumbnail(url_ids[i]));
    514     urls.push_back(rows[i].url());
    515   }
    516 
    517   StarURL(rows[0].url());
    518 
    519   // Delete the URLs and their dependencies.
    520   expirer_.DeleteURLs(urls);
    521 
    522   // First one should still be around (since it was starred).
    523   ASSERT_TRUE(main_db_->GetRowForURL(rows[0].url(), &rows[0]));
    524   EnsureURLInfoGone(rows[1]);
    525   EnsureURLInfoGone(rows[2]);
    526   EXPECT_TRUE(HasFavicon(favicon_ids[0]));
    527   EXPECT_TRUE(HasFavicon(favicon_ids[1]));
    528   EXPECT_FALSE(HasFavicon(favicon_ids[2]));
    529 }
    530 
    531 // Expires all URLs more recent than a given time, with no starred items.
    532 // Our time threshold is such that one URL should be updated (we delete one of
    533 // the two visits) and one is deleted.
    534 TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) {
    535   URLID url_ids[3];
    536   Time visit_times[4];
    537   AddExampleData(url_ids, visit_times);
    538 
    539   URLRow url_row1, url_row2;
    540   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1));
    541   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2));
    542 
    543   VisitVector visits;
    544   main_db_->GetVisitsForURL(url_ids[2], &visits);
    545   ASSERT_EQ(1U, visits.size());
    546 
    547   // This should delete the last two visits.
    548   std::set<GURL> restrict_urls;
    549   expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
    550 
    551   // Verify that the middle URL had its last visit deleted only.
    552   visits.clear();
    553   main_db_->GetVisitsForURL(url_ids[1], &visits);
    554   EXPECT_EQ(1U, visits.size());
    555 
    556   // Verify that the middle URL visit time and visit counts were updated.
    557   URLRow temp_row;
    558   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
    559   EXPECT_TRUE(visit_times[2] == url_row1.last_visit());  // Previous value.
    560   EXPECT_TRUE(visit_times[1] == temp_row.last_visit());  // New value.
    561   EXPECT_EQ(2, url_row1.visit_count());
    562   EXPECT_EQ(1, temp_row.visit_count());
    563   EXPECT_EQ(1, url_row1.typed_count());
    564   EXPECT_EQ(0, temp_row.typed_count());
    565 
    566   // Verify that the middle URL's favicon and thumbnail is still there.
    567   chrome::FaviconID favicon_id = GetFavicon(url_row1.url(), chrome::FAVICON);
    568   EXPECT_TRUE(HasFavicon(favicon_id));
    569   // TODO(sky): fix this, see comment in HasThumbnail.
    570   // EXPECT_TRUE(HasThumbnail(url_row1.id()));
    571 
    572   // Verify that the last URL was deleted.
    573   chrome::FaviconID favicon_id2 = GetFavicon(url_row2.url(), chrome::FAVICON);
    574   EnsureURLInfoGone(url_row2);
    575   EXPECT_FALSE(HasFavicon(favicon_id2));
    576 }
    577 
    578 // Expires all URLs with times in a given set.
    579 TEST_F(ExpireHistoryTest, FlushURLsForTimes) {
    580   URLID url_ids[3];
    581   Time visit_times[4];
    582   AddExampleData(url_ids, visit_times);
    583 
    584   URLRow url_row1, url_row2;
    585   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1));
    586   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2));
    587 
    588   VisitVector visits;
    589   main_db_->GetVisitsForURL(url_ids[2], &visits);
    590   ASSERT_EQ(1U, visits.size());
    591 
    592   // This should delete the last two visits.
    593   std::vector<base::Time> times;
    594   times.push_back(visit_times[3]);
    595   times.push_back(visit_times[2]);
    596   expirer_.ExpireHistoryForTimes(times);
    597 
    598   // Verify that the middle URL had its last visit deleted only.
    599   visits.clear();
    600   main_db_->GetVisitsForURL(url_ids[1], &visits);
    601   EXPECT_EQ(1U, visits.size());
    602 
    603   // Verify that the middle URL visit time and visit counts were updated.
    604   URLRow temp_row;
    605   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
    606   EXPECT_TRUE(visit_times[2] == url_row1.last_visit());  // Previous value.
    607   EXPECT_TRUE(visit_times[1] == temp_row.last_visit());  // New value.
    608   EXPECT_EQ(2, url_row1.visit_count());
    609   EXPECT_EQ(1, temp_row.visit_count());
    610   EXPECT_EQ(1, url_row1.typed_count());
    611   EXPECT_EQ(0, temp_row.typed_count());
    612 
    613   // Verify that the middle URL's favicon and thumbnail is still there.
    614   chrome::FaviconID favicon_id = GetFavicon(url_row1.url(), chrome::FAVICON);
    615   EXPECT_TRUE(HasFavicon(favicon_id));
    616   // TODO(sky): fix this, see comment in HasThumbnail.
    617   // EXPECT_TRUE(HasThumbnail(url_row1.id()));
    618 
    619   // Verify that the last URL was deleted.
    620   chrome::FaviconID favicon_id2 = GetFavicon(url_row2.url(), chrome::FAVICON);
    621   EnsureURLInfoGone(url_row2);
    622   EXPECT_FALSE(HasFavicon(favicon_id2));
    623 }
    624 
    625 // Expires only a specific URLs more recent than a given time, with no starred
    626 // items.  Our time threshold is such that the URL should be updated (we delete
    627 // one of the two visits).
    628 TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarredRestricted) {
    629   URLID url_ids[3];
    630   Time visit_times[4];
    631   AddExampleData(url_ids, visit_times);
    632 
    633   URLRow url_row1, url_row2;
    634   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1));
    635   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2));
    636 
    637   VisitVector visits;
    638   main_db_->GetVisitsForURL(url_ids[2], &visits);
    639   ASSERT_EQ(1U, visits.size());
    640 
    641   // This should delete the last two visits.
    642   std::set<GURL> restrict_urls;
    643   restrict_urls.insert(url_row1.url());
    644   expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
    645 
    646   // Verify that the middle URL had its last visit deleted only.
    647   visits.clear();
    648   main_db_->GetVisitsForURL(url_ids[1], &visits);
    649   EXPECT_EQ(1U, visits.size());
    650 
    651   // Verify that the middle URL visit time and visit counts were updated.
    652   URLRow temp_row;
    653   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
    654   EXPECT_TRUE(visit_times[2] == url_row1.last_visit());  // Previous value.
    655   EXPECT_TRUE(visit_times[1] == temp_row.last_visit());  // New value.
    656   EXPECT_EQ(2, url_row1.visit_count());
    657   EXPECT_EQ(1, temp_row.visit_count());
    658   EXPECT_EQ(1, url_row1.typed_count());
    659   EXPECT_EQ(0, temp_row.typed_count());
    660 
    661   // Verify that the middle URL's favicon and thumbnail is still there.
    662   chrome::FaviconID favicon_id = GetFavicon(url_row1.url(), chrome::FAVICON);
    663   EXPECT_TRUE(HasFavicon(favicon_id));
    664   // TODO(sky): fix this, see comment in HasThumbnail.
    665   // EXPECT_TRUE(HasThumbnail(url_row1.id()));
    666 
    667   // Verify that the last URL was not touched.
    668   EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row));
    669   EXPECT_TRUE(HasFavicon(favicon_id));
    670   // TODO(sky): fix this, see comment in HasThumbnail.
    671   // EXPECT_TRUE(HasThumbnail(url_row2.id()));
    672 }
    673 
    674 // Expire a starred URL, it shouldn't get deleted
    675 TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
    676   URLID url_ids[3];
    677   Time visit_times[4];
    678   AddExampleData(url_ids, visit_times);
    679 
    680   URLRow url_row1, url_row2;
    681   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1));
    682   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2));
    683 
    684   // Star the last two URLs.
    685   StarURL(url_row1.url());
    686   StarURL(url_row2.url());
    687 
    688   // This should delete the last two visits.
    689   std::set<GURL> restrict_urls;
    690   expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
    691 
    692   // The URL rows should still exist.
    693   URLRow new_url_row1, new_url_row2;
    694   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &new_url_row1));
    695   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &new_url_row2));
    696 
    697   // The visit times should be updated.
    698   EXPECT_TRUE(new_url_row1.last_visit() == visit_times[1]);
    699   EXPECT_TRUE(new_url_row2.last_visit().is_null());  // No last visit time.
    700 
    701   // Visit/typed count should not be updated for bookmarks.
    702   EXPECT_EQ(0, new_url_row1.typed_count());
    703   EXPECT_EQ(1, new_url_row1.visit_count());
    704   EXPECT_EQ(0, new_url_row2.typed_count());
    705   EXPECT_EQ(0, new_url_row2.visit_count());
    706 
    707   // Thumbnails and favicons should still exist. Note that we keep thumbnails
    708   // that may have been updated since the time threshold. Since the URL still
    709   // exists in history, this should not be a privacy problem, we only update
    710   // the visit counts in this case for consistency anyway.
    711   chrome::FaviconID favicon_id = GetFavicon(url_row1.url(), chrome::FAVICON);
    712   EXPECT_TRUE(HasFavicon(favicon_id));
    713   // TODO(sky): fix this, see comment in HasThumbnail.
    714   // EXPECT_TRUE(HasThumbnail(new_url_row1.id()));
    715   favicon_id = GetFavicon(url_row1.url(), chrome::FAVICON);
    716   EXPECT_TRUE(HasFavicon(favicon_id));
    717   // TODO(sky): fix this, see comment in HasThumbnail.
    718   // EXPECT_TRUE(HasThumbnail(new_url_row2.id()));
    719 }
    720 
    721 TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) {
    722   URLID url_ids[3];
    723   Time visit_times[4];
    724   AddExampleData(url_ids, visit_times);
    725 
    726   URLRow url_row1, url_row2;
    727   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1));
    728   ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2));
    729 
    730   // Archive the oldest two visits. This will actually result in deleting them
    731   // since their transition types are empty (not important).
    732   expirer_.ArchiveHistoryBefore(visit_times[1]);
    733 
    734   // The first URL should be deleted, the second should not be affected.
    735   URLRow temp_row;
    736   EXPECT_FALSE(main_db_->GetURLRow(url_ids[0], &temp_row));
    737   EXPECT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
    738   EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row));
    739 
    740   // Make sure the archived database has nothing in it.
    741   EXPECT_FALSE(archived_db_->GetRowForURL(url_row1.url(), NULL));
    742   EXPECT_FALSE(archived_db_->GetRowForURL(url_row2.url(), NULL));
    743 
    744   // Now archive one more visit so that the middle URL should be removed. This
    745   // one will actually be archived instead of deleted.
    746   expirer_.ArchiveHistoryBefore(visit_times[2]);
    747   EXPECT_FALSE(main_db_->GetURLRow(url_ids[1], &temp_row));
    748   EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row));
    749 
    750   // Make sure the archived database has an entry for the second URL.
    751   URLRow archived_row;
    752   // Note that the ID is different in the archived DB, so look up by URL.
    753   EXPECT_TRUE(archived_db_->GetRowForURL(url_row1.url(), &archived_row));
    754   VisitVector archived_visits;
    755   archived_db_->GetVisitsForURL(archived_row.id(), &archived_visits);
    756   EXPECT_EQ(1U, archived_visits.size());
    757 }
    758 
    759 TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeStarred) {
    760   URLID url_ids[3];
    761   Time visit_times[4];
    762   AddExampleData(url_ids, visit_times);
    763 
    764   URLRow url_row0, url_row1;
    765   ASSERT_TRUE(main_db_->GetURLRow(url_ids[0], &url_row0));
    766   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1));
    767 
    768   // Star the URLs.
    769   StarURL(url_row0.url());
    770   StarURL(url_row1.url());
    771 
    772   // Now archive the first three visits (first two URLs). The first two visits
    773   // should be, the third deleted, but the URL records should not.
    774   expirer_.ArchiveHistoryBefore(visit_times[2]);
    775 
    776   // The first URL should have its visit deleted, but it should still be present
    777   // in the main DB and not in the archived one since it is starred.
    778   URLRow temp_row;
    779   ASSERT_TRUE(main_db_->GetURLRow(url_ids[0], &temp_row));
    780   // Note that the ID is different in the archived DB, so look up by URL.
    781   EXPECT_FALSE(archived_db_->GetRowForURL(temp_row.url(), NULL));
    782   VisitVector visits;
    783   main_db_->GetVisitsForURL(temp_row.id(), &visits);
    784   EXPECT_EQ(0U, visits.size());
    785 
    786   // The second URL should have its first visit deleted and its second visit
    787   // archived. It should be present in both the main DB (because it's starred)
    788   // and the archived DB (for the archived visit).
    789   ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
    790   main_db_->GetVisitsForURL(temp_row.id(), &visits);
    791   EXPECT_EQ(0U, visits.size());
    792 
    793   // Note that the ID is different in the archived DB, so look up by URL.
    794   ASSERT_TRUE(archived_db_->GetRowForURL(temp_row.url(), &temp_row));
    795   archived_db_->GetVisitsForURL(temp_row.id(), &visits);
    796   ASSERT_EQ(1U, visits.size());
    797   EXPECT_TRUE(visit_times[2] == visits[0].visit_time);
    798 
    799   // The third URL should be unchanged.
    800   EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row));
    801   EXPECT_FALSE(archived_db_->GetRowForURL(temp_row.url(), NULL));
    802 }
    803 
    804 // Tests the return values from ArchiveSomeOldHistory. The rest of the
    805 // functionality of this function is tested by the ArchiveHistoryBefore*
    806 // tests which use this function internally.
    807 TEST_F(ExpireHistoryTest, ArchiveSomeOldHistory) {
    808   URLID url_ids[3];
    809   Time visit_times[4];
    810   AddExampleData(url_ids, visit_times);
    811   const ExpiringVisitsReader* reader = expirer_.GetAllVisitsReader();
    812 
    813   // Deleting a time range with no URLs should return false (nothing found).
    814   EXPECT_FALSE(expirer_.ArchiveSomeOldHistory(
    815       visit_times[0] - TimeDelta::FromDays(100), reader, 1));
    816 
    817   // Deleting a time range with not up the the max results should also return
    818   // false (there will only be one visit deleted in this range).
    819   EXPECT_FALSE(expirer_.ArchiveSomeOldHistory(visit_times[0], reader, 2));
    820 
    821   // Deleting a time range with the max number of results should return true
    822   // (max deleted).
    823   EXPECT_TRUE(expirer_.ArchiveSomeOldHistory(visit_times[2], reader, 1));
    824 }
    825 
    826 TEST_F(ExpireHistoryTest, ExpiringVisitsReader) {
    827   URLID url_ids[3];
    828   Time visit_times[4];
    829   AddExampleData(url_ids, visit_times);
    830 
    831   const ExpiringVisitsReader* all = expirer_.GetAllVisitsReader();
    832   const ExpiringVisitsReader* auto_subframes =
    833       expirer_.GetAutoSubframeVisitsReader();
    834 
    835   VisitVector visits;
    836   Time now = Time::Now();
    837 
    838   // Verify that the early expiration threshold, stored in the meta table is
    839   // initialized.
    840   EXPECT_TRUE(main_db_->GetEarlyExpirationThreshold() ==
    841       Time::FromInternalValue(1L));
    842 
    843   // First, attempt reading AUTO_SUBFRAME visits. We should get none.
    844   EXPECT_FALSE(auto_subframes->Read(now, main_db_.get(), &visits, 1));
    845   EXPECT_EQ(0U, visits.size());
    846 
    847   // Verify that the early expiration threshold was updated, since there are no
    848   // AUTO_SUBFRAME visits in the given time range.
    849   EXPECT_TRUE(now <= main_db_->GetEarlyExpirationThreshold());
    850 
    851   // Now, read all visits and verify that there's at least one.
    852   EXPECT_TRUE(all->Read(now, main_db_.get(), &visits, 1));
    853   EXPECT_EQ(1U, visits.size());
    854 }
    855 
    856 // Tests how ArchiveSomeOldHistory treats source information.
    857 TEST_F(ExpireHistoryTest, ArchiveSomeOldHistoryWithSource) {
    858   const GURL url("www.testsource.com");
    859   URLID url_id;
    860   AddExampleSourceData(url, &url_id);
    861   const ExpiringVisitsReader* reader = expirer_.GetAllVisitsReader();
    862 
    863   // Archiving all the visits we added.
    864   ASSERT_FALSE(expirer_.ArchiveSomeOldHistory(Time::Now(), reader, 10));
    865 
    866   URLRow archived_row;
    867   ASSERT_TRUE(archived_db_->GetRowForURL(url, &archived_row));
    868   VisitVector archived_visits;
    869   archived_db_->GetVisitsForURL(archived_row.id(), &archived_visits);
    870   ASSERT_EQ(4U, archived_visits.size());
    871   VisitSourceMap sources;
    872   archived_db_->GetVisitsSource(archived_visits, &sources);
    873   ASSERT_EQ(3U, sources.size());
    874   int result = 0;
    875   VisitSourceMap::iterator iter;
    876   for (int i = 0; i < 4; i++) {
    877     iter = sources.find(archived_visits[i].visit_id);
    878     if (iter == sources.end())
    879       continue;
    880     switch (iter->second) {
    881       case history::SOURCE_EXTENSION:
    882         result |= 0x1;
    883         break;
    884       case history::SOURCE_FIREFOX_IMPORTED:
    885         result |= 0x2;
    886         break;
    887       case history::SOURCE_SYNCED:
    888         result |= 0x4;
    889       default:
    890         break;
    891     }
    892   }
    893   EXPECT_EQ(0x7, result);
    894   main_db_->GetVisitsSource(archived_visits, &sources);
    895   EXPECT_EQ(0U, sources.size());
    896   main_db_->GetVisitsForURL(url_id, &archived_visits);
    897   EXPECT_EQ(0U, archived_visits.size());
    898 }
    899 
    900 // TODO(brettw) add some visits with no URL to make sure everything is updated
    901 // properly. Have the visits also refer to nonexistent FTS rows.
    902 //
    903 // Maybe also refer to invalid favicons.
    904 
    905 }  // namespace history
    906