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