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.
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"
38 using base::TimeDelta;
39 using base::TimeTicks;
40 using content::BrowserThread;
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");
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::".
54 // ExpireHistoryTest -----------------------------------------------------------
56 class ExpireHistoryTest : public testing::Test,
57 public BroadcastNotificationDelegate {
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()),
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);
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);
76 favicon_base::FaviconID GetFavicon(const GURL& page_url,
77 favicon_base::IconType icon_type);
79 // EXPECTs that each URL-specific history thing (basically, everything but
80 // favicons) is gone, the reason being either that it was |archived|, or
82 void EnsureURLInfoGone(const URLRow& row, bool archived);
84 // Returns whether a NOTIFICATION_HISTORY_URLS_MODIFIED was sent for |url|.
85 bool ModifiedNotificationSent(const GURL& url);
87 // Clears the list of notifications received.
88 void ClearLastNotifications() {
89 STLDeleteValues(¬ifications_);
92 void StarURL(const GURL& url) {
93 bookmark_model_->AddURL(
94 bookmark_model_->bookmark_bar_node(), 0, base::string16(), url);
97 static bool IsStringInFile(const base::FilePath& filename, const char* str);
99 // Returns the path the db files are created in.
100 const base::FilePath& path() const { return tmp_dir_.path(); }
102 // This must be destroyed last.
103 base::ScopedTempDir tmp_dir_;
105 test::TestBookmarkClient bookmark_client_;
106 scoped_ptr<BookmarkModel> bookmark_model_;
108 base::MessageLoopForUI message_loop_;
109 content::TestBrowserThread ui_thread_;
110 content::TestBrowserThread db_thread_;
112 ExpireHistoryBackend expirer_;
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_;
120 // Time at the beginning of the test, so everybody agrees what "now" is.
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
126 typedef std::vector< std::pair<int, HistoryDetails*> >
128 NotificationList notifications_;
131 virtual void SetUp() {
132 ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
134 base::FilePath history_name = path().Append(kHistoryFile);
135 main_db_.reset(new HistoryDatabase);
136 if (main_db_->Init(history_name) != sql::INIT_OK)
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();
144 base::FilePath thumb_name = path().Append(kThumbnailFile);
145 thumb_db_.reset(new ThumbnailDatabase);
146 if (thumb_db_->Init(thumb_name) != sql::INIT_OK)
149 expirer_.SetDatabases(main_db_.get(), archived_db_.get(), thumb_db_.get());
150 profile_.CreateTopSites();
151 profile_.BlockUntilTopSitesLoaded();
152 top_sites_ = profile_.GetTopSites();
155 virtual void TearDown() {
158 ClearLastNotifications();
160 expirer_.SetDatabases(NULL, NULL, NULL);
163 archived_db_.reset();
167 // BroadcastNotificationDelegate:
168 virtual void BroadcastNotifications(
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()));
175 virtual void NotifySyncURLsModified(URLRows* rows) OVERRIDE {}
176 virtual void NotifySyncURLsDeleted(bool all_history,
178 URLRows* rows) OVERRIDE {}
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.
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.
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]) {
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);
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);
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);
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);
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);
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());
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);
244 visit_row1.url_id = url_ids[0];
245 visit_row1.visit_time = visit_times[0];
246 main_db_->AddVisit(&visit_row1, SOURCE_BROWSED);
249 visit_row2.url_id = url_ids[1];
250 visit_row2.visit_time = visit_times[1];
251 main_db_->AddVisit(&visit_row2, SOURCE_BROWSED);
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);
260 visit_row4.url_id = url_ids[2];
261 visit_row4.visit_time = visit_times[3];
262 main_db_->AddVisit(&visit_row4, SOURCE_BROWSED);
265 void ExpireHistoryTest::AddExampleSourceData(const GURL& url, URLID* id) {
269 Time last_visit_time = Time::Now();
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);
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);
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);
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);
291 url_id, last_visit_time, 0, content::PAGE_TRANSITION_TYPED, 0);
292 main_db_->AddVisit(&visit_row4, SOURCE_FIREFOX_IMPORTED);
295 bool ExpireHistoryTest::HasFavicon(favicon_base::FaviconID favicon_id) {
296 if (!thumb_db_.get() || favicon_id == 0)
298 return thumb_db_->GetFaviconHeader(favicon_id, NULL, NULL);
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,
307 return icon_mappings[0].icon_id;
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.
316 if (!main_db_->GetURLRow(url_id, &info))
318 GURL url = info.url();
319 scoped_refptr<base::RefCountedMemory> data;
320 return top_sites_->GetPageThumbnail(url, false, &data);
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());
328 // Verify the URL no longer exists.
330 EXPECT_FALSE(main_db_->GetURLRow(row.id(), &temp_row));
332 // There should be no visits.
334 main_db_->GetVisitsForURL(row.id(), &visits);
335 EXPECT_EQ(0U, visits.size());
337 // Thumbnail should be gone.
338 // TODO(sky): fix this, see comment in HasThumbnail.
339 // EXPECT_FALSE(HasThumbnail(row.id()));
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;
357 } else if (notifications_[i].first ==
358 chrome::NOTIFICATION_HISTORY_URLS_MODIFIED) {
359 const history::URLRows& rows =
360 static_cast<URLsModifiedDetails*>(notifications_[i].second)->
363 std::find_if(rows.begin(), rows.end(),
364 history::URLRow::URLRowHasURL(row.url())) ==
368 EXPECT_TRUE(found_delete_notification);
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)->
377 if (std::find_if(rows.begin(), rows.end(),
378 history::URLRow::URLRowHasURL(url)) != rows.end())
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));
393 // The favicon should be deletable with no users.
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));
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));
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);
414 // Favicon should not be deletable.
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());
425 bool ExpireHistoryTest::IsStringInFile(const base::FilePath& filename,
427 std::string contents;
428 EXPECT_TRUE(base::ReadFileToString(filename, &contents));
429 return contents.find(str) != std::string::npos;
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) {
438 AddExampleData(url_ids, visit_times);
440 // Verify things are the way we expect with a URL row, favicon, thumbnail.
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]));
450 main_db_->GetVisitsForURL(url_ids[2], &visits);
451 ASSERT_EQ(1U, visits.size());
453 // Delete the URL and its dependencies.
454 expirer_.DeleteURL(last_row.url());
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));
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) {
467 AddExampleData(url_ids, visit_times);
469 // Verify things are the way we expect with a URL row, favicon, thumbnail.
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]));
479 main_db_->GetVisitsForURL(url_ids[1], &visits);
480 EXPECT_EQ(2U, visits.size());
482 // Delete the URL and its dependencies.
483 expirer_.DeleteURL(last_row.url());
485 // All the normal data except the favicon should be gone.
486 EnsureURLInfoGone(last_row, false);
487 EXPECT_TRUE(HasFavicon(favicon_id));
490 // DeleteURL should not delete starred urls.
491 TEST_F(ExpireHistoryTest, DontDeleteStarredURL) {
494 AddExampleData(url_ids, visit_times);
497 ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row));
499 // Star the last URL.
500 StarURL(url_row.url());
502 // Attempt to delete the url.
503 expirer_.DeleteURL(url_row.url());
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));
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));
516 main_db_->GetVisitsForURL(url_row.id(), &visits);
517 ASSERT_EQ(0U, visits.size());
519 // Should still have the thumbnail.
520 // TODO(sky): fix this, see comment in HasThumbnail.
521 // ASSERT_TRUE(HasThumbnail(url_row.id()));
523 // Unstar the URL and delete again.
524 bookmark_utils::RemoveAllBookmarks(bookmark_model_.get(), url);
525 ClearLastNotifications();
526 expirer_.DeleteURL(url);
528 // Now it should be completely deleted.
529 EnsureURLInfoGone(url_row, false);
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) {
537 AddExampleData(url_ids, visit_times);
539 // Verify things are the way we expect with URL rows, favicons,
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());
555 StarURL(rows[0].url());
557 // Delete the URLs and their dependencies.
558 expirer_.DeleteURLs(urls);
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]));
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) {
575 AddExampleData(url_ids, visit_times);
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));
582 main_db_->GetVisitsForURL(url_ids[2], &visits);
583 ASSERT_EQ(1U, visits.size());
585 // This should delete the last two visits.
586 std::set<GURL> restrict_urls;
587 expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
589 // Verify that the middle URL had its last visit deleted only.
591 main_db_->GetVisitsForURL(url_ids[1], &visits);
592 EXPECT_EQ(1U, visits.size());
594 // Verify that the middle URL visit time and visit counts were updated.
595 EXPECT_TRUE(ModifiedNotificationSent(url_row1.url()));
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());
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()));
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));
619 // Expires all URLs with times in a given set.
620 TEST_F(ExpireHistoryTest, FlushURLsForTimes) {
623 AddExampleData(url_ids, visit_times);
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));
630 main_db_->GetVisitsForURL(url_ids[2], &visits);
631 ASSERT_EQ(1U, visits.size());
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);
639 // Verify that the middle URL had its last visit deleted only.
641 main_db_->GetVisitsForURL(url_ids[1], &visits);
642 EXPECT_EQ(1U, visits.size());
644 // Verify that the middle URL visit time and visit counts were updated.
645 EXPECT_TRUE(ModifiedNotificationSent(url_row1.url()));
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());
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()));
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));
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) {
675 AddExampleData(url_ids, visit_times);
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));
682 main_db_->GetVisitsForURL(url_ids[2], &visits);
683 ASSERT_EQ(1U, visits.size());
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());
690 // Verify that the middle URL had its last visit deleted only.
692 main_db_->GetVisitsForURL(url_ids[1], &visits);
693 EXPECT_EQ(1U, visits.size());
695 // Verify that the middle URL visit time and visit counts were updated.
696 EXPECT_TRUE(ModifiedNotificationSent(url_row1.url()));
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());
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()));
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()));
720 // Expire a starred URL, it shouldn't get deleted
721 TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
724 AddExampleData(url_ids, visit_times);
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));
730 // Star the last two URLs.
731 StarURL(url_row1.url());
732 StarURL(url_row2.url());
734 // This should delete the last two visits.
735 std::set<GURL> restrict_urls;
736 expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
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));
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.
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());
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()));
770 TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) {
773 AddExampleData(url_ids, visit_times);
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));
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]);
784 // The first URL should be deleted, the second should not.
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));
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));
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));
802 // Make sure the archived database has an entry for the second URL.
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());
811 TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeStarred) {
814 AddExampleData(url_ids, visit_times);
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));
821 StarURL(url_row0.url());
822 StarURL(url_row1.url());
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]);
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.
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()));
836 main_db_->GetVisitsForURL(temp_row.id(), &visits);
837 EXPECT_EQ(0U, visits.size());
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());
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);
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()));
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) {
865 AddExampleData(url_ids, visit_times);
866 const ExpiringVisitsReader* reader = expirer_.GetAllVisitsReader();
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));
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));
876 // Deleting a time range with the max number of results should return true
878 EXPECT_TRUE(expirer_.ArchiveSomeOldHistory(visit_times[2], reader, 1));
881 TEST_F(ExpireHistoryTest, ExpiringVisitsReader) {
884 AddExampleData(url_ids, visit_times);
886 const ExpiringVisitsReader* all = expirer_.GetAllVisitsReader();
887 const ExpiringVisitsReader* auto_subframes =
888 expirer_.GetAutoSubframeVisitsReader();
891 Time now = Time::Now();
893 // Verify that the early expiration threshold, stored in the meta table is
895 EXPECT_TRUE(main_db_->GetEarlyExpirationThreshold() ==
896 Time::FromInternalValue(1L));
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());
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());
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());
911 // Tests how ArchiveSomeOldHistory treats source information.
912 TEST_F(ExpireHistoryTest, ArchiveSomeOldHistoryWithSource) {
913 const GURL url("www.testsource.com");
915 AddExampleSourceData(url, &url_id);
916 const ExpiringVisitsReader* reader = expirer_.GetAllVisitsReader();
918 // Archiving all the visits we added.
919 ASSERT_FALSE(expirer_.ArchiveSomeOldHistory(Time::Now(), reader, 10));
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());
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())
935 switch (iter->second) {
936 case history::SOURCE_EXTENSION:
939 case history::SOURCE_FIREFOX_IMPORTED:
942 case history::SOURCE_SYNCED:
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());
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.
958 // Maybe also refer to invalid favicons.
960 } // namespace history