• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ExpireHistoryTest()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.
ClearLastNotifications()85   void ClearLastNotifications() {
86     STLDeleteValues(&notifications_);
87   }
88 
StarURL(const GURL & url)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.
path() const94   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:
SetUp()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 
TearDown()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:
BroadcastNotifications(int type,scoped_ptr<HistoryDetails> details)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   }
NotifySyncURLsModified(URLRows * rows)161   virtual void NotifySyncURLsModified(URLRows* rows) OVERRIDE {}
NotifySyncURLsDeleted(bool all_history,bool expired,URLRows * rows)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.
AddExampleData(URLID url_ids[3],Time visit_times[4])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 
AddExampleSourceData(const GURL & url,URLID * id)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 
HasFavicon(favicon_base::FaviconID favicon_id)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 
GetFavicon(const GURL & page_url,favicon_base::IconType icon_type)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 
HasThumbnail(URLID url_id)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 
EnsureURLInfoGone(const URLRow & row,bool expired)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 
ModifiedNotificationSent(const GURL & url)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 
TEST_F(ExpireHistoryTest,DeleteFaviconsIfPossible)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
IsStringInFile(const base::FilePath & filename,const char * str)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
TEST_F(ExpireHistoryTest,DISABLED_DeleteURLAndFavicon)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.
TEST_F(ExpireHistoryTest,DeleteURLWithoutFavicon)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.
TEST_F(ExpireHistoryTest,DontDeleteStarredURL)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.
TEST_F(ExpireHistoryTest,DeleteURLs)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.
TEST_F(ExpireHistoryTest,FlushRecentURLsUnstarred)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.
TEST_F(ExpireHistoryTest,FlushURLsForTimes)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).
TEST_F(ExpireHistoryTest,FlushRecentURLsUnstarredRestricted)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
TEST_F(ExpireHistoryTest,FlushRecentURLsStarred)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 
TEST_F(ExpireHistoryTest,ExpireHistoryBeforeUnstarred)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 
TEST_F(ExpireHistoryTest,ExpireHistoryBeforeStarred)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.
TEST_F(ExpireHistoryTest,ExpireSomeOldHistory)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 
TEST_F(ExpireHistoryTest,ExpiringVisitsReader)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