1 // Copyright (c) 2011 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 <set> 6 #include <vector> 7 8 #include "base/command_line.h" 9 #include "base/file_path.h" 10 #include "base/file_util.h" 11 #include "base/memory/ref_counted.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/path_service.h" 14 #include "base/string16.h" 15 #include "base/utf_string_conversions.h" 16 #include "chrome/browser/bookmarks/bookmark_model.h" 17 #include "chrome/browser/history/history_backend.h" 18 #include "chrome/browser/history/history_notifications.h" 19 #include "chrome/browser/history/in_memory_database.h" 20 #include "chrome/browser/history/in_memory_history_backend.h" 21 #include "chrome/common/chrome_constants.h" 22 #include "chrome/common/chrome_paths.h" 23 #include "chrome/common/thumbnail_score.h" 24 #include "chrome/tools/profiles/thumbnail-inl.h" 25 #include "content/common/notification_details.h" 26 #include "content/common/notification_source.h" 27 #include "googleurl/src/gurl.h" 28 #include "testing/gtest/include/gtest/gtest.h" 29 #include "ui/gfx/codec/jpeg_codec.h" 30 31 using base::Time; 32 33 // This file only tests functionality where it is most convenient to call the 34 // backend directly. Most of the history backend functions are tested by the 35 // history unit test. Because of the elaborate callbacks involved, this is no 36 // harder than calling it directly for many things. 37 38 namespace { 39 40 // data we'll put into the thumbnail database 41 static const unsigned char blob1[] = 42 "12346102356120394751634516591348710478123649165419234519234512349134"; 43 44 } // namepace 45 46 namespace history { 47 48 class HistoryBackendTest; 49 50 // This must be a separate object since HistoryBackend manages its lifetime. 51 // This just forwards the messages we're interested in to the test object. 52 class HistoryBackendTestDelegate : public HistoryBackend::Delegate { 53 public: 54 explicit HistoryBackendTestDelegate(HistoryBackendTest* test) : test_(test) {} 55 56 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {} 57 virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE; 58 virtual void BroadcastNotifications(NotificationType type, 59 HistoryDetails* details) OVERRIDE; 60 virtual void DBLoaded() OVERRIDE; 61 virtual void StartTopSitesMigration() OVERRIDE; 62 63 private: 64 // Not owned by us. 65 HistoryBackendTest* test_; 66 67 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate); 68 }; 69 70 class HistoryBackendTest : public testing::Test { 71 public: 72 HistoryBackendTest() : bookmark_model_(NULL), loaded_(false) {} 73 virtual ~HistoryBackendTest() { 74 } 75 76 protected: 77 scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure. 78 scoped_ptr<InMemoryHistoryBackend> mem_backend_; 79 80 void AddRedirectChain(const char* sequence[], int page_id) { 81 history::RedirectList redirects; 82 for (int i = 0; sequence[i] != NULL; ++i) 83 redirects.push_back(GURL(sequence[i])); 84 85 int int_scope = 1; 86 void* scope = 0; 87 memcpy(&scope, &int_scope, sizeof(int_scope)); 88 scoped_refptr<history::HistoryAddPageArgs> request( 89 new history::HistoryAddPageArgs( 90 redirects.back(), Time::Now(), scope, page_id, GURL(), 91 redirects, PageTransition::LINK, history::SOURCE_BROWSED, true)); 92 backend_->AddPage(request); 93 } 94 95 // Adds CLIENT_REDIRECT page transition. 96 // |url1| is the source URL and |url2| is the destination. 97 // |did_replace| is true if the transition is non-user initiated and the 98 // navigation entry for |url2| has replaced that for |url1|. The possibly 99 // updated transition code of the visit records for |url1| and |url2| is 100 // returned by filling in |*transition1| and |*transition2|, respectively. 101 void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace, 102 int* transition1, int* transition2) { 103 void* const dummy_scope = reinterpret_cast<void*>(0x87654321); 104 history::RedirectList redirects; 105 if (url1.is_valid()) 106 redirects.push_back(url1); 107 if (url2.is_valid()) 108 redirects.push_back(url2); 109 scoped_refptr<HistoryAddPageArgs> request( 110 new HistoryAddPageArgs(url2, base::Time(), dummy_scope, 0, url1, 111 redirects, PageTransition::CLIENT_REDIRECT, 112 history::SOURCE_BROWSED, did_replace)); 113 backend_->AddPage(request); 114 115 *transition1 = getTransition(url1); 116 *transition2 = getTransition(url2); 117 } 118 119 int getTransition(const GURL& url) { 120 if (!url.is_valid()) 121 return 0; 122 URLRow row; 123 URLID id = backend_->db()->GetRowForURL(url, &row); 124 VisitVector visits; 125 EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 126 return visits[0].transition; 127 } 128 129 FilePath getTestDir() { 130 return test_dir_; 131 } 132 133 FaviconID GetFavicon(const GURL& url, IconType icon_type) { 134 IconMapping icon_mapping; 135 if (backend_->thumbnail_db_->GetIconMappingForPageURL(url, icon_type, 136 &icon_mapping)) 137 return icon_mapping.icon_id; 138 else 139 return 0; 140 } 141 142 BookmarkModel bookmark_model_; 143 144 protected: 145 bool loaded_; 146 147 private: 148 friend class HistoryBackendTestDelegate; 149 150 // testing::Test 151 virtual void SetUp() { 152 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"), 153 &test_dir_)) 154 return; 155 backend_ = new HistoryBackend(test_dir_, 156 new HistoryBackendTestDelegate(this), 157 &bookmark_model_); 158 backend_->Init(std::string(), false); 159 } 160 virtual void TearDown() { 161 if (backend_.get()) 162 backend_->Closing(); 163 backend_ = NULL; 164 mem_backend_.reset(); 165 file_util::Delete(test_dir_, true); 166 } 167 168 void SetInMemoryBackend(InMemoryHistoryBackend* backend) { 169 mem_backend_.reset(backend); 170 } 171 172 void BroadcastNotifications(NotificationType type, 173 HistoryDetails* details) { 174 // Send the notifications directly to the in-memory database. 175 Details<HistoryDetails> det(details); 176 mem_backend_->Observe(type, Source<HistoryBackendTest>(NULL), det); 177 178 // The backend passes ownership of the details pointer to us. 179 delete details; 180 } 181 182 MessageLoop message_loop_; 183 FilePath test_dir_; 184 }; 185 186 void HistoryBackendTestDelegate::SetInMemoryBackend( 187 InMemoryHistoryBackend* backend) { 188 test_->SetInMemoryBackend(backend); 189 } 190 191 void HistoryBackendTestDelegate::BroadcastNotifications( 192 NotificationType type, 193 HistoryDetails* details) { 194 test_->BroadcastNotifications(type, details); 195 } 196 197 void HistoryBackendTestDelegate::DBLoaded() { 198 test_->loaded_ = true; 199 } 200 201 void HistoryBackendTestDelegate::StartTopSitesMigration() { 202 test_->backend_->MigrateThumbnailsDatabase(); 203 } 204 205 TEST_F(HistoryBackendTest, Loaded) { 206 ASSERT_TRUE(backend_.get()); 207 ASSERT_TRUE(loaded_); 208 } 209 210 TEST_F(HistoryBackendTest, DeleteAll) { 211 ASSERT_TRUE(backend_.get()); 212 213 // Add two favicons, use the characters '1' and '2' for the image data. Note 214 // that we do these in the opposite order. This is so the first one gets ID 215 // 2 autoassigned to the database, which will change when the other one is 216 // deleted. This way we can test that updating works properly. 217 GURL favicon_url1("http://www.google.com/favicon.ico"); 218 GURL favicon_url2("http://news.google.com/favicon.ico"); 219 FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2, 220 FAVICON); 221 FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1, 222 FAVICON); 223 224 std::vector<unsigned char> data; 225 data.push_back('1'); 226 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1, 227 new RefCountedBytes(data), Time::Now())); 228 229 data[0] = '2'; 230 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon( 231 favicon2, new RefCountedBytes(data), Time::Now())); 232 233 // First visit two URLs. 234 URLRow row1(GURL("http://www.google.com/")); 235 row1.set_visit_count(2); 236 row1.set_typed_count(1); 237 row1.set_last_visit(Time::Now()); 238 backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1); 239 240 URLRow row2(GURL("http://news.google.com/")); 241 row2.set_visit_count(1); 242 row2.set_last_visit(Time::Now()); 243 backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2); 244 245 std::vector<URLRow> rows; 246 rows.push_back(row2); // Reversed order for the same reason as favicons. 247 rows.push_back(row1); 248 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 249 250 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL); 251 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL); 252 253 // Get the two visits for the URLs we just added. 254 VisitVector visits; 255 backend_->db_->GetVisitsForURL(row1_id, &visits); 256 ASSERT_EQ(1U, visits.size()); 257 VisitID visit1_id = visits[0].visit_id; 258 259 visits.clear(); 260 backend_->db_->GetVisitsForURL(row2_id, &visits); 261 ASSERT_EQ(1U, visits.size()); 262 VisitID visit2_id = visits[0].visit_id; 263 264 // The in-memory backend should have been set and it should have gotten the 265 // typed URL. 266 ASSERT_TRUE(mem_backend_.get()); 267 URLRow outrow1; 268 EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL)); 269 270 // Add thumbnails for each page. 271 ThumbnailScore score(0.25, true, true); 272 scoped_ptr<SkBitmap> google_bitmap( 273 gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail))); 274 275 Time time; 276 GURL gurl; 277 backend_->thumbnail_db_->SetPageThumbnail(gurl, row1_id, *google_bitmap, 278 score, time); 279 scoped_ptr<SkBitmap> weewar_bitmap( 280 gfx::JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail))); 281 backend_->thumbnail_db_->SetPageThumbnail(gurl, row2_id, *weewar_bitmap, 282 score, time); 283 284 // Star row1. 285 bookmark_model_.AddURL( 286 bookmark_model_.GetBookmarkBarNode(), 0, string16(), row1.url()); 287 288 // Set full text index for each one. 289 backend_->text_database_->AddPageData(row1.url(), row1_id, visit1_id, 290 row1.last_visit(), 291 UTF8ToUTF16("Title 1"), 292 UTF8ToUTF16("Body 1")); 293 backend_->text_database_->AddPageData(row2.url(), row2_id, visit2_id, 294 row2.last_visit(), 295 UTF8ToUTF16("Title 2"), 296 UTF8ToUTF16("Body 2")); 297 298 // Now finally clear all history. 299 backend_->DeleteAllHistory(); 300 301 // The first URL should be preserved but the time should be cleared. 302 EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1)); 303 EXPECT_EQ(row1.url(), outrow1.url()); 304 EXPECT_EQ(0, outrow1.visit_count()); 305 EXPECT_EQ(0, outrow1.typed_count()); 306 EXPECT_TRUE(Time() == outrow1.last_visit()); 307 308 // The second row should be deleted. 309 URLRow outrow2; 310 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2)); 311 312 // All visits should be deleted for both URLs. 313 VisitVector all_visits; 314 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits); 315 ASSERT_EQ(0U, all_visits.size()); 316 317 // All thumbnails should be deleted. 318 std::vector<unsigned char> out_data; 319 EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(outrow1.id(), 320 &out_data)); 321 EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(row2_id, &out_data)); 322 323 // We should have a favicon for the first URL only. We look them up by favicon 324 // URL since the IDs may hav changed. 325 FaviconID out_favicon1 = backend_->thumbnail_db_-> 326 GetFaviconIDForFaviconURL(favicon_url1, FAVICON, NULL); 327 EXPECT_TRUE(out_favicon1); 328 FaviconID out_favicon2 = backend_->thumbnail_db_-> 329 GetFaviconIDForFaviconURL(favicon_url2, FAVICON, NULL); 330 EXPECT_FALSE(out_favicon2) << "Favicon not deleted"; 331 332 // The remaining URL should still reference the same favicon, even if its 333 // ID has changed. 334 EXPECT_EQ(out_favicon1, GetFavicon(outrow1.url(), FAVICON)); 335 336 // The first URL should still be bookmarked. 337 EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url())); 338 339 // The full text database should have no data. 340 std::vector<TextDatabase::Match> text_matches; 341 Time first_time_searched; 342 backend_->text_database_->GetTextMatches(UTF8ToUTF16("Body"), 343 QueryOptions(), 344 &text_matches, 345 &first_time_searched); 346 EXPECT_EQ(0U, text_matches.size()); 347 } 348 349 TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) { 350 GURL favicon_url1("http://www.google.com/favicon.ico"); 351 GURL favicon_url2("http://news.google.com/favicon.ico"); 352 FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2, 353 FAVICON); 354 FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1, 355 FAVICON); 356 357 std::vector<unsigned char> data; 358 data.push_back('1'); 359 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon( 360 favicon1, new RefCountedBytes(data), Time::Now())); 361 362 data[0] = '2'; 363 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon( 364 favicon2, new RefCountedBytes(data), Time::Now())); 365 366 // First visit two URLs. 367 URLRow row1(GURL("http://www.google.com/")); 368 row1.set_visit_count(2); 369 row1.set_typed_count(1); 370 row1.set_last_visit(Time::Now()); 371 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1)); 372 373 URLRow row2(GURL("http://news.google.com/")); 374 row2.set_visit_count(1); 375 row2.set_last_visit(Time::Now()); 376 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2)); 377 378 std::vector<URLRow> rows; 379 rows.push_back(row2); // Reversed order for the same reason as favicons. 380 rows.push_back(row1); 381 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 382 383 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL); 384 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL); 385 386 // Star the two URLs. 387 bookmark_model_.SetURLStarred(row1.url(), string16(), true); 388 bookmark_model_.SetURLStarred(row2.url(), string16(), true); 389 390 // Delete url 2. Because url 2 is starred this won't delete the URL, only 391 // the visits. 392 backend_->expirer_.DeleteURL(row2.url()); 393 394 // Make sure url 2 is still valid, but has no visits. 395 URLRow tmp_url_row; 396 EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL)); 397 VisitVector visits; 398 backend_->db_->GetVisitsForURL(row2_id, &visits); 399 EXPECT_EQ(0U, visits.size()); 400 // The favicon should still be valid. 401 EXPECT_EQ(favicon2, 402 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2, 403 FAVICON, 404 NULL)); 405 406 // Unstar row2. 407 bookmark_model_.SetURLStarred(row2.url(), string16(), false); 408 // Tell the backend it was unstarred. We have to explicitly do this as 409 // BookmarkModel isn't wired up to the backend during testing. 410 std::set<GURL> unstarred_urls; 411 unstarred_urls.insert(row2.url()); 412 backend_->URLsNoLongerBookmarked(unstarred_urls); 413 414 // The URL should no longer exist. 415 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row)); 416 // And the favicon should be deleted. 417 EXPECT_EQ(0, 418 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2, 419 FAVICON, 420 NULL)); 421 422 // Unstar row 1. 423 bookmark_model_.SetURLStarred(row1.url(), string16(), false); 424 // Tell the backend it was unstarred. We have to explicitly do this as 425 // BookmarkModel isn't wired up to the backend during testing. 426 unstarred_urls.clear(); 427 unstarred_urls.insert(row1.url()); 428 backend_->URLsNoLongerBookmarked(unstarred_urls); 429 430 // The URL should still exist (because there were visits). 431 EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL)); 432 433 // There should still be visits. 434 visits.clear(); 435 backend_->db_->GetVisitsForURL(row1_id, &visits); 436 EXPECT_EQ(1U, visits.size()); 437 438 // The favicon should still be valid. 439 EXPECT_EQ(favicon1, 440 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url1, 441 FAVICON, 442 NULL)); 443 } 444 445 // Tests a handful of assertions for a navigation with a type of 446 // KEYWORD_GENERATED. 447 TEST_F(HistoryBackendTest, KeywordGenerated) { 448 ASSERT_TRUE(backend_.get()); 449 450 GURL url("http://google.com"); 451 452 Time visit_time = Time::Now() - base::TimeDelta::FromDays(1); 453 scoped_refptr<HistoryAddPageArgs> request( 454 new HistoryAddPageArgs(url, visit_time, NULL, 0, GURL(), 455 history::RedirectList(), 456 PageTransition::KEYWORD_GENERATED, 457 history::SOURCE_BROWSED, false)); 458 backend_->AddPage(request); 459 460 // A row should have been added for the url. 461 URLRow row; 462 URLID url_id = backend_->db()->GetRowForURL(url, &row); 463 ASSERT_NE(0, url_id); 464 465 // The typed count should be 1. 466 ASSERT_EQ(1, row.typed_count()); 467 468 // KEYWORD_GENERATED urls should not be added to the segment db. 469 std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url); 470 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name)); 471 472 // One visit should be added. 473 VisitVector visits; 474 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits)); 475 EXPECT_EQ(1U, visits.size()); 476 477 // But no visible visits. 478 visits.clear(); 479 backend_->db()->GetVisibleVisitsInRange(base::Time(), base::Time(), 1, 480 &visits); 481 EXPECT_TRUE(visits.empty()); 482 483 // Expire the visits. 484 std::set<GURL> restrict_urls; 485 backend_->expire_backend()->ExpireHistoryBetween(restrict_urls, 486 visit_time, Time::Now()); 487 488 // The visit should have been nuked. 489 visits.clear(); 490 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits)); 491 EXPECT_TRUE(visits.empty()); 492 493 // As well as the url. 494 ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row)); 495 } 496 497 TEST_F(HistoryBackendTest, ClientRedirect) { 498 ASSERT_TRUE(backend_.get()); 499 500 int transition1; 501 int transition2; 502 503 // Initial transition to page A. 504 GURL url_a("http://google.com/a"); 505 AddClientRedirect(GURL(), url_a, false, &transition1, &transition2); 506 EXPECT_TRUE(transition2 & PageTransition::CHAIN_END); 507 508 // User initiated redirect to page B. 509 GURL url_b("http://google.com/b"); 510 AddClientRedirect(url_a, url_b, false, &transition1, &transition2); 511 EXPECT_TRUE(transition1 & PageTransition::CHAIN_END); 512 EXPECT_TRUE(transition2 & PageTransition::CHAIN_END); 513 514 // Non-user initiated redirect to page C. 515 GURL url_c("http://google.com/c"); 516 AddClientRedirect(url_b, url_c, true, &transition1, &transition2); 517 EXPECT_FALSE(transition1 & PageTransition::CHAIN_END); 518 EXPECT_TRUE(transition2 & PageTransition::CHAIN_END); 519 } 520 521 TEST_F(HistoryBackendTest, ImportedFaviconsTest) { 522 // Setup test data - two Urls in the history, one with favicon assigned and 523 // one without. 524 GURL favicon_url1("http://www.google.com/favicon.ico"); 525 FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1, 526 FAVICON); 527 std::vector<unsigned char> data; 528 data.push_back('1'); 529 EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1, 530 RefCountedBytes::TakeVector(&data), Time::Now())); 531 URLRow row1(GURL("http://www.google.com/")); 532 row1.set_visit_count(1); 533 row1.set_last_visit(Time::Now()); 534 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1)); 535 536 URLRow row2(GURL("http://news.google.com/")); 537 row2.set_visit_count(1); 538 row2.set_last_visit(Time::Now()); 539 std::vector<URLRow> rows; 540 rows.push_back(row1); 541 rows.push_back(row2); 542 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 543 URLRow url_row1, url_row2; 544 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0); 545 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0); 546 EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0); 547 EXPECT_TRUE(GetFavicon(row2.url(), FAVICON) == 0); 548 549 // Now provide one imported favicon for both URLs already in the registry. 550 // The new favicon should only be used with the URL that doesn't already have 551 // a favicon. 552 std::vector<history::ImportedFaviconUsage> favicons; 553 history::ImportedFaviconUsage favicon; 554 favicon.favicon_url = GURL("http://news.google.com/favicon.ico"); 555 favicon.png_data.push_back('2'); 556 favicon.urls.insert(row1.url()); 557 favicon.urls.insert(row2.url()); 558 favicons.push_back(favicon); 559 backend_->SetImportedFavicons(favicons); 560 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0); 561 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0); 562 EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0); 563 EXPECT_FALSE(GetFavicon(row2.url(), FAVICON) == 0); 564 EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 565 GetFavicon(row2.url(), FAVICON)); 566 567 // A URL should not be added to history (to store favicon), if 568 // the URL is not bookmarked. 569 GURL url3("http://mail.google.com"); 570 favicons.clear(); 571 favicon.favicon_url = GURL("http://mail.google.com/favicon.ico"); 572 favicon.png_data.push_back('3'); 573 favicon.urls.insert(url3); 574 favicons.push_back(favicon); 575 backend_->SetImportedFavicons(favicons); 576 URLRow url_row3; 577 EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0); 578 579 // If the URL is bookmarked, it should get added to history with 0 visits. 580 bookmark_model_.AddURL(bookmark_model_.GetBookmarkBarNode(), 0, string16(), 581 url3); 582 backend_->SetImportedFavicons(favicons); 583 EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0); 584 EXPECT_TRUE(url_row3.visit_count() == 0); 585 } 586 587 TEST_F(HistoryBackendTest, StripUsernamePasswordTest) { 588 ASSERT_TRUE(backend_.get()); 589 590 GURL url("http://anyuser:anypass@www.google.com"); 591 GURL stripped_url("http://www.google.com"); 592 593 // Clear all history. 594 backend_->DeleteAllHistory(); 595 596 // Visit the url with username, password. 597 backend_->AddPageVisit(url, base::Time::Now(), 0, 598 PageTransition::GetQualifier(PageTransition::TYPED), 599 history::SOURCE_BROWSED); 600 601 // Fetch the row information about stripped url from history db. 602 VisitVector visits; 603 URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL); 604 backend_->db_->GetVisitsForURL(row_id, &visits); 605 606 // Check if stripped url is stored in database. 607 ASSERT_EQ(1U, visits.size()); 608 } 609 610 TEST_F(HistoryBackendTest, AddPageVisitSource) { 611 ASSERT_TRUE(backend_.get()); 612 613 GURL url("http://www.google.com"); 614 615 // Clear all history. 616 backend_->DeleteAllHistory(); 617 618 // Assume visiting the url from an externsion. 619 backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED, 620 history::SOURCE_EXTENSION); 621 // Assume the url is imported from Firefox. 622 backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED, 623 history::SOURCE_FIREFOX_IMPORTED); 624 // Assume this url is also synced. 625 backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED, 626 history::SOURCE_SYNCED); 627 628 // Fetch the row information about the url from history db. 629 VisitVector visits; 630 URLID row_id = backend_->db_->GetRowForURL(url, NULL); 631 backend_->db_->GetVisitsForURL(row_id, &visits); 632 633 // Check if all the visits to the url are stored in database. 634 ASSERT_EQ(3U, visits.size()); 635 VisitSourceMap visit_sources; 636 backend_->db_->GetVisitsSource(visits, &visit_sources); 637 ASSERT_EQ(3U, visit_sources.size()); 638 int sources = 0; 639 for (int i = 0; i < 3; i++) { 640 switch (visit_sources[visits[i].visit_id]) { 641 case history::SOURCE_EXTENSION: 642 sources |= 0x1; 643 break; 644 case history::SOURCE_FIREFOX_IMPORTED: 645 sources |= 0x2; 646 break; 647 case history::SOURCE_SYNCED: 648 sources |= 0x4; 649 default: 650 break; 651 } 652 } 653 EXPECT_EQ(0x7, sources); 654 } 655 656 TEST_F(HistoryBackendTest, AddPageArgsSource) { 657 ASSERT_TRUE(backend_.get()); 658 659 GURL url("http://testpageargs.com"); 660 661 // Assume this page is browsed by user. 662 scoped_refptr<HistoryAddPageArgs> request1( 663 new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(), 664 history::RedirectList(), 665 PageTransition::KEYWORD_GENERATED, 666 history::SOURCE_BROWSED, false)); 667 backend_->AddPage(request1); 668 // Assume this page is synced. 669 scoped_refptr<HistoryAddPageArgs> request2( 670 new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(), 671 history::RedirectList(), 672 PageTransition::LINK, 673 history::SOURCE_SYNCED, false)); 674 backend_->AddPage(request2); 675 // Assume this page is browsed again. 676 scoped_refptr<HistoryAddPageArgs> request3( 677 new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(), 678 history::RedirectList(), 679 PageTransition::TYPED, 680 history::SOURCE_BROWSED, false)); 681 backend_->AddPage(request3); 682 683 // Three visits should be added with proper sources. 684 VisitVector visits; 685 URLRow row; 686 URLID id = backend_->db()->GetRowForURL(url, &row); 687 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 688 ASSERT_EQ(3U, visits.size()); 689 VisitSourceMap visit_sources; 690 backend_->db_->GetVisitsSource(visits, &visit_sources); 691 ASSERT_EQ(1U, visit_sources.size()); 692 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second); 693 } 694 695 TEST_F(HistoryBackendTest, AddVisitsSource) { 696 ASSERT_TRUE(backend_.get()); 697 698 GURL url1("http://www.cnn.com"); 699 std::vector<base::Time> visits1; 700 visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5)); 701 visits1.push_back(Time::Now() - base::TimeDelta::FromDays(1)); 702 visits1.push_back(Time::Now()); 703 704 GURL url2("http://www.example.com"); 705 std::vector<base::Time> visits2; 706 visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10)); 707 visits2.push_back(Time::Now()); 708 709 // Clear all history. 710 backend_->DeleteAllHistory(); 711 712 // Add the visits. 713 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED); 714 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED); 715 716 // Verify the visits were added with their sources. 717 VisitVector visits; 718 URLRow row; 719 URLID id = backend_->db()->GetRowForURL(url1, &row); 720 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 721 ASSERT_EQ(3U, visits.size()); 722 VisitSourceMap visit_sources; 723 backend_->db_->GetVisitsSource(visits, &visit_sources); 724 ASSERT_EQ(3U, visit_sources.size()); 725 for (int i = 0; i < 3; i++) 726 EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]); 727 id = backend_->db()->GetRowForURL(url2, &row); 728 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 729 ASSERT_EQ(2U, visits.size()); 730 backend_->db_->GetVisitsSource(visits, &visit_sources); 731 ASSERT_EQ(2U, visit_sources.size()); 732 for (int i = 0; i < 2; i++) 733 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]); 734 } 735 736 TEST_F(HistoryBackendTest, RemoveVisitsSource) { 737 ASSERT_TRUE(backend_.get()); 738 739 GURL url1("http://www.cnn.com"); 740 std::vector<base::Time> visits1; 741 visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5)); 742 visits1.push_back(Time::Now()); 743 744 GURL url2("http://www.example.com"); 745 std::vector<base::Time> visits2; 746 visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10)); 747 visits2.push_back(Time::Now()); 748 749 // Clear all history. 750 backend_->DeleteAllHistory(); 751 752 // Add the visits. 753 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED); 754 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED); 755 756 // Verify the visits of url1 were added. 757 VisitVector visits; 758 URLRow row; 759 URLID id = backend_->db()->GetRowForURL(url1, &row); 760 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 761 ASSERT_EQ(2U, visits.size()); 762 // Remove these visits. 763 ASSERT_TRUE(backend_->RemoveVisits(visits)); 764 765 // Now check only url2's source in visit_source table. 766 VisitSourceMap visit_sources; 767 backend_->db_->GetVisitsSource(visits, &visit_sources); 768 ASSERT_EQ(0U, visit_sources.size()); 769 id = backend_->db()->GetRowForURL(url2, &row); 770 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 771 ASSERT_EQ(2U, visits.size()); 772 backend_->db_->GetVisitsSource(visits, &visit_sources); 773 ASSERT_EQ(2U, visit_sources.size()); 774 for (int i = 0; i < 2; i++) 775 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]); 776 } 777 778 // Test for migration of adding visit_source table. 779 TEST_F(HistoryBackendTest, MigrationVisitSource) { 780 ASSERT_TRUE(backend_.get()); 781 backend_->Closing(); 782 backend_ = NULL; 783 784 FilePath old_history_path; 785 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path)); 786 old_history_path = old_history_path.AppendASCII("History"); 787 old_history_path = old_history_path.AppendASCII("HistoryNoSource"); 788 789 // Copy history database file to current directory so that it will be deleted 790 // in Teardown. 791 FilePath new_history_path(getTestDir()); 792 file_util::Delete(new_history_path, true); 793 file_util::CreateDirectory(new_history_path); 794 FilePath new_history_file = new_history_path.Append(chrome::kHistoryFilename); 795 ASSERT_TRUE(file_util::CopyFile(old_history_path, new_history_file)); 796 797 backend_ = new HistoryBackend(new_history_path, 798 new HistoryBackendTestDelegate(this), 799 &bookmark_model_); 800 backend_->Init(std::string(), false); 801 backend_->Closing(); 802 backend_ = NULL; 803 804 // Now the database should already be migrated. 805 // Check version first. 806 int cur_version = HistoryDatabase::GetCurrentVersion(); 807 sql::Connection db; 808 ASSERT_TRUE(db.Open(new_history_file)); 809 sql::Statement s(db.GetUniqueStatement( 810 "SELECT value FROM meta WHERE key = 'version'")); 811 ASSERT_TRUE(s.Step()); 812 int file_version = s.ColumnInt(0); 813 EXPECT_EQ(cur_version, file_version); 814 815 // Check visit_source table is created and empty. 816 s.Assign(db.GetUniqueStatement( 817 "SELECT name FROM sqlite_master WHERE name=\"visit_source\"")); 818 ASSERT_TRUE(s.Step()); 819 s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10")); 820 EXPECT_FALSE(s.Step()); 821 } 822 823 TEST_F(HistoryBackendTest, SetFaviconMapping) { 824 // Init recent_redirects_ 825 const GURL url1("http://www.google.com"); 826 const GURL url2("http://www.google.com/m"); 827 URLRow url_info1(url1); 828 url_info1.set_visit_count(0); 829 url_info1.set_typed_count(0); 830 url_info1.set_last_visit(base::Time()); 831 url_info1.set_hidden(false); 832 backend_->db_->AddURL(url_info1); 833 834 URLRow url_info2(url2); 835 url_info2.set_visit_count(0); 836 url_info2.set_typed_count(0); 837 url_info2.set_last_visit(base::Time()); 838 url_info2.set_hidden(false); 839 backend_->db_->AddURL(url_info2); 840 841 history::RedirectList redirects; 842 redirects.push_back(url2); 843 redirects.push_back(url1); 844 backend_->recent_redirects_.Put(url1, redirects); 845 846 const GURL icon_url("http://www.google.com/icon"); 847 std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); 848 // Add a favicon 849 backend_->SetFavicon( 850 url1, icon_url, RefCountedBytes::TakeVector(&data), FAVICON); 851 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 852 url1, FAVICON, NULL)); 853 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 854 url2, FAVICON, NULL)); 855 856 // Add a touch_icon 857 backend_->SetFavicon( 858 url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON); 859 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 860 url1, TOUCH_ICON, NULL)); 861 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 862 url2, TOUCH_ICON, NULL)); 863 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 864 url1, FAVICON, NULL)); 865 866 // Add a TOUCH_PRECOMPOSED_ICON 867 backend_->SetFavicon(url1, 868 icon_url, 869 RefCountedBytes::TakeVector(&data), 870 TOUCH_PRECOMPOSED_ICON); 871 // The touch_icon was replaced. 872 EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL( 873 url1, TOUCH_ICON, NULL)); 874 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 875 url1, FAVICON, NULL)); 876 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 877 url1, TOUCH_PRECOMPOSED_ICON, NULL)); 878 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 879 url2, TOUCH_PRECOMPOSED_ICON, NULL)); 880 881 // Add a touch_icon 882 backend_->SetFavicon( 883 url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON); 884 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 885 url1, TOUCH_ICON, NULL)); 886 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL( 887 url1, FAVICON, NULL)); 888 // The TOUCH_PRECOMPOSED_ICON was replaced. 889 EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL( 890 url1, TOUCH_PRECOMPOSED_ICON, NULL)); 891 892 // Add a favicon 893 const GURL icon_url2("http://www.google.com/icon2"); 894 backend_->SetFavicon( 895 url1, icon_url2, RefCountedBytes::TakeVector(&data), FAVICON); 896 FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 897 icon_url2, FAVICON, NULL); 898 EXPECT_NE(0, icon_id); 899 std::vector<IconMapping> icon_mapping; 900 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 901 url1, &icon_mapping)); 902 // The old icon was replaced. 903 EXPECT_TRUE(icon_mapping.size() > 1); 904 EXPECT_EQ(icon_id, icon_mapping[1].icon_id); 905 } 906 907 TEST_F(HistoryBackendTest, AddOrUpdateIconMapping) { 908 // Test the same icon and page mapping will not be added twice. other case 909 // should be covered in TEST_F(HistoryBackendTest, SetFaviconMapping) 910 const GURL url("http://www.google.com/"); 911 const GURL icon_url("http://www.google.com/icon"); 912 std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1)); 913 914 backend_->SetFavicon( 915 url, icon_url, RefCountedBytes::TakeVector(&data), FAVICON); 916 FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 917 icon_url, FAVICON, NULL); 918 919 // Add the same mapping 920 FaviconID replaced; 921 EXPECT_FALSE(backend_->AddOrUpdateIconMapping( 922 url, icon_id, FAVICON, &replaced)); 923 EXPECT_EQ(0, replaced); 924 925 std::vector<IconMapping> icon_mapping; 926 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 927 url, &icon_mapping)); 928 EXPECT_EQ(1u, icon_mapping.size()); 929 } 930 931 } // namespace history 932