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