Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / expire_history_backend_unittest.cc
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