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