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