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/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"
35 using base::TimeDelta;
36 using base::TimeTicks;
37 using content::BrowserThread;
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");
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::".
49 // ExpireHistoryTest -----------------------------------------------------------
51 class ExpireHistoryTest : public testing::Test,
52 public BroadcastNotificationDelegate {
55 : ui_thread_(BrowserThread::UI, &message_loop_),
56 db_thread_(BrowserThread::DB, &message_loop_),
57 expirer_(this, &history_client_),
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);
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);
70 favicon_base::FaviconID GetFavicon(const GURL& page_url,
71 favicon_base::IconType icon_type);
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);
78 // Returns whether a NOTIFICATION_HISTORY_URLS_MODIFIED was sent for |url|.
79 bool ModifiedNotificationSent(const GURL& url);
81 // Clears the list of notifications received.
82 void ClearLastNotifications() {
83 STLDeleteValues(¬ifications_);
86 void StarURL(const GURL& url) { history_client_.AddBookmark(url); }
88 static bool IsStringInFile(const base::FilePath& filename, const char* str);
90 // Returns the path the db files are created in.
91 const base::FilePath& path() const { return tmp_dir_.path(); }
93 // This must be destroyed last.
94 base::ScopedTempDir tmp_dir_;
96 HistoryClientFakeBookmarks history_client_;
98 base::MessageLoopForUI message_loop_;
99 content::TestBrowserThread ui_thread_;
100 content::TestBrowserThread db_thread_;
102 ExpireHistoryBackend expirer_;
104 scoped_ptr<HistoryDatabase> main_db_;
105 scoped_ptr<ThumbnailDatabase> thumb_db_;
106 TestingProfile profile_;
107 scoped_refptr<TopSites> top_sites_;
109 // Time at the beginning of the test, so everybody agrees what "now" is.
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
115 typedef std::vector< std::pair<int, HistoryDetails*> >
117 NotificationList notifications_;
120 virtual void SetUp() {
121 ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
123 base::FilePath history_name = path().Append(kHistoryFile);
124 main_db_.reset(new HistoryDatabase);
125 if (main_db_->Init(history_name) != sql::INIT_OK)
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)
133 expirer_.SetDatabases(main_db_.get(), thumb_db_.get());
134 profile_.CreateTopSites();
135 profile_.BlockUntilTopSitesLoaded();
136 top_sites_ = profile_.GetTopSites();
139 virtual void TearDown() {
142 ClearLastNotifications();
144 expirer_.SetDatabases(NULL, NULL);
150 // BroadcastNotificationDelegate:
151 virtual void BroadcastNotifications(
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()));
158 virtual void NotifySyncURLsModified(URLRows* rows) OVERRIDE {}
159 virtual void NotifySyncURLsDeleted(bool all_history,
161 URLRows* rows) OVERRIDE {}
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.
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.
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]) {
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);
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);
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);
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);
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);
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());
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);
227 visit_row1.url_id = url_ids[0];
228 visit_row1.visit_time = visit_times[0];
229 main_db_->AddVisit(&visit_row1, SOURCE_BROWSED);
232 visit_row2.url_id = url_ids[1];
233 visit_row2.visit_time = visit_times[1];
234 main_db_->AddVisit(&visit_row2, SOURCE_BROWSED);
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);
243 visit_row4.url_id = url_ids[2];
244 visit_row4.visit_time = visit_times[3];
245 main_db_->AddVisit(&visit_row4, SOURCE_BROWSED);
248 void ExpireHistoryTest::AddExampleSourceData(const GURL& url, URLID* id) {
252 Time last_visit_time = Time::Now();
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);
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);
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);
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);
274 url_id, last_visit_time, 0, ui::PAGE_TRANSITION_TYPED, 0);
275 main_db_->AddVisit(&visit_row4, SOURCE_FIREFOX_IMPORTED);
278 bool ExpireHistoryTest::HasFavicon(favicon_base::FaviconID favicon_id) {
279 if (!thumb_db_.get() || favicon_id == 0)
281 return thumb_db_->GetFaviconHeader(favicon_id, NULL, NULL);
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,
290 return icon_mappings[0].icon_id;
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.
299 if (!main_db_->GetURLRow(url_id, &info))
301 GURL url = info.url();
302 scoped_refptr<base::RefCountedMemory> data;
303 return top_sites_->GetPageThumbnail(url, false, &data);
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());
311 // Verify the URL no longer exists.
313 EXPECT_FALSE(main_db_->GetURLRow(row.id(), &temp_row));
315 // There should be no visits.
317 main_db_->GetVisitsForURL(row.id(), &visits);
318 EXPECT_EQ(0U, visits.size());
320 // Thumbnail should be gone.
321 // TODO(sky): fix this, see comment in HasThumbnail.
322 // EXPECT_FALSE(HasThumbnail(row.id()));
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;
340 } else if (notifications_[i].first ==
341 chrome::NOTIFICATION_HISTORY_URLS_MODIFIED) {
342 const history::URLRows& rows =
343 static_cast<URLsModifiedDetails*>(notifications_[i].second)->
346 std::find_if(rows.begin(), rows.end(),
347 history::URLRow::URLRowHasURL(row.url())) ==
351 EXPECT_TRUE(found_delete_notification);
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)->
360 if (std::find_if(rows.begin(), rows.end(),
361 history::URLRow::URLRowHasURL(url)) != rows.end())
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));
376 // The favicon should be deletable with no users.
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));
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));
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);
397 // Favicon should not be deletable.
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());
408 bool ExpireHistoryTest::IsStringInFile(const base::FilePath& filename,
410 std::string contents;
411 EXPECT_TRUE(base::ReadFileToString(filename, &contents));
412 return contents.find(str) != std::string::npos;
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) {
421 AddExampleData(url_ids, visit_times);
423 // Verify things are the way we expect with a URL row, favicon, thumbnail.
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]));
433 main_db_->GetVisitsForURL(url_ids[2], &visits);
434 ASSERT_EQ(1U, visits.size());
436 // Delete the URL and its dependencies.
437 expirer_.DeleteURL(last_row.url());
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));
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) {
450 AddExampleData(url_ids, visit_times);
452 // Verify things are the way we expect with a URL row, favicon, thumbnail.
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]));
462 main_db_->GetVisitsForURL(url_ids[1], &visits);
463 EXPECT_EQ(2U, visits.size());
465 // Delete the URL and its dependencies.
466 expirer_.DeleteURL(last_row.url());
468 // All the normal data except the favicon should be gone.
469 EnsureURLInfoGone(last_row, false);
470 EXPECT_TRUE(HasFavicon(favicon_id));
473 // DeleteURL should not delete starred urls.
474 TEST_F(ExpireHistoryTest, DontDeleteStarredURL) {
477 AddExampleData(url_ids, visit_times);
480 ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row));
482 // Star the last URL.
483 StarURL(url_row.url());
485 // Attempt to delete the url.
486 expirer_.DeleteURL(url_row.url());
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));
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));
499 main_db_->GetVisitsForURL(url_row.id(), &visits);
500 ASSERT_EQ(0U, visits.size());
502 // Should still have the thumbnail.
503 // TODO(sky): fix this, see comment in HasThumbnail.
504 // ASSERT_TRUE(HasThumbnail(url_row.id()));
506 // Unstar the URL and delete again.
507 history_client_.ClearAllBookmarks();
508 ClearLastNotifications();
509 expirer_.DeleteURL(url);
511 // Now it should be completely deleted.
512 EnsureURLInfoGone(url_row, false);
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) {
520 AddExampleData(url_ids, visit_times);
522 // Verify things are the way we expect with URL rows, favicons,
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());
538 StarURL(rows[0].url());
540 // Delete the URLs and their dependencies.
541 expirer_.DeleteURLs(urls);
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]));
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) {
558 AddExampleData(url_ids, visit_times);
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));
565 main_db_->GetVisitsForURL(url_ids[2], &visits);
566 ASSERT_EQ(1U, visits.size());
568 // This should delete the last two visits.
569 std::set<GURL> restrict_urls;
570 expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
572 // Verify that the middle URL had its last visit deleted only.
574 main_db_->GetVisitsForURL(url_ids[1], &visits);
575 EXPECT_EQ(1U, visits.size());
577 // Verify that the middle URL visit time and visit counts were updated.
578 EXPECT_TRUE(ModifiedNotificationSent(url_row1.url()));
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());
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()));
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));
602 // Expires all URLs with times in a given set.
603 TEST_F(ExpireHistoryTest, FlushURLsForTimes) {
606 AddExampleData(url_ids, visit_times);
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));
613 main_db_->GetVisitsForURL(url_ids[2], &visits);
614 ASSERT_EQ(1U, visits.size());
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);
622 // Verify that the middle URL had its last visit deleted only.
624 main_db_->GetVisitsForURL(url_ids[1], &visits);
625 EXPECT_EQ(1U, visits.size());
627 // Verify that the middle URL visit time and visit counts were updated.
628 EXPECT_TRUE(ModifiedNotificationSent(url_row1.url()));
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());
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()));
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));
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) {
658 AddExampleData(url_ids, visit_times);
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));
665 main_db_->GetVisitsForURL(url_ids[2], &visits);
666 ASSERT_EQ(1U, visits.size());
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());
673 // Verify that the middle URL had its last visit deleted only.
675 main_db_->GetVisitsForURL(url_ids[1], &visits);
676 EXPECT_EQ(1U, visits.size());
678 // Verify that the middle URL visit time and visit counts were updated.
679 EXPECT_TRUE(ModifiedNotificationSent(url_row1.url()));
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());
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()));
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()));
703 // Expire a starred URL, it shouldn't get deleted
704 TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) {
707 AddExampleData(url_ids, visit_times);
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));
713 // Star the last two URLs.
714 StarURL(url_row1.url());
715 StarURL(url_row2.url());
717 // This should delete the last two visits.
718 std::set<GURL> restrict_urls;
719 expirer_.ExpireHistoryBetween(restrict_urls, visit_times[2], Time());
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));
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.
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());
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()));
753 TEST_F(ExpireHistoryTest, ExpireHistoryBeforeUnstarred) {
756 AddExampleData(url_ids, visit_times);
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));
763 // Expire the oldest two visits.
764 expirer_.ExpireHistoryBefore(visit_times[1]);
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.
770 EnsureURLInfoGone(url_row0, true);
771 EXPECT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row));
772 EXPECT_TRUE(ModifiedNotificationSent(url_row1.url()));
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));
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());
789 TEST_F(ExpireHistoryTest, ExpireHistoryBeforeStarred) {
792 AddExampleData(url_ids, visit_times);
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));
799 StarURL(url_row0.url());
800 StarURL(url_row1.url());
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
805 expirer_.ExpireHistoryBefore(visit_times[2]);
808 ASSERT_TRUE(main_db_->GetURLRow(url_ids[0], &temp_row));
809 EXPECT_TRUE(ModifiedNotificationSent(url_row0.url()));
811 main_db_->GetVisitsForURL(temp_row.id(), &visits);
812 EXPECT_EQ(0U, visits.size());
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());
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());
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) {
832 AddExampleData(url_ids, visit_times);
833 const ExpiringVisitsReader* reader = expirer_.GetAllVisitsReader();
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));
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));
843 // Deleting a time range with the max number of results should return true
845 EXPECT_TRUE(expirer_.ExpireSomeOldHistory(visit_times[2], reader, 1));
848 TEST_F(ExpireHistoryTest, ExpiringVisitsReader) {
851 AddExampleData(url_ids, visit_times);
853 const ExpiringVisitsReader* all = expirer_.GetAllVisitsReader();
854 const ExpiringVisitsReader* auto_subframes =
855 expirer_.GetAutoSubframeVisitsReader();
858 Time now = Time::Now();
860 // Verify that the early expiration threshold, stored in the meta table is
862 EXPECT_TRUE(main_db_->GetEarlyExpirationThreshold() ==
863 Time::FromInternalValue(1L));
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());
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());
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());
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.
881 // Maybe also refer to invalid favicons.
883 } // namespace history