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