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