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 "chrome/browser/history/history_backend.h" 6 7 #include <algorithm> 8 #include <set> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/bind.h" 13 #include "base/command_line.h" 14 #include "base/files/file_path.h" 15 #include "base/files/file_util.h" 16 #include "base/memory/ref_counted.h" 17 #include "base/memory/scoped_ptr.h" 18 #include "base/path_service.h" 19 #include "base/run_loop.h" 20 #include "base/strings/string16.h" 21 #include "base/strings/string_number_conversions.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "chrome/browser/chrome_notification_types.h" 24 #include "chrome/browser/history/history_notifications.h" 25 #include "chrome/browser/history/history_service.h" 26 #include "chrome/browser/history/history_service_factory.h" 27 #include "chrome/browser/history/in_memory_history_backend.h" 28 #include "chrome/browser/history/visit_filter.h" 29 #include "chrome/common/chrome_constants.h" 30 #include "chrome/common/chrome_paths.h" 31 #include "chrome/common/importer/imported_favicon_usage.h" 32 #include "chrome/test/base/testing_profile.h" 33 #include "components/history/core/browser/in_memory_database.h" 34 #include "components/history/core/browser/keyword_search_term.h" 35 #include "components/history/core/test/history_client_fake_bookmarks.h" 36 #include "content/public/browser/notification_details.h" 37 #include "content/public/browser/notification_source.h" 38 #include "content/public/test/test_browser_thread.h" 39 #include "testing/gmock/include/gmock/gmock.h" 40 #include "testing/gtest/include/gtest/gtest.h" 41 #include "third_party/skia/include/core/SkBitmap.h" 42 #include "ui/gfx/codec/png_codec.h" 43 #include "url/gurl.h" 44 45 using base::Time; 46 47 // This file only tests functionality where it is most convenient to call the 48 // backend directly. Most of the history backend functions are tested by the 49 // history unit test. Because of the elaborate callbacks involved, this is no 50 // harder than calling it directly for many things. 51 52 namespace { 53 54 const int kTinyEdgeSize = 10; 55 const int kSmallEdgeSize = 16; 56 const int kLargeEdgeSize = 32; 57 58 const gfx::Size kTinySize = gfx::Size(kTinyEdgeSize, kTinyEdgeSize); 59 const gfx::Size kSmallSize = gfx::Size(kSmallEdgeSize, kSmallEdgeSize); 60 const gfx::Size kLargeSize = gfx::Size(kLargeEdgeSize, kLargeEdgeSize); 61 62 // Comparison functions as to make it easier to check results of 63 // GetFaviconBitmaps() and GetIconMappingsForPageURL(). 64 bool IconMappingLessThan(const history::IconMapping& a, 65 const history::IconMapping& b) { 66 return a.icon_url < b.icon_url; 67 } 68 69 bool FaviconBitmapLessThan(const history::FaviconBitmap& a, 70 const history::FaviconBitmap& b) { 71 return a.pixel_size.GetArea() < b.pixel_size.GetArea(); 72 } 73 74 class HistoryClientMock : public history::HistoryClientFakeBookmarks { 75 public: 76 MOCK_METHOD0(BlockUntilBookmarksLoaded, void()); 77 }; 78 79 } // namespace 80 81 namespace history { 82 83 class HistoryBackendTestBase; 84 85 // This must be a separate object since HistoryBackend manages its lifetime. 86 // This just forwards the messages we're interested in to the test object. 87 class HistoryBackendTestDelegate : public HistoryBackend::Delegate { 88 public: 89 explicit HistoryBackendTestDelegate(HistoryBackendTestBase* test) 90 : test_(test) {} 91 92 virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {} 93 virtual void SetInMemoryBackend( 94 scoped_ptr<InMemoryHistoryBackend> backend) OVERRIDE; 95 virtual void NotifyFaviconChanged(const std::set<GURL>& urls) OVERRIDE; 96 virtual void BroadcastNotifications( 97 int type, 98 scoped_ptr<HistoryDetails> details) OVERRIDE; 99 virtual void DBLoaded() OVERRIDE; 100 virtual void NotifyVisitDBObserversOnAddVisit( 101 const BriefVisitInfo& info) OVERRIDE {} 102 103 private: 104 // Not owned by us. 105 HistoryBackendTestBase* test_; 106 107 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate); 108 }; 109 110 class HistoryBackendTestBase : public testing::Test { 111 public: 112 typedef std::vector<std::pair<int, HistoryDetails*> > NotificationList; 113 114 HistoryBackendTestBase() 115 : loaded_(false), 116 favicon_changed_notifications_(0), 117 ui_thread_(content::BrowserThread::UI, &message_loop_) {} 118 119 virtual ~HistoryBackendTestBase() { 120 STLDeleteValues(&broadcasted_notifications_); 121 } 122 123 protected: 124 int favicon_changed_notifications() const { 125 return favicon_changed_notifications_; 126 } 127 128 void ClearFaviconChangedNotificationCounter() { 129 favicon_changed_notifications_ = 0; 130 } 131 132 int num_broadcasted_notifications() const { 133 return broadcasted_notifications_.size(); 134 } 135 136 const NotificationList& broadcasted_notifications() const { 137 return broadcasted_notifications_; 138 } 139 140 void ClearBroadcastedNotifications() { 141 STLDeleteValues(&broadcasted_notifications_); 142 } 143 144 base::FilePath test_dir() { 145 return test_dir_; 146 } 147 148 void NotifyFaviconChanged(const std::set<GURL>& changed_favicons) { 149 ++favicon_changed_notifications_; 150 } 151 152 void BroadcastNotifications(int type, scoped_ptr<HistoryDetails> details) { 153 // Send the notifications directly to the in-memory database. 154 content::Details<HistoryDetails> det(details.get()); 155 mem_backend_->Observe( 156 type, content::Source<HistoryBackendTestBase>(NULL), det); 157 158 // The backend passes ownership of the details pointer to us. 159 broadcasted_notifications_.push_back( 160 std::make_pair(type, details.release())); 161 } 162 163 history::HistoryClientFakeBookmarks history_client_; 164 scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure. 165 scoped_ptr<InMemoryHistoryBackend> mem_backend_; 166 bool loaded_; 167 168 private: 169 friend class HistoryBackendTestDelegate; 170 171 // testing::Test 172 virtual void SetUp() { 173 ClearFaviconChangedNotificationCounter(); 174 if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"), 175 &test_dir_)) 176 return; 177 backend_ = new HistoryBackend( 178 test_dir_, new HistoryBackendTestDelegate(this), &history_client_); 179 backend_->Init(std::string(), false); 180 } 181 182 virtual void TearDown() { 183 if (backend_.get()) 184 backend_->Closing(); 185 backend_ = NULL; 186 mem_backend_.reset(); 187 base::DeleteFile(test_dir_, true); 188 base::RunLoop().RunUntilIdle(); 189 history_client_.ClearAllBookmarks(); 190 } 191 192 void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) { 193 mem_backend_.swap(backend); 194 } 195 196 // The types and details of notifications which were broadcasted. 197 NotificationList broadcasted_notifications_; 198 int favicon_changed_notifications_; 199 200 base::MessageLoop message_loop_; 201 base::FilePath test_dir_; 202 content::TestBrowserThread ui_thread_; 203 204 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestBase); 205 }; 206 207 void HistoryBackendTestDelegate::SetInMemoryBackend( 208 scoped_ptr<InMemoryHistoryBackend> backend) { 209 test_->SetInMemoryBackend(backend.Pass()); 210 } 211 212 void HistoryBackendTestDelegate::NotifyFaviconChanged( 213 const std::set<GURL>& changed_favicons) { 214 test_->NotifyFaviconChanged(changed_favicons); 215 } 216 217 void HistoryBackendTestDelegate::BroadcastNotifications( 218 int type, 219 scoped_ptr<HistoryDetails> details) { 220 test_->BroadcastNotifications(type, details.Pass()); 221 } 222 223 void HistoryBackendTestDelegate::DBLoaded() { 224 test_->loaded_ = true; 225 } 226 227 class HistoryBackendTest : public HistoryBackendTestBase { 228 public: 229 HistoryBackendTest() {} 230 virtual ~HistoryBackendTest() {} 231 232 protected: 233 void AddRedirectChain(const char* sequence[], int page_id) { 234 AddRedirectChainWithTransitionAndTime(sequence, page_id, 235 ui::PAGE_TRANSITION_LINK, 236 Time::Now()); 237 } 238 239 void AddRedirectChainWithTransitionAndTime( 240 const char* sequence[], 241 int page_id, 242 ui::PageTransition transition, 243 base::Time time) { 244 history::RedirectList redirects; 245 for (int i = 0; sequence[i] != NULL; ++i) 246 redirects.push_back(GURL(sequence[i])); 247 248 ContextID context_id = reinterpret_cast<ContextID>(1); 249 history::HistoryAddPageArgs request( 250 redirects.back(), time, context_id, page_id, GURL(), 251 redirects, transition, history::SOURCE_BROWSED, 252 true); 253 backend_->AddPage(request); 254 } 255 256 // Adds CLIENT_REDIRECT page transition. 257 // |url1| is the source URL and |url2| is the destination. 258 // |did_replace| is true if the transition is non-user initiated and the 259 // navigation entry for |url2| has replaced that for |url1|. The possibly 260 // updated transition code of the visit records for |url1| and |url2| is 261 // returned by filling in |*transition1| and |*transition2|, respectively. 262 // |time| is a time of the redirect. 263 void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace, 264 base::Time time, 265 int* transition1, int* transition2) { 266 ContextID dummy_context_id = reinterpret_cast<ContextID>(0x87654321); 267 history::RedirectList redirects; 268 if (url1.is_valid()) 269 redirects.push_back(url1); 270 if (url2.is_valid()) 271 redirects.push_back(url2); 272 HistoryAddPageArgs request( 273 url2, time, dummy_context_id, 0, url1, 274 redirects, ui::PAGE_TRANSITION_CLIENT_REDIRECT, 275 history::SOURCE_BROWSED, did_replace); 276 backend_->AddPage(request); 277 278 *transition1 = GetTransition(url1); 279 *transition2 = GetTransition(url2); 280 } 281 282 int GetTransition(const GURL& url) { 283 if (!url.is_valid()) 284 return 0; 285 URLRow row; 286 URLID id = backend_->db()->GetRowForURL(url, &row); 287 VisitVector visits; 288 EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 289 return visits[0].transition; 290 } 291 292 // Returns a vector with the small and large edge sizes. 293 const std::vector<int> GetEdgeSizesSmallAndLarge() { 294 std::vector<int> sizes_small_and_large; 295 sizes_small_and_large.push_back(kSmallEdgeSize); 296 sizes_small_and_large.push_back(kLargeEdgeSize); 297 return sizes_small_and_large; 298 } 299 300 // Returns the number of icon mappings of |icon_type| to |page_url|. 301 size_t NumIconMappingsForPageURL(const GURL& page_url, 302 favicon_base::IconType icon_type) { 303 std::vector<IconMapping> icon_mappings; 304 backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type, 305 &icon_mappings); 306 return icon_mappings.size(); 307 } 308 309 // Returns the icon mappings for |page_url| sorted alphabetically by icon 310 // URL in ascending order. Returns true if there is at least one icon 311 // mapping. 312 bool GetSortedIconMappingsForPageURL( 313 const GURL& page_url, 314 std::vector<IconMapping>* icon_mappings) { 315 if (!backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 316 icon_mappings)) { 317 return false; 318 } 319 std::sort(icon_mappings->begin(), icon_mappings->end(), 320 IconMappingLessThan); 321 return true; 322 } 323 324 // Returns the favicon bitmaps for |icon_id| sorted by pixel size in 325 // ascending order. Returns true if there is at least one favicon bitmap. 326 bool GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id, 327 std::vector<FaviconBitmap>* favicon_bitmaps) { 328 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps)) 329 return false; 330 std::sort(favicon_bitmaps->begin(), favicon_bitmaps->end(), 331 FaviconBitmapLessThan); 332 return true; 333 } 334 335 // Returns true if there is exactly one favicon bitmap associated to 336 // |favicon_id|. If true, returns favicon bitmap in output parameter. 337 bool GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id, 338 FaviconBitmap* favicon_bitmap) { 339 std::vector<FaviconBitmap> favicon_bitmaps; 340 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps)) 341 return false; 342 if (favicon_bitmaps.size() != 1) 343 return false; 344 *favicon_bitmap = favicon_bitmaps[0]; 345 return true; 346 } 347 348 // Creates an |edge_size|x|edge_size| bitmap of |color|. 349 SkBitmap CreateBitmap(SkColor color, int edge_size) { 350 SkBitmap bitmap; 351 bitmap.allocN32Pixels(edge_size, edge_size); 352 bitmap.eraseColor(color); 353 return bitmap; 354 } 355 356 // Returns true if |bitmap_data| is equal to |expected_data|. 357 bool BitmapDataEqual(char expected_data, 358 scoped_refptr<base::RefCountedMemory> bitmap_data) { 359 return bitmap_data.get() && 360 bitmap_data->size() == 1u && 361 *bitmap_data->front() == expected_data; 362 } 363 364 // Returns true if |bitmap_data| is of |color|. 365 bool BitmapColorEqual(SkColor expected_color, 366 scoped_refptr<base::RefCountedMemory> bitmap_data) { 367 SkBitmap bitmap; 368 if (!gfx::PNGCodec::Decode( 369 bitmap_data->front(), bitmap_data->size(), &bitmap)) 370 return false; 371 SkAutoLockPixels bitmap_lock(bitmap); 372 return expected_color == bitmap.getColor(0, 0); 373 } 374 375 private: 376 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTest); 377 }; 378 379 class InMemoryHistoryBackendTest : public HistoryBackendTestBase { 380 public: 381 InMemoryHistoryBackendTest() {} 382 virtual ~InMemoryHistoryBackendTest() {} 383 384 protected: 385 void SimulateNotification(int type, 386 const URLRow* row1, 387 const URLRow* row2 = NULL, 388 const URLRow* row3 = NULL) { 389 URLRows rows; 390 rows.push_back(*row1); 391 if (row2) rows.push_back(*row2); 392 if (row3) rows.push_back(*row3); 393 394 if (type == chrome::NOTIFICATION_HISTORY_URLS_MODIFIED) { 395 scoped_ptr<URLsModifiedDetails> details(new URLsModifiedDetails()); 396 details->changed_urls.swap(rows); 397 BroadcastNotifications(type, details.PassAs<HistoryDetails>()); 398 } else if (type == chrome::NOTIFICATION_HISTORY_URL_VISITED) { 399 for (URLRows::const_iterator it = rows.begin(); it != rows.end(); ++it) { 400 scoped_ptr<URLVisitedDetails> details(new URLVisitedDetails()); 401 details->row = *it; 402 BroadcastNotifications(type, details.PassAs<HistoryDetails>()); 403 } 404 } else if (type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) { 405 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails()); 406 details->rows = rows; 407 BroadcastNotifications(type, details.PassAs<HistoryDetails>()); 408 } else { 409 NOTREACHED(); 410 } 411 } 412 413 size_t GetNumberOfMatchingSearchTerms(const int keyword_id, 414 const base::string16& prefix) { 415 std::vector<KeywordSearchTermVisit> matching_terms; 416 mem_backend_->db()->GetMostRecentKeywordSearchTerms( 417 keyword_id, prefix, 1, &matching_terms); 418 return matching_terms.size(); 419 } 420 421 static URLRow CreateTestTypedURL() { 422 URLRow url_row(GURL("https://www.google.com/")); 423 url_row.set_id(10); 424 url_row.set_title(base::UTF8ToUTF16("Google Search")); 425 url_row.set_typed_count(1); 426 url_row.set_visit_count(1); 427 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(1)); 428 return url_row; 429 } 430 431 static URLRow CreateAnotherTestTypedURL() { 432 URLRow url_row(GURL("https://maps.google.com/")); 433 url_row.set_id(20); 434 url_row.set_title(base::UTF8ToUTF16("Google Maps")); 435 url_row.set_typed_count(2); 436 url_row.set_visit_count(3); 437 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(2)); 438 return url_row; 439 } 440 441 static URLRow CreateTestNonTypedURL() { 442 URLRow url_row(GURL("https://news.google.com/")); 443 url_row.set_id(30); 444 url_row.set_title(base::UTF8ToUTF16("Google News")); 445 url_row.set_visit_count(5); 446 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(3)); 447 return url_row; 448 } 449 450 void PopulateTestURLsAndSearchTerms(URLRow* row1, 451 URLRow* row2, 452 const base::string16& term1, 453 const base::string16& term2); 454 455 void TestAddingAndChangingURLRows(int notification_type); 456 457 static const KeywordID kTestKeywordId; 458 static const char kTestSearchTerm1[]; 459 static const char kTestSearchTerm2[]; 460 461 private: 462 DISALLOW_COPY_AND_ASSIGN(InMemoryHistoryBackendTest); 463 }; 464 465 const KeywordID InMemoryHistoryBackendTest::kTestKeywordId = 42; 466 const char InMemoryHistoryBackendTest::kTestSearchTerm1[] = "banana"; 467 const char InMemoryHistoryBackendTest::kTestSearchTerm2[] = "orange"; 468 469 // http://crbug.com/114287 470 #if defined(OS_WIN) 471 #define MAYBE_Loaded DISABLED_Loaded 472 #else 473 #define MAYBE_Loaded Loaded 474 #endif // defined(OS_WIN) 475 TEST_F(HistoryBackendTest, MAYBE_Loaded) { 476 ASSERT_TRUE(backend_.get()); 477 ASSERT_TRUE(loaded_); 478 } 479 480 TEST_F(HistoryBackendTest, DeleteAll) { 481 ASSERT_TRUE(backend_.get()); 482 483 // Add two favicons, each with two bitmaps. Note that we add favicon2 before 484 // adding favicon1. This is so that favicon1 one gets ID 2 autoassigned to 485 // the database, which will change when the other one is deleted. This way 486 // we can test that updating works properly. 487 GURL favicon_url1("http://www.google.com/favicon.ico"); 488 GURL favicon_url2("http://news.google.com/favicon.ico"); 489 favicon_base::FaviconID favicon2 = 490 backend_->thumbnail_db_->AddFavicon(favicon_url2, favicon_base::FAVICON); 491 favicon_base::FaviconID favicon1 = 492 backend_->thumbnail_db_->AddFavicon(favicon_url1, favicon_base::FAVICON); 493 494 std::vector<unsigned char> data; 495 data.push_back('a'); 496 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1, 497 new base::RefCountedBytes(data), Time::Now(), kSmallSize)); 498 data[0] = 'b'; 499 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1, 500 new base::RefCountedBytes(data), Time::Now(), kLargeSize)); 501 502 data[0] = 'c'; 503 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2, 504 new base::RefCountedBytes(data), Time::Now(), kSmallSize)); 505 data[0] = 'd'; 506 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2, 507 new base::RefCountedBytes(data), Time::Now(), kLargeSize)); 508 509 // First visit two URLs. 510 URLRow row1(GURL("http://www.google.com/")); 511 row1.set_visit_count(2); 512 row1.set_typed_count(1); 513 row1.set_last_visit(Time::Now()); 514 backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1); 515 516 URLRow row2(GURL("http://news.google.com/")); 517 row2.set_visit_count(1); 518 row2.set_last_visit(Time::Now()); 519 backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2); 520 521 URLRows rows; 522 rows.push_back(row2); // Reversed order for the same reason as favicons. 523 rows.push_back(row1); 524 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 525 526 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL); 527 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL); 528 529 // Get the two visits for the URLs we just added. 530 VisitVector visits; 531 backend_->db_->GetVisitsForURL(row1_id, &visits); 532 ASSERT_EQ(1U, visits.size()); 533 534 visits.clear(); 535 backend_->db_->GetVisitsForURL(row2_id, &visits); 536 ASSERT_EQ(1U, visits.size()); 537 538 // The in-memory backend should have been set and it should have gotten the 539 // typed URL. 540 ASSERT_TRUE(mem_backend_.get()); 541 URLRow outrow1; 542 EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL)); 543 544 // Star row1. 545 history_client_.AddBookmark(row1.url()); 546 547 // Now finally clear all history. 548 ClearBroadcastedNotifications(); 549 backend_->DeleteAllHistory(); 550 551 // The first URL should be preserved but the time should be cleared. 552 EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1)); 553 EXPECT_EQ(row1.url(), outrow1.url()); 554 EXPECT_EQ(0, outrow1.visit_count()); 555 EXPECT_EQ(0, outrow1.typed_count()); 556 EXPECT_TRUE(Time() == outrow1.last_visit()); 557 558 // The second row should be deleted. 559 URLRow outrow2; 560 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2)); 561 562 // All visits should be deleted for both URLs. 563 VisitVector all_visits; 564 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits); 565 ASSERT_EQ(0U, all_visits.size()); 566 567 // We should have a favicon and favicon bitmaps for the first URL only. We 568 // look them up by favicon URL since the IDs may have changed. 569 favicon_base::FaviconID out_favicon1 = 570 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 571 favicon_url1, favicon_base::FAVICON, NULL); 572 EXPECT_TRUE(out_favicon1); 573 574 std::vector<FaviconBitmap> favicon_bitmaps; 575 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps( 576 out_favicon1, &favicon_bitmaps)); 577 ASSERT_EQ(2u, favicon_bitmaps.size()); 578 579 FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0]; 580 FaviconBitmap favicon_bitmap2 = favicon_bitmaps[1]; 581 582 // Favicon bitmaps do not need to be in particular order. 583 if (favicon_bitmap1.pixel_size == kLargeSize) { 584 FaviconBitmap tmp_favicon_bitmap = favicon_bitmap1; 585 favicon_bitmap1 = favicon_bitmap2; 586 favicon_bitmap2 = tmp_favicon_bitmap; 587 } 588 589 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap1.bitmap_data)); 590 EXPECT_EQ(kSmallSize, favicon_bitmap1.pixel_size); 591 592 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap2.bitmap_data)); 593 EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size); 594 595 favicon_base::FaviconID out_favicon2 = 596 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 597 favicon_url2, favicon_base::FAVICON, NULL); 598 EXPECT_FALSE(out_favicon2) << "Favicon not deleted"; 599 600 // The remaining URL should still reference the same favicon, even if its 601 // ID has changed. 602 std::vector<IconMapping> mappings; 603 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 604 outrow1.url(), favicon_base::FAVICON, &mappings)); 605 EXPECT_EQ(1u, mappings.size()); 606 EXPECT_EQ(out_favicon1, mappings[0].icon_id); 607 608 // The first URL should still be bookmarked. 609 EXPECT_TRUE(history_client_.IsBookmarked(row1.url())); 610 611 // Check that we fire the notification about all history having been deleted. 612 ASSERT_EQ(1u, broadcasted_notifications().size()); 613 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_DELETED, 614 broadcasted_notifications()[0].first); 615 const URLsDeletedDetails* details = static_cast<const URLsDeletedDetails*>( 616 broadcasted_notifications()[0].second); 617 EXPECT_TRUE(details->all_history); 618 EXPECT_FALSE(details->expired); 619 } 620 621 // Checks that adding a visit, then calling DeleteAll, and then trying to add 622 // data for the visited page works. This can happen when clearing the history 623 // immediately after visiting a page. 624 TEST_F(HistoryBackendTest, DeleteAllThenAddData) { 625 ASSERT_TRUE(backend_.get()); 626 627 Time visit_time = Time::Now(); 628 GURL url("http://www.google.com/"); 629 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(), 630 history::RedirectList(), 631 ui::PAGE_TRANSITION_KEYWORD_GENERATED, 632 history::SOURCE_BROWSED, false); 633 backend_->AddPage(request); 634 635 // Check that a row was added. 636 URLRow outrow; 637 EXPECT_TRUE(backend_->db_->GetRowForURL(url, &outrow)); 638 639 // Check that the visit was added. 640 VisitVector all_visits; 641 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits); 642 ASSERT_EQ(1U, all_visits.size()); 643 644 // Clear all history. 645 backend_->DeleteAllHistory(); 646 647 // The row should be deleted. 648 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow)); 649 650 // The visit should be deleted. 651 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits); 652 ASSERT_EQ(0U, all_visits.size()); 653 654 // Try and set the title. 655 backend_->SetPageTitle(url, base::UTF8ToUTF16("Title")); 656 657 // The row should still be deleted. 658 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow)); 659 660 // The visit should still be deleted. 661 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits); 662 ASSERT_EQ(0U, all_visits.size()); 663 } 664 665 TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) { 666 GURL favicon_url1("http://www.google.com/favicon.ico"); 667 GURL favicon_url2("http://news.google.com/favicon.ico"); 668 669 std::vector<unsigned char> data; 670 data.push_back('1'); 671 favicon_base::FaviconID favicon1 = 672 backend_->thumbnail_db_->AddFavicon(favicon_url1, 673 favicon_base::FAVICON, 674 new base::RefCountedBytes(data), 675 Time::Now(), 676 gfx::Size()); 677 678 data[0] = '2'; 679 favicon_base::FaviconID favicon2 = 680 backend_->thumbnail_db_->AddFavicon(favicon_url2, 681 favicon_base::FAVICON, 682 new base::RefCountedBytes(data), 683 Time::Now(), 684 gfx::Size()); 685 686 // First visit two URLs. 687 URLRow row1(GURL("http://www.google.com/")); 688 row1.set_visit_count(2); 689 row1.set_typed_count(1); 690 row1.set_last_visit(Time::Now()); 691 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1)); 692 693 URLRow row2(GURL("http://news.google.com/")); 694 row2.set_visit_count(1); 695 row2.set_last_visit(Time::Now()); 696 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2)); 697 698 URLRows rows; 699 rows.push_back(row2); // Reversed order for the same reason as favicons. 700 rows.push_back(row1); 701 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 702 703 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL); 704 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL); 705 706 // Star the two URLs. 707 history_client_.AddBookmark(row1.url()); 708 history_client_.AddBookmark(row2.url()); 709 710 // Delete url 2. Because url 2 is starred this won't delete the URL, only 711 // the visits. 712 backend_->expirer_.DeleteURL(row2.url()); 713 714 // Make sure url 2 is still valid, but has no visits. 715 URLRow tmp_url_row; 716 EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL)); 717 VisitVector visits; 718 backend_->db_->GetVisitsForURL(row2_id, &visits); 719 EXPECT_EQ(0U, visits.size()); 720 // The favicon should still be valid. 721 EXPECT_EQ(favicon2, 722 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 723 favicon_url2, favicon_base::FAVICON, NULL)); 724 725 // Unstar row2. 726 history_client_.DelBookmark(row2.url()); 727 728 // Tell the backend it was unstarred. We have to explicitly do this as 729 // BookmarkModel isn't wired up to the backend during testing. 730 std::set<GURL> unstarred_urls; 731 unstarred_urls.insert(row2.url()); 732 backend_->URLsNoLongerBookmarked(unstarred_urls); 733 734 // The URL should no longer exist. 735 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row)); 736 // And the favicon should be deleted. 737 EXPECT_EQ(0, 738 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 739 favicon_url2, favicon_base::FAVICON, NULL)); 740 741 // Unstar row 1. 742 history_client_.DelBookmark(row1.url()); 743 744 // Tell the backend it was unstarred. We have to explicitly do this as 745 // BookmarkModel isn't wired up to the backend during testing. 746 unstarred_urls.clear(); 747 unstarred_urls.insert(row1.url()); 748 backend_->URLsNoLongerBookmarked(unstarred_urls); 749 750 // The URL should still exist (because there were visits). 751 EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL)); 752 753 // There should still be visits. 754 visits.clear(); 755 backend_->db_->GetVisitsForURL(row1_id, &visits); 756 EXPECT_EQ(1U, visits.size()); 757 758 // The favicon should still be valid. 759 EXPECT_EQ(favicon1, 760 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 761 favicon_url1, favicon_base::FAVICON, NULL)); 762 } 763 764 // Tests a handful of assertions for a navigation with a type of 765 // KEYWORD_GENERATED. 766 TEST_F(HistoryBackendTest, KeywordGenerated) { 767 ASSERT_TRUE(backend_.get()); 768 769 GURL url("http://google.com"); 770 771 Time visit_time = Time::Now() - base::TimeDelta::FromDays(1); 772 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(), 773 history::RedirectList(), 774 ui::PAGE_TRANSITION_KEYWORD_GENERATED, 775 history::SOURCE_BROWSED, false); 776 backend_->AddPage(request); 777 778 // A row should have been added for the url. 779 URLRow row; 780 URLID url_id = backend_->db()->GetRowForURL(url, &row); 781 ASSERT_NE(0, url_id); 782 783 // The typed count should be 1. 784 ASSERT_EQ(1, row.typed_count()); 785 786 // KEYWORD_GENERATED urls should not be added to the segment db. 787 std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url); 788 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name)); 789 790 // One visit should be added. 791 VisitVector visits; 792 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits)); 793 EXPECT_EQ(1U, visits.size()); 794 795 // But no visible visits. 796 visits.clear(); 797 QueryOptions query_options; 798 query_options.max_count = 1; 799 backend_->db()->GetVisibleVisitsInRange(query_options, &visits); 800 EXPECT_TRUE(visits.empty()); 801 802 // Expire the visits. 803 std::set<GURL> restrict_urls; 804 backend_->expire_backend()->ExpireHistoryBetween(restrict_urls, 805 visit_time, Time::Now()); 806 807 // The visit should have been nuked. 808 visits.clear(); 809 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits)); 810 EXPECT_TRUE(visits.empty()); 811 812 // As well as the url. 813 ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row)); 814 } 815 816 TEST_F(HistoryBackendTest, ClientRedirect) { 817 ASSERT_TRUE(backend_.get()); 818 819 int transition1; 820 int transition2; 821 822 // Initial transition to page A. 823 GURL url_a("http://google.com/a"); 824 AddClientRedirect(GURL(), url_a, false, base::Time(), 825 &transition1, &transition2); 826 EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END); 827 828 // User initiated redirect to page B. 829 GURL url_b("http://google.com/b"); 830 AddClientRedirect(url_a, url_b, false, base::Time(), 831 &transition1, &transition2); 832 EXPECT_TRUE(transition1 & ui::PAGE_TRANSITION_CHAIN_END); 833 EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END); 834 835 // Non-user initiated redirect to page C. 836 GURL url_c("http://google.com/c"); 837 AddClientRedirect(url_b, url_c, true, base::Time(), 838 &transition1, &transition2); 839 EXPECT_FALSE(transition1 & ui::PAGE_TRANSITION_CHAIN_END); 840 EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END); 841 } 842 843 TEST_F(HistoryBackendTest, AddPagesWithDetails) { 844 ASSERT_TRUE(backend_.get()); 845 846 // Import one non-typed URL, and two recent and one expired typed URLs. 847 URLRow row1(GURL("https://news.google.com/")); 848 row1.set_visit_count(1); 849 row1.set_last_visit(Time::Now()); 850 URLRow row2(GURL("https://www.google.com/")); 851 row2.set_typed_count(1); 852 row2.set_last_visit(Time::Now()); 853 URLRow row3(GURL("https://mail.google.com/")); 854 row3.set_visit_count(1); 855 row3.set_typed_count(1); 856 row3.set_last_visit(Time::Now() - base::TimeDelta::FromDays(7 - 1)); 857 URLRow row4(GURL("https://maps.google.com/")); 858 row4.set_visit_count(1); 859 row4.set_typed_count(1); 860 row4.set_last_visit(Time::Now() - base::TimeDelta::FromDays(365 + 2)); 861 862 URLRows rows; 863 rows.push_back(row1); 864 rows.push_back(row2); 865 rows.push_back(row3); 866 rows.push_back(row4); 867 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 868 869 // Verify that recent URLs have ended up in the main |db_|, while the already 870 // expired URL has been ignored. 871 URLRow stored_row1, stored_row2, stored_row3, stored_row4; 872 EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1)); 873 EXPECT_NE(0, backend_->db_->GetRowForURL(row2.url(), &stored_row2)); 874 EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3)); 875 EXPECT_EQ(0, backend_->db_->GetRowForURL(row4.url(), &stored_row4)); 876 877 // Ensure that a notification was fired for both typed and non-typed URLs. 878 // Further verify that the IDs in the notification are set to those that are 879 // in effect in the main database. The InMemoryHistoryBackend relies on this 880 // for caching. 881 ASSERT_EQ(1u, broadcasted_notifications().size()); 882 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 883 broadcasted_notifications()[0].first); 884 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>( 885 broadcasted_notifications()[0].second); 886 EXPECT_EQ(3u, details->changed_urls.size()); 887 888 URLRows::const_iterator it_row1 = std::find_if( 889 details->changed_urls.begin(), 890 details->changed_urls.end(), 891 history::URLRow::URLRowHasURL(row1.url())); 892 ASSERT_NE(details->changed_urls.end(), it_row1); 893 EXPECT_EQ(stored_row1.id(), it_row1->id()); 894 895 URLRows::const_iterator it_row2 = std::find_if( 896 details->changed_urls.begin(), 897 details->changed_urls.end(), 898 history::URLRow::URLRowHasURL(row2.url())); 899 ASSERT_NE(details->changed_urls.end(), it_row2); 900 EXPECT_EQ(stored_row2.id(), it_row2->id()); 901 902 URLRows::const_iterator it_row3 = std::find_if( 903 details->changed_urls.begin(), 904 details->changed_urls.end(), 905 history::URLRow::URLRowHasURL(row3.url())); 906 ASSERT_NE(details->changed_urls.end(), it_row3); 907 EXPECT_EQ(stored_row3.id(), it_row3->id()); 908 } 909 910 TEST_F(HistoryBackendTest, UpdateURLs) { 911 ASSERT_TRUE(backend_.get()); 912 913 // Add three pages directly to the database. 914 URLRow row1(GURL("https://news.google.com/")); 915 row1.set_visit_count(1); 916 row1.set_last_visit(Time::Now()); 917 URLRow row2(GURL("https://maps.google.com/")); 918 row2.set_visit_count(2); 919 row2.set_last_visit(Time::Now()); 920 URLRow row3(GURL("https://www.google.com/")); 921 row3.set_visit_count(3); 922 row3.set_last_visit(Time::Now()); 923 924 backend_->db_->AddURL(row1); 925 backend_->db_->AddURL(row2); 926 backend_->db_->AddURL(row3); 927 928 // Now create changed versions of all URLRows by incrementing their visit 929 // counts, and in the meantime, also delete the second row from the database. 930 URLRow altered_row1, altered_row2, altered_row3; 931 backend_->db_->GetRowForURL(row1.url(), &altered_row1); 932 altered_row1.set_visit_count(42); 933 backend_->db_->GetRowForURL(row2.url(), &altered_row2); 934 altered_row2.set_visit_count(43); 935 backend_->db_->GetRowForURL(row3.url(), &altered_row3); 936 altered_row3.set_visit_count(44); 937 938 backend_->db_->DeleteURLRow(altered_row2.id()); 939 940 // Now try to update all three rows at once. The change to the second URLRow 941 // should be ignored, as it is no longer present in the DB. 942 URLRows rows; 943 rows.push_back(altered_row1); 944 rows.push_back(altered_row2); 945 rows.push_back(altered_row3); 946 EXPECT_EQ(2u, backend_->UpdateURLs(rows)); 947 948 URLRow stored_row1, stored_row3; 949 EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1)); 950 EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3)); 951 EXPECT_EQ(altered_row1.visit_count(), stored_row1.visit_count()); 952 EXPECT_EQ(altered_row3.visit_count(), stored_row3.visit_count()); 953 954 // Ensure that a notification was fired, and further verify that the IDs in 955 // the notification are set to those that are in effect in the main database. 956 // The InMemoryHistoryBackend relies on this for caching. 957 ASSERT_EQ(1u, broadcasted_notifications().size()); 958 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 959 broadcasted_notifications()[0].first); 960 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>( 961 broadcasted_notifications()[0].second); 962 EXPECT_EQ(2u, details->changed_urls.size()); 963 964 URLRows::const_iterator it_row1 = 965 std::find_if(details->changed_urls.begin(), 966 details->changed_urls.end(), 967 history::URLRow::URLRowHasURL(row1.url())); 968 ASSERT_NE(details->changed_urls.end(), it_row1); 969 EXPECT_EQ(altered_row1.id(), it_row1->id()); 970 EXPECT_EQ(altered_row1.visit_count(), it_row1->visit_count()); 971 972 URLRows::const_iterator it_row3 = 973 std::find_if(details->changed_urls.begin(), 974 details->changed_urls.end(), 975 history::URLRow::URLRowHasURL(row3.url())); 976 ASSERT_NE(details->changed_urls.end(), it_row3); 977 EXPECT_EQ(altered_row3.id(), it_row3->id()); 978 EXPECT_EQ(altered_row3.visit_count(), it_row3->visit_count()); 979 } 980 981 // This verifies that a notification is fired. In-depth testing of logic should 982 // be done in HistoryTest.SetTitle. 983 TEST_F(HistoryBackendTest, SetPageTitleFiresNotificationWithCorrectDetails) { 984 const char kTestUrlTitle[] = "Google Search"; 985 986 ASSERT_TRUE(backend_.get()); 987 988 // Add two pages, then change the title of the second one. 989 URLRow row1(GURL("https://news.google.com/")); 990 row1.set_typed_count(1); 991 row1.set_last_visit(Time::Now()); 992 URLRow row2(GURL("https://www.google.com/")); 993 row2.set_visit_count(2); 994 row2.set_last_visit(Time::Now()); 995 996 URLRows rows; 997 rows.push_back(row1); 998 rows.push_back(row2); 999 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 1000 1001 ClearBroadcastedNotifications(); 1002 backend_->SetPageTitle(row2.url(), base::UTF8ToUTF16(kTestUrlTitle)); 1003 1004 // Ensure that a notification was fired, and further verify that the IDs in 1005 // the notification are set to those that are in effect in the main database. 1006 // The InMemoryHistoryBackend relies on this for caching. 1007 URLRow stored_row2; 1008 EXPECT_TRUE(backend_->GetURL(row2.url(), &stored_row2)); 1009 ASSERT_EQ(1u, broadcasted_notifications().size()); 1010 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 1011 broadcasted_notifications()[0].first); 1012 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>( 1013 broadcasted_notifications()[0].second); 1014 ASSERT_EQ(1u, details->changed_urls.size()); 1015 EXPECT_EQ(base::UTF8ToUTF16(kTestUrlTitle), details->changed_urls[0].title()); 1016 EXPECT_EQ(stored_row2.id(), details->changed_urls[0].id()); 1017 } 1018 1019 // There's no importer on Android. 1020 #if !defined(OS_ANDROID) 1021 TEST_F(HistoryBackendTest, ImportedFaviconsTest) { 1022 // Setup test data - two Urls in the history, one with favicon assigned and 1023 // one without. 1024 GURL favicon_url1("http://www.google.com/favicon.ico"); 1025 std::vector<unsigned char> data; 1026 data.push_back('1'); 1027 favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon( 1028 favicon_url1, 1029 favicon_base::FAVICON, 1030 base::RefCountedBytes::TakeVector(&data), 1031 Time::Now(), 1032 gfx::Size()); 1033 URLRow row1(GURL("http://www.google.com/")); 1034 row1.set_visit_count(1); 1035 row1.set_last_visit(Time::Now()); 1036 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1)); 1037 1038 URLRow row2(GURL("http://news.google.com/")); 1039 row2.set_visit_count(1); 1040 row2.set_last_visit(Time::Now()); 1041 URLRows rows; 1042 rows.push_back(row1); 1043 rows.push_back(row2); 1044 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 1045 URLRow url_row1, url_row2; 1046 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0); 1047 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0); 1048 EXPECT_EQ(1u, NumIconMappingsForPageURL(row1.url(), favicon_base::FAVICON)); 1049 EXPECT_EQ(0u, NumIconMappingsForPageURL(row2.url(), favicon_base::FAVICON)); 1050 1051 // Now provide one imported favicon for both URLs already in the registry. 1052 // The new favicon should only be used with the URL that doesn't already have 1053 // a favicon. 1054 std::vector<ImportedFaviconUsage> favicons; 1055 ImportedFaviconUsage favicon; 1056 favicon.favicon_url = GURL("http://news.google.com/favicon.ico"); 1057 favicon.png_data.push_back('2'); 1058 favicon.urls.insert(row1.url()); 1059 favicon.urls.insert(row2.url()); 1060 favicons.push_back(favicon); 1061 backend_->SetImportedFavicons(favicons); 1062 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0); 1063 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0); 1064 1065 std::vector<IconMapping> mappings; 1066 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1067 row1.url(), favicon_base::FAVICON, &mappings)); 1068 EXPECT_EQ(1u, mappings.size()); 1069 EXPECT_EQ(favicon1, mappings[0].icon_id); 1070 EXPECT_EQ(favicon_url1, mappings[0].icon_url); 1071 1072 mappings.clear(); 1073 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1074 row2.url(), favicon_base::FAVICON, &mappings)); 1075 EXPECT_EQ(1u, mappings.size()); 1076 EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url); 1077 1078 // A URL should not be added to history (to store favicon), if 1079 // the URL is not bookmarked. 1080 GURL url3("http://mail.google.com"); 1081 favicons.clear(); 1082 favicon.favicon_url = GURL("http://mail.google.com/favicon.ico"); 1083 favicon.png_data.push_back('3'); 1084 favicon.urls.insert(url3); 1085 favicons.push_back(favicon); 1086 backend_->SetImportedFavicons(favicons); 1087 URLRow url_row3; 1088 EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0); 1089 1090 // If the URL is bookmarked, it should get added to history with 0 visits. 1091 history_client_.AddBookmark(url3); 1092 backend_->SetImportedFavicons(favicons); 1093 EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0); 1094 EXPECT_TRUE(url_row3.visit_count() == 0); 1095 } 1096 #endif // !defined(OS_ANDROID) 1097 1098 TEST_F(HistoryBackendTest, StripUsernamePasswordTest) { 1099 ASSERT_TRUE(backend_.get()); 1100 1101 GURL url("http://anyuser:anypass@www.google.com"); 1102 GURL stripped_url("http://www.google.com"); 1103 1104 // Clear all history. 1105 backend_->DeleteAllHistory(); 1106 1107 // Visit the url with username, password. 1108 backend_->AddPageVisit(url, base::Time::Now(), 0, 1109 ui::PageTransitionFromInt( 1110 ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)), 1111 history::SOURCE_BROWSED); 1112 1113 // Fetch the row information about stripped url from history db. 1114 VisitVector visits; 1115 URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL); 1116 backend_->db_->GetVisitsForURL(row_id, &visits); 1117 1118 // Check if stripped url is stored in database. 1119 ASSERT_EQ(1U, visits.size()); 1120 } 1121 1122 TEST_F(HistoryBackendTest, AddPageVisitSource) { 1123 ASSERT_TRUE(backend_.get()); 1124 1125 GURL url("http://www.google.com"); 1126 1127 // Clear all history. 1128 backend_->DeleteAllHistory(); 1129 1130 // Assume visiting the url from an externsion. 1131 backend_->AddPageVisit( 1132 url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED, 1133 history::SOURCE_EXTENSION); 1134 // Assume the url is imported from Firefox. 1135 backend_->AddPageVisit(url, base::Time::Now(), 0, 1136 ui::PAGE_TRANSITION_TYPED, 1137 history::SOURCE_FIREFOX_IMPORTED); 1138 // Assume this url is also synced. 1139 backend_->AddPageVisit(url, base::Time::Now(), 0, 1140 ui::PAGE_TRANSITION_TYPED, 1141 history::SOURCE_SYNCED); 1142 1143 // Fetch the row information about the url from history db. 1144 VisitVector visits; 1145 URLID row_id = backend_->db_->GetRowForURL(url, NULL); 1146 backend_->db_->GetVisitsForURL(row_id, &visits); 1147 1148 // Check if all the visits to the url are stored in database. 1149 ASSERT_EQ(3U, visits.size()); 1150 VisitSourceMap visit_sources; 1151 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources)); 1152 ASSERT_EQ(3U, visit_sources.size()); 1153 int sources = 0; 1154 for (int i = 0; i < 3; i++) { 1155 switch (visit_sources[visits[i].visit_id]) { 1156 case history::SOURCE_EXTENSION: 1157 sources |= 0x1; 1158 break; 1159 case history::SOURCE_FIREFOX_IMPORTED: 1160 sources |= 0x2; 1161 break; 1162 case history::SOURCE_SYNCED: 1163 sources |= 0x4; 1164 default: 1165 break; 1166 } 1167 } 1168 EXPECT_EQ(0x7, sources); 1169 } 1170 1171 TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) { 1172 ASSERT_TRUE(backend_.get()); 1173 1174 GURL url("http://www.google.com"); 1175 1176 // Clear all history. 1177 backend_->DeleteAllHistory(); 1178 1179 // Create visit times 1180 base::Time recent_time = base::Time::Now(); 1181 base::TimeDelta visit_age = base::TimeDelta::FromDays(3); 1182 base::Time older_time = recent_time - visit_age; 1183 1184 // Visit the url with recent time. 1185 backend_->AddPageVisit(url, recent_time, 0, 1186 ui::PageTransitionFromInt( 1187 ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)), 1188 history::SOURCE_BROWSED); 1189 1190 // Add to the url a visit with older time (could be syncing from another 1191 // client, etc.). 1192 backend_->AddPageVisit(url, older_time, 0, 1193 ui::PageTransitionFromInt( 1194 ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)), 1195 history::SOURCE_SYNCED); 1196 1197 // Fetch the row information about url from history db. 1198 VisitVector visits; 1199 URLRow row; 1200 URLID row_id = backend_->db_->GetRowForURL(url, &row); 1201 backend_->db_->GetVisitsForURL(row_id, &visits); 1202 1203 // Last visit time should be the most recent time, not the most recently added 1204 // visit. 1205 ASSERT_EQ(2U, visits.size()); 1206 ASSERT_EQ(recent_time, row.last_visit()); 1207 } 1208 1209 TEST_F(HistoryBackendTest, AddPageVisitFiresNotificationWithCorrectDetails) { 1210 ASSERT_TRUE(backend_.get()); 1211 1212 GURL url1("http://www.google.com"); 1213 GURL url2("http://maps.google.com"); 1214 1215 // Clear all history. 1216 backend_->DeleteAllHistory(); 1217 ClearBroadcastedNotifications(); 1218 1219 // Visit two distinct URLs, the second one twice. 1220 backend_->AddPageVisit(url1, base::Time::Now(), 0, 1221 ui::PAGE_TRANSITION_LINK, 1222 history::SOURCE_BROWSED); 1223 for (int i = 0; i < 2; ++i) { 1224 backend_->AddPageVisit(url2, base::Time::Now(), 0, 1225 ui::PAGE_TRANSITION_TYPED, 1226 history::SOURCE_BROWSED); 1227 } 1228 1229 URLRow stored_row1, stored_row2; 1230 EXPECT_NE(0, backend_->db_->GetRowForURL(url1, &stored_row1)); 1231 EXPECT_NE(0, backend_->db_->GetRowForURL(url2, &stored_row2)); 1232 1233 // Expect that NOTIFICATION_HISTORY_URLS_VISITED has been fired 3x, and that 1234 // each time, the URLRows have the correct URLs and IDs set. 1235 ASSERT_EQ(3, num_broadcasted_notifications()); 1236 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URL_VISITED, 1237 broadcasted_notifications()[0].first); 1238 const URLVisitedDetails* details = static_cast<const URLVisitedDetails*>( 1239 broadcasted_notifications()[0].second); 1240 EXPECT_EQ(ui::PAGE_TRANSITION_LINK, 1241 ui::PageTransitionStripQualifier(details->transition)); 1242 EXPECT_EQ(stored_row1.id(), details->row.id()); 1243 EXPECT_EQ(stored_row1.url(), details->row.url()); 1244 1245 // No further checking, this case analogous to the first one. 1246 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URL_VISITED, 1247 broadcasted_notifications()[1].first); 1248 1249 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URL_VISITED, 1250 broadcasted_notifications()[2].first); 1251 details = static_cast<const URLVisitedDetails*>( 1252 broadcasted_notifications()[2].second); 1253 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED, 1254 ui::PageTransitionStripQualifier(details->transition)); 1255 EXPECT_EQ(stored_row2.id(), details->row.id()); 1256 EXPECT_EQ(stored_row2.url(), details->row.url()); 1257 } 1258 1259 TEST_F(HistoryBackendTest, AddPageArgsSource) { 1260 ASSERT_TRUE(backend_.get()); 1261 1262 GURL url("http://testpageargs.com"); 1263 1264 // Assume this page is browsed by user. 1265 HistoryAddPageArgs request1(url, base::Time::Now(), NULL, 0, GURL(), 1266 history::RedirectList(), 1267 ui::PAGE_TRANSITION_KEYWORD_GENERATED, 1268 history::SOURCE_BROWSED, false); 1269 backend_->AddPage(request1); 1270 // Assume this page is synced. 1271 HistoryAddPageArgs request2(url, base::Time::Now(), NULL, 0, GURL(), 1272 history::RedirectList(), 1273 ui::PAGE_TRANSITION_LINK, 1274 history::SOURCE_SYNCED, false); 1275 backend_->AddPage(request2); 1276 // Assume this page is browsed again. 1277 HistoryAddPageArgs request3(url, base::Time::Now(), NULL, 0, GURL(), 1278 history::RedirectList(), 1279 ui::PAGE_TRANSITION_TYPED, 1280 history::SOURCE_BROWSED, false); 1281 backend_->AddPage(request3); 1282 1283 // Three visits should be added with proper sources. 1284 VisitVector visits; 1285 URLRow row; 1286 URLID id = backend_->db()->GetRowForURL(url, &row); 1287 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1288 ASSERT_EQ(3U, visits.size()); 1289 VisitSourceMap visit_sources; 1290 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources)); 1291 ASSERT_EQ(1U, visit_sources.size()); 1292 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second); 1293 } 1294 1295 TEST_F(HistoryBackendTest, AddVisitsSource) { 1296 ASSERT_TRUE(backend_.get()); 1297 1298 GURL url1("http://www.cnn.com"); 1299 std::vector<VisitInfo> visits1, visits2; 1300 visits1.push_back(VisitInfo( 1301 Time::Now() - base::TimeDelta::FromDays(5), 1302 ui::PAGE_TRANSITION_LINK)); 1303 visits1.push_back(VisitInfo( 1304 Time::Now() - base::TimeDelta::FromDays(1), 1305 ui::PAGE_TRANSITION_LINK)); 1306 visits1.push_back(VisitInfo( 1307 Time::Now(), ui::PAGE_TRANSITION_LINK)); 1308 1309 GURL url2("http://www.example.com"); 1310 visits2.push_back(VisitInfo( 1311 Time::Now() - base::TimeDelta::FromDays(10), 1312 ui::PAGE_TRANSITION_LINK)); 1313 visits2.push_back(VisitInfo(Time::Now(), ui::PAGE_TRANSITION_LINK)); 1314 1315 // Clear all history. 1316 backend_->DeleteAllHistory(); 1317 1318 // Add the visits. 1319 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED); 1320 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED); 1321 1322 // Verify the visits were added with their sources. 1323 VisitVector visits; 1324 URLRow row; 1325 URLID id = backend_->db()->GetRowForURL(url1, &row); 1326 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1327 ASSERT_EQ(3U, visits.size()); 1328 VisitSourceMap visit_sources; 1329 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources)); 1330 ASSERT_EQ(3U, visit_sources.size()); 1331 for (int i = 0; i < 3; i++) 1332 EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]); 1333 id = backend_->db()->GetRowForURL(url2, &row); 1334 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1335 ASSERT_EQ(2U, visits.size()); 1336 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources)); 1337 ASSERT_EQ(2U, visit_sources.size()); 1338 for (int i = 0; i < 2; i++) 1339 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]); 1340 } 1341 1342 TEST_F(HistoryBackendTest, GetMostRecentVisits) { 1343 ASSERT_TRUE(backend_.get()); 1344 1345 GURL url1("http://www.cnn.com"); 1346 std::vector<VisitInfo> visits1; 1347 visits1.push_back(VisitInfo( 1348 Time::Now() - base::TimeDelta::FromDays(5), 1349 ui::PAGE_TRANSITION_LINK)); 1350 visits1.push_back(VisitInfo( 1351 Time::Now() - base::TimeDelta::FromDays(1), 1352 ui::PAGE_TRANSITION_LINK)); 1353 visits1.push_back(VisitInfo( 1354 Time::Now(), ui::PAGE_TRANSITION_LINK)); 1355 1356 // Clear all history. 1357 backend_->DeleteAllHistory(); 1358 1359 // Add the visits. 1360 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED); 1361 1362 // Verify the visits were added with their sources. 1363 VisitVector visits; 1364 URLRow row; 1365 URLID id = backend_->db()->GetRowForURL(url1, &row); 1366 ASSERT_TRUE(backend_->db()->GetMostRecentVisitsForURL(id, 1, &visits)); 1367 ASSERT_EQ(1U, visits.size()); 1368 EXPECT_EQ(visits1[2].first, visits[0].visit_time); 1369 } 1370 1371 TEST_F(HistoryBackendTest, RemoveVisitsTransitions) { 1372 ASSERT_TRUE(backend_.get()); 1373 1374 // Clear all history. 1375 backend_->DeleteAllHistory(); 1376 1377 GURL url1("http://www.cnn.com"); 1378 VisitInfo typed_visit( 1379 Time::Now() - base::TimeDelta::FromDays(6), 1380 ui::PAGE_TRANSITION_TYPED); 1381 VisitInfo reload_visit( 1382 Time::Now() - base::TimeDelta::FromDays(5), 1383 ui::PAGE_TRANSITION_RELOAD); 1384 VisitInfo link_visit( 1385 Time::Now() - base::TimeDelta::FromDays(4), 1386 ui::PAGE_TRANSITION_LINK); 1387 std::vector<VisitInfo> visits_to_add; 1388 visits_to_add.push_back(typed_visit); 1389 visits_to_add.push_back(reload_visit); 1390 visits_to_add.push_back(link_visit); 1391 1392 // Add the visits. 1393 backend_->AddVisits(url1, visits_to_add, history::SOURCE_SYNCED); 1394 1395 // Verify that the various counts are what we expect. 1396 VisitVector visits; 1397 URLRow row; 1398 URLID id = backend_->db()->GetRowForURL(url1, &row); 1399 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1400 ASSERT_EQ(3U, visits.size()); 1401 ASSERT_EQ(1, row.typed_count()); 1402 ASSERT_EQ(2, row.visit_count()); 1403 1404 // Now, delete the typed visit and verify that typed_count is updated. 1405 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0]))); 1406 id = backend_->db()->GetRowForURL(url1, &row); 1407 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1408 ASSERT_EQ(2U, visits.size()); 1409 ASSERT_EQ(0, row.typed_count()); 1410 ASSERT_EQ(1, row.visit_count()); 1411 1412 // Delete the reload visit now and verify that none of the counts have 1413 // changed. 1414 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0]))); 1415 id = backend_->db()->GetRowForURL(url1, &row); 1416 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1417 ASSERT_EQ(1U, visits.size()); 1418 ASSERT_EQ(0, row.typed_count()); 1419 ASSERT_EQ(1, row.visit_count()); 1420 1421 // Delete the last visit and verify that we delete the URL. 1422 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0]))); 1423 ASSERT_EQ(0, backend_->db()->GetRowForURL(url1, &row)); 1424 } 1425 1426 TEST_F(HistoryBackendTest, RemoveVisitsSource) { 1427 ASSERT_TRUE(backend_.get()); 1428 1429 GURL url1("http://www.cnn.com"); 1430 std::vector<VisitInfo> visits1, visits2; 1431 visits1.push_back(VisitInfo( 1432 Time::Now() - base::TimeDelta::FromDays(5), 1433 ui::PAGE_TRANSITION_LINK)); 1434 visits1.push_back(VisitInfo(Time::Now(), 1435 ui::PAGE_TRANSITION_LINK)); 1436 1437 GURL url2("http://www.example.com"); 1438 visits2.push_back(VisitInfo( 1439 Time::Now() - base::TimeDelta::FromDays(10), 1440 ui::PAGE_TRANSITION_LINK)); 1441 visits2.push_back(VisitInfo(Time::Now(), ui::PAGE_TRANSITION_LINK)); 1442 1443 // Clear all history. 1444 backend_->DeleteAllHistory(); 1445 1446 // Add the visits. 1447 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED); 1448 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED); 1449 1450 // Verify the visits of url1 were added. 1451 VisitVector visits; 1452 URLRow row; 1453 URLID id = backend_->db()->GetRowForURL(url1, &row); 1454 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1455 ASSERT_EQ(2U, visits.size()); 1456 // Remove these visits. 1457 ASSERT_TRUE(backend_->RemoveVisits(visits)); 1458 1459 // Now check only url2's source in visit_source table. 1460 VisitSourceMap visit_sources; 1461 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources)); 1462 ASSERT_EQ(0U, visit_sources.size()); 1463 id = backend_->db()->GetRowForURL(url2, &row); 1464 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits)); 1465 ASSERT_EQ(2U, visits.size()); 1466 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources)); 1467 ASSERT_EQ(2U, visit_sources.size()); 1468 for (int i = 0; i < 2; i++) 1469 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]); 1470 } 1471 1472 // Test for migration of adding visit_source table. 1473 TEST_F(HistoryBackendTest, MigrationVisitSource) { 1474 ASSERT_TRUE(backend_.get()); 1475 backend_->Closing(); 1476 backend_ = NULL; 1477 1478 base::FilePath old_history_path; 1479 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path)); 1480 old_history_path = old_history_path.AppendASCII("History"); 1481 old_history_path = old_history_path.AppendASCII("HistoryNoSource"); 1482 1483 // Copy history database file to current directory so that it will be deleted 1484 // in Teardown. 1485 base::FilePath new_history_path(test_dir()); 1486 base::DeleteFile(new_history_path, true); 1487 base::CreateDirectory(new_history_path); 1488 base::FilePath new_history_file = 1489 new_history_path.Append(chrome::kHistoryFilename); 1490 ASSERT_TRUE(base::CopyFile(old_history_path, new_history_file)); 1491 1492 backend_ = new HistoryBackend( 1493 new_history_path, new HistoryBackendTestDelegate(this), &history_client_); 1494 backend_->Init(std::string(), false); 1495 backend_->Closing(); 1496 backend_ = NULL; 1497 1498 // Now the database should already be migrated. 1499 // Check version first. 1500 int cur_version = HistoryDatabase::GetCurrentVersion(); 1501 sql::Connection db; 1502 ASSERT_TRUE(db.Open(new_history_file)); 1503 sql::Statement s(db.GetUniqueStatement( 1504 "SELECT value FROM meta WHERE key = 'version'")); 1505 ASSERT_TRUE(s.Step()); 1506 int file_version = s.ColumnInt(0); 1507 EXPECT_EQ(cur_version, file_version); 1508 1509 // Check visit_source table is created and empty. 1510 s.Assign(db.GetUniqueStatement( 1511 "SELECT name FROM sqlite_master WHERE name=\"visit_source\"")); 1512 ASSERT_TRUE(s.Step()); 1513 s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10")); 1514 EXPECT_FALSE(s.Step()); 1515 } 1516 1517 // Test that SetFaviconMappingsForPageAndRedirects correctly updates icon 1518 // mappings based on redirects, icon URLs and icon types. 1519 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirects) { 1520 // Init recent_redirects_ 1521 const GURL url1("http://www.google.com"); 1522 const GURL url2("http://www.google.com/m"); 1523 URLRow url_info1(url1); 1524 url_info1.set_visit_count(0); 1525 url_info1.set_typed_count(0); 1526 url_info1.set_last_visit(base::Time()); 1527 url_info1.set_hidden(false); 1528 backend_->db_->AddURL(url_info1); 1529 1530 URLRow url_info2(url2); 1531 url_info2.set_visit_count(0); 1532 url_info2.set_typed_count(0); 1533 url_info2.set_last_visit(base::Time()); 1534 url_info2.set_hidden(false); 1535 backend_->db_->AddURL(url_info2); 1536 1537 history::RedirectList redirects; 1538 redirects.push_back(url2); 1539 redirects.push_back(url1); 1540 backend_->recent_redirects_.Put(url1, redirects); 1541 1542 const GURL icon_url1("http://www.google.com/icon"); 1543 const GURL icon_url2("http://www.google.com/icon2"); 1544 std::vector<SkBitmap> bitmaps; 1545 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 1546 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize)); 1547 1548 // Add a favicon. 1549 backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url1, bitmaps); 1550 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON)); 1551 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON)); 1552 1553 // Add one touch_icon 1554 backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps); 1555 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON)); 1556 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::TOUCH_ICON)); 1557 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON)); 1558 1559 // Add one TOUCH_PRECOMPOSED_ICON 1560 backend_->SetFavicons( 1561 url1, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_url1, bitmaps); 1562 // The touch_icon was replaced. 1563 EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON)); 1564 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON)); 1565 EXPECT_EQ( 1566 1u, 1567 NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON)); 1568 EXPECT_EQ( 1569 1u, 1570 NumIconMappingsForPageURL(url2, favicon_base::TOUCH_PRECOMPOSED_ICON)); 1571 1572 // Add a touch_icon. 1573 backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps); 1574 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON)); 1575 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON)); 1576 // The TOUCH_PRECOMPOSED_ICON was replaced. 1577 EXPECT_EQ( 1578 0u, 1579 NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON)); 1580 1581 // Add a different favicon. 1582 backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url2, bitmaps); 1583 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON)); 1584 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON)); 1585 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON)); 1586 } 1587 1588 // Test that there is no churn in icon mappings from calling 1589 // SetFavicons() twice with the same |bitmaps| parameter. 1590 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) { 1591 const GURL url("http://www.google.com/"); 1592 const GURL icon_url("http://www.google.com/icon"); 1593 1594 std::vector<SkBitmap> bitmaps; 1595 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 1596 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize)); 1597 1598 backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps); 1599 1600 std::vector<IconMapping> icon_mappings; 1601 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1602 url, favicon_base::FAVICON, &icon_mappings)); 1603 EXPECT_EQ(1u, icon_mappings.size()); 1604 IconMappingID mapping_id = icon_mappings[0].mapping_id; 1605 1606 backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps); 1607 1608 icon_mappings.clear(); 1609 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1610 url, favicon_base::FAVICON, &icon_mappings)); 1611 EXPECT_EQ(1u, icon_mappings.size()); 1612 1613 // The same row in the icon_mapping table should be used for the mapping as 1614 // before. 1615 EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id); 1616 } 1617 1618 // Test that calling SetFavicons() with FaviconBitmapData of different pixel 1619 // sizes than the initially passed in FaviconBitmapData deletes the no longer 1620 // used favicon bitmaps. 1621 TEST_F(HistoryBackendTest, SetFaviconsDeleteBitmaps) { 1622 const GURL page_url("http://www.google.com/"); 1623 const GURL icon_url("http://www.google.com/icon"); 1624 1625 std::vector<SkBitmap> bitmaps; 1626 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 1627 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize)); 1628 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 1629 1630 // Test initial state. 1631 std::vector<IconMapping> icon_mappings; 1632 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url, &icon_mappings)); 1633 EXPECT_EQ(1u, icon_mappings.size()); 1634 EXPECT_EQ(icon_url, icon_mappings[0].icon_url); 1635 EXPECT_EQ(favicon_base::FAVICON, icon_mappings[0].icon_type); 1636 favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id; 1637 1638 std::vector<FaviconBitmap> favicon_bitmaps; 1639 EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps)); 1640 EXPECT_EQ(2u, favicon_bitmaps.size()); 1641 FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id; 1642 EXPECT_NE(0, small_bitmap_id); 1643 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmaps[0].bitmap_data)); 1644 EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size); 1645 FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id; 1646 EXPECT_NE(0, large_bitmap_id); 1647 EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, favicon_bitmaps[1].bitmap_data)); 1648 EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size); 1649 1650 // Call SetFavicons() with bitmap data for only the large bitmap. Check that 1651 // the small bitmap is in fact deleted. 1652 bitmaps.clear(); 1653 bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kLargeEdgeSize)); 1654 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 1655 1656 scoped_refptr<base::RefCountedMemory> bitmap_data_out; 1657 gfx::Size pixel_size_out; 1658 EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(small_bitmap_id, 1659 NULL, &bitmap_data_out, &pixel_size_out)); 1660 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id, 1661 NULL, &bitmap_data_out, &pixel_size_out)); 1662 EXPECT_TRUE(BitmapColorEqual(SK_ColorWHITE, bitmap_data_out)); 1663 EXPECT_EQ(kLargeSize, pixel_size_out); 1664 1665 icon_mappings.clear(); 1666 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1667 &icon_mappings)); 1668 EXPECT_EQ(1u, icon_mappings.size()); 1669 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id); 1670 1671 // Notifications should have been broadcast for each call to SetFavicons(). 1672 EXPECT_EQ(2, favicon_changed_notifications()); 1673 } 1674 1675 // Test updating a single favicon bitmap's data via SetFavicons. 1676 TEST_F(HistoryBackendTest, SetFaviconsReplaceBitmapData) { 1677 const GURL page_url("http://www.google.com/"); 1678 const GURL icon_url("http://www.google.com/icon"); 1679 std::vector<SkBitmap> bitmaps; 1680 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 1681 1682 // Add bitmap to the database. 1683 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 1684 1685 favicon_base::FaviconID original_favicon_id = 1686 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 1687 icon_url, favicon_base::FAVICON, NULL); 1688 EXPECT_NE(0, original_favicon_id); 1689 FaviconBitmap original_favicon_bitmap; 1690 EXPECT_TRUE( 1691 GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap)); 1692 EXPECT_TRUE( 1693 BitmapColorEqual(SK_ColorBLUE, original_favicon_bitmap.bitmap_data)); 1694 1695 EXPECT_EQ(1, favicon_changed_notifications()); 1696 1697 // Call SetFavicons() with completely identical data. 1698 bitmaps[0] = CreateBitmap(SK_ColorBLUE, kSmallEdgeSize); 1699 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 1700 1701 favicon_base::FaviconID updated_favicon_id = 1702 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 1703 icon_url, favicon_base::FAVICON, NULL); 1704 EXPECT_NE(0, updated_favicon_id); 1705 FaviconBitmap updated_favicon_bitmap; 1706 EXPECT_TRUE( 1707 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap)); 1708 EXPECT_TRUE( 1709 BitmapColorEqual(SK_ColorBLUE, updated_favicon_bitmap.bitmap_data)); 1710 1711 // Because the bitmap data is byte equivalent, no notifications should have 1712 // been broadcasted. 1713 EXPECT_EQ(1, favicon_changed_notifications()); 1714 1715 // Call SetFavicons() with a different bitmap of the same size. 1716 bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize); 1717 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 1718 1719 updated_favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 1720 icon_url, favicon_base::FAVICON, NULL); 1721 EXPECT_NE(0, updated_favicon_id); 1722 EXPECT_TRUE( 1723 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap)); 1724 EXPECT_TRUE( 1725 BitmapColorEqual(SK_ColorWHITE, updated_favicon_bitmap.bitmap_data)); 1726 1727 // There should be no churn in FaviconIDs or FaviconBitmapIds even though 1728 // the bitmap data changed. 1729 EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id); 1730 EXPECT_EQ(original_favicon_bitmap.bitmap_id, 1731 updated_favicon_bitmap.bitmap_id); 1732 1733 // A notification should have been broadcasted as the favicon bitmap data has 1734 // changed. 1735 EXPECT_EQ(2, favicon_changed_notifications()); 1736 } 1737 1738 // Test that if two pages share the same FaviconID, changing the favicon for 1739 // one page does not affect the other. 1740 TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) { 1741 GURL icon_url("http://www.google.com/favicon.ico"); 1742 GURL icon_url_new("http://www.google.com/favicon2.ico"); 1743 GURL page_url1("http://www.google.com"); 1744 GURL page_url2("http://www.google.ca"); 1745 std::vector<SkBitmap> bitmaps; 1746 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 1747 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize)); 1748 1749 backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps); 1750 1751 std::vector<GURL> icon_urls; 1752 icon_urls.push_back(icon_url); 1753 1754 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results; 1755 backend_->UpdateFaviconMappingsAndFetch(page_url2, 1756 icon_urls, 1757 favicon_base::FAVICON, 1758 GetEdgeSizesSmallAndLarge(), 1759 &bitmap_results); 1760 1761 // Check that the same FaviconID is mapped to both page URLs. 1762 std::vector<IconMapping> icon_mappings; 1763 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1764 page_url1, &icon_mappings)); 1765 EXPECT_EQ(1u, icon_mappings.size()); 1766 favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id; 1767 EXPECT_NE(0, favicon_id); 1768 1769 icon_mappings.clear(); 1770 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1771 page_url2, &icon_mappings)); 1772 EXPECT_EQ(1u, icon_mappings.size()); 1773 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id); 1774 1775 // Change the icon URL that |page_url1| is mapped to. 1776 bitmaps.clear(); 1777 bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kSmallEdgeSize)); 1778 backend_->SetFavicons( 1779 page_url1, favicon_base::FAVICON, icon_url_new, bitmaps); 1780 1781 // |page_url1| should map to a new FaviconID and have valid bitmap data. 1782 icon_mappings.clear(); 1783 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1784 page_url1, &icon_mappings)); 1785 EXPECT_EQ(1u, icon_mappings.size()); 1786 EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url); 1787 EXPECT_NE(favicon_id, icon_mappings[0].icon_id); 1788 1789 std::vector<FaviconBitmap> favicon_bitmaps; 1790 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps( 1791 icon_mappings[0].icon_id, &favicon_bitmaps)); 1792 EXPECT_EQ(1u, favicon_bitmaps.size()); 1793 1794 // |page_url2| should still map to the same FaviconID and have valid bitmap 1795 // data. 1796 icon_mappings.clear(); 1797 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 1798 page_url2, &icon_mappings)); 1799 EXPECT_EQ(1u, icon_mappings.size()); 1800 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id); 1801 1802 favicon_bitmaps.clear(); 1803 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id, 1804 &favicon_bitmaps)); 1805 EXPECT_EQ(2u, favicon_bitmaps.size()); 1806 1807 // A notification should have been broadcast for each call to SetFavicons() 1808 // and each call to UpdateFaviconMappingsAndFetch(). 1809 EXPECT_EQ(3, favicon_changed_notifications()); 1810 } 1811 1812 // Test that no notifications are broadcast as a result of calling 1813 // UpdateFaviconMappingsAndFetch() for an icon URL which is already 1814 // mapped to the passed in page URL. 1815 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoChange) { 1816 GURL page_url("http://www.google.com"); 1817 GURL icon_url("http://www.google.com/favicon.ico"); 1818 std::vector<SkBitmap> bitmaps; 1819 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 1820 1821 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 1822 1823 favicon_base::FaviconID icon_id = 1824 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 1825 icon_url, favicon_base::FAVICON, NULL); 1826 EXPECT_NE(0, icon_id); 1827 EXPECT_EQ(1, favicon_changed_notifications()); 1828 1829 std::vector<GURL> icon_urls; 1830 icon_urls.push_back(icon_url); 1831 1832 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results; 1833 backend_->UpdateFaviconMappingsAndFetch(page_url, 1834 icon_urls, 1835 favicon_base::FAVICON, 1836 GetEdgeSizesSmallAndLarge(), 1837 &bitmap_results); 1838 1839 EXPECT_EQ(icon_id, 1840 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 1841 icon_url, favicon_base::FAVICON, NULL)); 1842 1843 // No notification should have been broadcast as no icon mapping, favicon, 1844 // or favicon bitmap was updated, added or removed. 1845 EXPECT_EQ(1, favicon_changed_notifications()); 1846 } 1847 1848 // Test repeatedly calling MergeFavicon(). |page_url| is initially not known 1849 // to the database. 1850 TEST_F(HistoryBackendTest, MergeFaviconPageURLNotInDB) { 1851 GURL page_url("http://www.google.com"); 1852 GURL icon_url("http:/www.google.com/favicon.ico"); 1853 1854 std::vector<unsigned char> data; 1855 data.push_back('a'); 1856 scoped_refptr<base::RefCountedBytes> bitmap_data( 1857 new base::RefCountedBytes(data)); 1858 1859 backend_->MergeFavicon( 1860 page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize); 1861 1862 // |page_url| should now be mapped to |icon_url| and the favicon bitmap should 1863 // not be expired. 1864 std::vector<IconMapping> icon_mappings; 1865 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1866 &icon_mappings)); 1867 EXPECT_EQ(1u, icon_mappings.size()); 1868 EXPECT_EQ(icon_url, icon_mappings[0].icon_url); 1869 1870 FaviconBitmap favicon_bitmap; 1871 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap)); 1872 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 1873 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data)); 1874 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 1875 1876 data[0] = 'b'; 1877 bitmap_data = new base::RefCountedBytes(data); 1878 backend_->MergeFavicon( 1879 page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize); 1880 1881 // |page_url| should still have a single favicon bitmap. The bitmap data 1882 // should be updated. 1883 icon_mappings.clear(); 1884 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1885 &icon_mappings)); 1886 EXPECT_EQ(1u, icon_mappings.size()); 1887 EXPECT_EQ(icon_url, icon_mappings[0].icon_url); 1888 1889 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap)); 1890 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 1891 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data)); 1892 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 1893 } 1894 1895 // Test calling MergeFavicon() when |page_url| is known to the database. 1896 TEST_F(HistoryBackendTest, MergeFaviconPageURLInDB) { 1897 GURL page_url("http://www.google.com"); 1898 GURL icon_url1("http:/www.google.com/favicon.ico"); 1899 GURL icon_url2("http://www.google.com/favicon2.ico"); 1900 std::vector<SkBitmap> bitmaps; 1901 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 1902 1903 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps); 1904 1905 // Test initial state. 1906 std::vector<IconMapping> icon_mappings; 1907 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1908 &icon_mappings)); 1909 EXPECT_EQ(1u, icon_mappings.size()); 1910 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url); 1911 1912 FaviconBitmap favicon_bitmap; 1913 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap)); 1914 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 1915 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data)); 1916 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 1917 1918 EXPECT_EQ(1, favicon_changed_notifications()); 1919 1920 // 1) Merge identical favicon bitmap. 1921 std::vector<unsigned char> data; 1922 gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data); 1923 scoped_refptr<base::RefCountedBytes> bitmap_data( 1924 new base::RefCountedBytes(data)); 1925 backend_->MergeFavicon( 1926 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize); 1927 1928 // All the data should stay the same and no notifications should have been 1929 // sent. 1930 icon_mappings.clear(); 1931 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1932 &icon_mappings)); 1933 EXPECT_EQ(1u, icon_mappings.size()); 1934 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url); 1935 1936 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap)); 1937 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 1938 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data)); 1939 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 1940 1941 EXPECT_EQ(1, favicon_changed_notifications()); 1942 1943 // 2) Merge favicon bitmap of the same size. 1944 data.clear(); 1945 data.push_back('b'); 1946 bitmap_data = new base::RefCountedBytes(data); 1947 backend_->MergeFavicon( 1948 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize); 1949 1950 // The small favicon bitmap at |icon_url1| should be overwritten. 1951 icon_mappings.clear(); 1952 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1953 &icon_mappings)); 1954 EXPECT_EQ(1u, icon_mappings.size()); 1955 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url); 1956 1957 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap)); 1958 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 1959 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data)); 1960 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 1961 1962 // 3) Merge favicon for the same icon URL, but a pixel size for which there is 1963 // no favicon bitmap. 1964 data[0] = 'c'; 1965 bitmap_data = new base::RefCountedBytes(data); 1966 backend_->MergeFavicon( 1967 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kTinySize); 1968 1969 // A new favicon bitmap should be created and the preexisting favicon bitmap 1970 // ('b') should be expired. 1971 icon_mappings.clear(); 1972 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1973 &icon_mappings)); 1974 EXPECT_EQ(1u, icon_mappings.size()); 1975 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url); 1976 1977 std::vector<FaviconBitmap> favicon_bitmaps; 1978 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id, 1979 &favicon_bitmaps)); 1980 EXPECT_NE(base::Time(), favicon_bitmaps[0].last_updated); 1981 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data)); 1982 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size); 1983 EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated); 1984 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data)); 1985 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size); 1986 1987 // 4) Merge favicon for an icon URL different from the icon URLs already 1988 // mapped to page URL. 1989 data[0] = 'd'; 1990 bitmap_data = new base::RefCountedBytes(data); 1991 backend_->MergeFavicon( 1992 page_url, icon_url2, favicon_base::FAVICON, bitmap_data, kSmallSize); 1993 1994 // The existing favicon bitmaps should be copied over to the newly created 1995 // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|. 1996 icon_mappings.clear(); 1997 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 1998 &icon_mappings)); 1999 EXPECT_EQ(1u, icon_mappings.size()); 2000 EXPECT_EQ(icon_url2, icon_mappings[0].icon_url); 2001 2002 favicon_bitmaps.clear(); 2003 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id, 2004 &favicon_bitmaps)); 2005 EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated); 2006 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data)); 2007 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size); 2008 // The favicon being merged should take precedence over the preexisting 2009 // favicon bitmaps. 2010 EXPECT_NE(base::Time(), favicon_bitmaps[1].last_updated); 2011 EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data)); 2012 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size); 2013 2014 // A notification should have been broadcast for each call to SetFavicons() 2015 // and MergeFavicon(). 2016 EXPECT_EQ(4, favicon_changed_notifications()); 2017 } 2018 2019 // Test calling MergeFavicon() when |icon_url| is known to the database but not 2020 // mapped to |page_url|. 2021 TEST_F(HistoryBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) { 2022 GURL page_url1("http://www.google.com"); 2023 GURL page_url2("http://news.google.com"); 2024 GURL page_url3("http://maps.google.com"); 2025 GURL icon_url("http:/www.google.com/favicon.ico"); 2026 std::vector<SkBitmap> bitmaps; 2027 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 2028 2029 backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps); 2030 2031 // Test initial state. 2032 std::vector<IconMapping> icon_mappings; 2033 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1, 2034 &icon_mappings)); 2035 EXPECT_EQ(1u, icon_mappings.size()); 2036 EXPECT_EQ(icon_url, icon_mappings[0].icon_url); 2037 2038 FaviconBitmap favicon_bitmap; 2039 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap)); 2040 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 2041 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data)); 2042 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 2043 2044 // 1) Merge in an identical favicon bitmap data but for a different page URL. 2045 std::vector<unsigned char> data; 2046 gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data); 2047 scoped_refptr<base::RefCountedBytes> bitmap_data( 2048 new base::RefCountedBytes(data)); 2049 2050 backend_->MergeFavicon( 2051 page_url2, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize); 2052 2053 favicon_base::FaviconID favicon_id = 2054 backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 2055 icon_url, favicon_base::FAVICON, NULL); 2056 EXPECT_NE(0, favicon_id); 2057 2058 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap)); 2059 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 2060 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data)); 2061 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 2062 2063 // 2) Merging a favicon bitmap with different bitmap data for the same icon 2064 // URL should overwrite the small favicon bitmap at |icon_url|. 2065 data.clear(); 2066 data.push_back('b'); 2067 bitmap_data = new base::RefCountedBytes(data); 2068 backend_->MergeFavicon( 2069 page_url3, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize); 2070 2071 favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL( 2072 icon_url, favicon_base::FAVICON, NULL); 2073 EXPECT_NE(0, favicon_id); 2074 2075 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap)); 2076 EXPECT_NE(base::Time(), favicon_bitmap.last_updated); 2077 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data)); 2078 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size); 2079 2080 // |icon_url| should be mapped to all three page URLs. 2081 icon_mappings.clear(); 2082 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1, 2083 &icon_mappings)); 2084 EXPECT_EQ(1u, icon_mappings.size()); 2085 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id); 2086 2087 icon_mappings.clear(); 2088 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url2, 2089 &icon_mappings)); 2090 EXPECT_EQ(1u, icon_mappings.size()); 2091 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id); 2092 2093 icon_mappings.clear(); 2094 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url3, 2095 &icon_mappings)); 2096 EXPECT_EQ(1u, icon_mappings.size()); 2097 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id); 2098 2099 // A notification should have been broadcast for each call to SetFavicons() 2100 // and MergeFavicon(). 2101 EXPECT_EQ(3, favicon_changed_notifications()); 2102 } 2103 2104 // Test that MergeFavicon() does not add more than 2105 // |kMaxFaviconBitmapsPerIconURL| to a favicon. 2106 TEST_F(HistoryBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) { 2107 GURL page_url("http://www.google.com"); 2108 std::string icon_url_string("http://www.google.com/favicon.ico"); 2109 size_t replace_index = icon_url_string.size() - 1; 2110 2111 std::vector<unsigned char> data; 2112 data.push_back('a'); 2113 scoped_refptr<base::RefCountedMemory> bitmap_data = 2114 base::RefCountedBytes::TakeVector(&data); 2115 2116 int pixel_size = 1; 2117 for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) { 2118 icon_url_string[replace_index] = '0' + i; 2119 GURL icon_url(icon_url_string); 2120 2121 backend_->MergeFavicon(page_url, 2122 icon_url, 2123 favicon_base::FAVICON, 2124 bitmap_data, 2125 gfx::Size(pixel_size, pixel_size)); 2126 ++pixel_size; 2127 } 2128 2129 // There should be a single favicon mapped to |page_url| with exactly 2130 // kMaxFaviconBitmapsPerIconURL favicon bitmaps. 2131 std::vector<IconMapping> icon_mappings; 2132 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, 2133 &icon_mappings)); 2134 EXPECT_EQ(1u, icon_mappings.size()); 2135 std::vector<FaviconBitmap> favicon_bitmaps; 2136 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps( 2137 icon_mappings[0].icon_id, &favicon_bitmaps)); 2138 EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size()); 2139 } 2140 2141 // Tests that the favicon set by MergeFavicon() shows up in the result of 2142 // GetFaviconsForURL(). 2143 TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) { 2144 GURL page_url("http://www.google.com"); 2145 GURL icon_url("http://www.google.com/favicon.ico"); 2146 GURL merged_icon_url("http://wwww.google.com/favicon2.ico"); 2147 std::vector<SkBitmap> bitmaps; 2148 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 2149 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize)); 2150 2151 // Set some preexisting favicons for |page_url|. 2152 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 2153 2154 // Merge small favicon. 2155 std::vector<unsigned char> data; 2156 data.push_back('c'); 2157 scoped_refptr<base::RefCountedBytes> bitmap_data( 2158 new base::RefCountedBytes(data)); 2159 backend_->MergeFavicon(page_url, 2160 merged_icon_url, 2161 favicon_base::FAVICON, 2162 bitmap_data, 2163 kSmallSize); 2164 2165 // Request favicon bitmaps for both 1x and 2x to simulate request done by 2166 // BookmarkModel::GetFavicon(). 2167 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results; 2168 backend_->GetFaviconsForURL(page_url, 2169 favicon_base::FAVICON, 2170 GetEdgeSizesSmallAndLarge(), 2171 &bitmap_results); 2172 2173 EXPECT_EQ(2u, bitmap_results.size()); 2174 const favicon_base::FaviconRawBitmapResult& first_result = bitmap_results[0]; 2175 const favicon_base::FaviconRawBitmapResult& result = 2176 (first_result.pixel_size == kSmallSize) ? first_result 2177 : bitmap_results[1]; 2178 EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data)); 2179 } 2180 2181 // Tests GetFaviconsForURL with icon_types priority, 2182 TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) { 2183 GURL page_url("http://www.google.com"); 2184 GURL icon_url("http://www.google.com/favicon.ico"); 2185 GURL touch_icon_url("http://wwww.google.com/touch_icon.ico"); 2186 2187 std::vector<SkBitmap> favicon_bitmaps; 2188 favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16)); 2189 favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32)); 2190 2191 std::vector<SkBitmap> touch_bitmaps; 2192 touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 64)); 2193 2194 // Set some preexisting favicons for |page_url|. 2195 backend_->SetFavicons( 2196 page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps); 2197 backend_->SetFavicons( 2198 page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps); 2199 2200 favicon_base::FaviconRawBitmapResult result; 2201 std::vector<int> icon_types; 2202 icon_types.push_back(favicon_base::FAVICON); 2203 icon_types.push_back(favicon_base::TOUCH_ICON); 2204 2205 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result); 2206 2207 // Verify the result icon is 32x32 favicon. 2208 EXPECT_EQ(gfx::Size(32, 32), result.pixel_size); 2209 EXPECT_EQ(favicon_base::FAVICON, result.icon_type); 2210 2211 // Change Minimal size to 32x32 and verify the 64x64 touch icon returned. 2212 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result); 2213 EXPECT_EQ(gfx::Size(64, 64), result.pixel_size); 2214 EXPECT_EQ(favicon_base::TOUCH_ICON, result.icon_type); 2215 } 2216 2217 // Test the the first types of icon is returned if its size equal to the 2218 // second types icon. 2219 TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFavicon) { 2220 GURL page_url("http://www.google.com"); 2221 GURL icon_url("http://www.google.com/favicon.ico"); 2222 GURL touch_icon_url("http://wwww.google.com/touch_icon.ico"); 2223 2224 std::vector<SkBitmap> favicon_bitmaps; 2225 favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16)); 2226 favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32)); 2227 2228 std::vector<SkBitmap> touch_bitmaps; 2229 touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 32)); 2230 2231 // Set some preexisting favicons for |page_url|. 2232 backend_->SetFavicons( 2233 page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps); 2234 backend_->SetFavicons( 2235 page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps); 2236 2237 favicon_base::FaviconRawBitmapResult result; 2238 std::vector<int> icon_types; 2239 icon_types.push_back(favicon_base::FAVICON); 2240 icon_types.push_back(favicon_base::TOUCH_ICON); 2241 2242 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result); 2243 2244 // Verify the result icon is 32x32 favicon. 2245 EXPECT_EQ(gfx::Size(32, 32), result.pixel_size); 2246 EXPECT_EQ(favicon_base::FAVICON, result.icon_type); 2247 2248 // Change minimal size to 32x32 and verify the 32x32 favicon returned. 2249 favicon_base::FaviconRawBitmapResult result1; 2250 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1); 2251 EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size); 2252 EXPECT_EQ(favicon_base::FAVICON, result1.icon_type); 2253 } 2254 2255 // Test the favicon is returned if its size is smaller than minimal size, 2256 // because it is only one available. 2257 TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) { 2258 GURL page_url("http://www.google.com"); 2259 GURL icon_url("http://www.google.com/favicon.ico"); 2260 2261 std::vector<SkBitmap> bitmaps; 2262 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16)); 2263 2264 // Set preexisting favicons for |page_url|. 2265 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 2266 2267 favicon_base::FaviconRawBitmapResult result; 2268 std::vector<int> icon_types; 2269 icon_types.push_back(favicon_base::FAVICON); 2270 icon_types.push_back(favicon_base::TOUCH_ICON); 2271 2272 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result); 2273 2274 // Verify 16x16 icon is returned, even it small than minimal_size. 2275 EXPECT_EQ(gfx::Size(16, 16), result.pixel_size); 2276 EXPECT_EQ(favicon_base::FAVICON, result.icon_type); 2277 } 2278 2279 // Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in. 2280 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) { 2281 GURL page_url1("http://www.google.com"); 2282 GURL page_url2("http://news.google.com"); 2283 GURL page_url3("http://mail.google.com"); 2284 GURL icon_urla("http://www.google.com/favicon1.ico"); 2285 GURL icon_urlb("http://www.google.com/favicon2.ico"); 2286 std::vector<SkBitmap> bitmaps; 2287 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 2288 2289 // |page_url1| is mapped to |icon_urla| which if of type TOUCH_ICON. 2290 backend_->SetFavicons( 2291 page_url1, favicon_base::TOUCH_ICON, icon_urla, bitmaps); 2292 2293 // |page_url2| is mapped to |icon_urlb| which is of type 2294 // TOUCH_PRECOMPOSED_ICON. 2295 backend_->SetFavicons( 2296 page_url2, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_urlb, bitmaps); 2297 2298 std::vector<GURL> icon_urls; 2299 icon_urls.push_back(icon_urla); 2300 icon_urls.push_back(icon_urlb); 2301 2302 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results; 2303 backend_->UpdateFaviconMappingsAndFetch( 2304 page_url3, 2305 icon_urls, 2306 (favicon_base::TOUCH_ICON | favicon_base::TOUCH_PRECOMPOSED_ICON), 2307 GetEdgeSizesSmallAndLarge(), 2308 &bitmap_results); 2309 2310 // |page_url1| and |page_url2| should still be mapped to the same icon URLs. 2311 std::vector<IconMapping> icon_mappings; 2312 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1, 2313 &icon_mappings)); 2314 EXPECT_EQ(1u, icon_mappings.size()); 2315 EXPECT_EQ(icon_urla, icon_mappings[0].icon_url); 2316 EXPECT_EQ(favicon_base::TOUCH_ICON, icon_mappings[0].icon_type); 2317 2318 icon_mappings.clear(); 2319 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url2, &icon_mappings)); 2320 EXPECT_EQ(1u, icon_mappings.size()); 2321 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url); 2322 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type); 2323 2324 // |page_url3| should be mapped only to |icon_urlb| as TOUCH_PRECOMPOSED_ICON 2325 // is the largest IconType. 2326 icon_mappings.clear(); 2327 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url3, &icon_mappings)); 2328 EXPECT_EQ(1u, icon_mappings.size()); 2329 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url); 2330 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type); 2331 } 2332 2333 // Test the results of GetFaviconsFromDB() when there are no found favicons. 2334 TEST_F(HistoryBackendTest, GetFaviconsFromDBEmpty) { 2335 const GURL page_url("http://www.google.com/"); 2336 2337 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results; 2338 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url, 2339 favicon_base::FAVICON, 2340 GetEdgeSizesSmallAndLarge(), 2341 &bitmap_results)); 2342 EXPECT_TRUE(bitmap_results.empty()); 2343 } 2344 2345 // Test the results of GetFaviconsFromDB() when there are matching favicons 2346 // but there are no associated favicon bitmaps. 2347 TEST_F(HistoryBackendTest, GetFaviconsFromDBNoFaviconBitmaps) { 2348 const GURL page_url("http://www.google.com/"); 2349 const GURL icon_url("http://www.google.com/icon1"); 2350 2351 favicon_base::FaviconID icon_id = 2352 backend_->thumbnail_db_->AddFavicon(icon_url, favicon_base::FAVICON); 2353 EXPECT_NE(0, icon_id); 2354 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id)); 2355 2356 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out; 2357 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url, 2358 favicon_base::FAVICON, 2359 GetEdgeSizesSmallAndLarge(), 2360 &bitmap_results_out)); 2361 EXPECT_TRUE(bitmap_results_out.empty()); 2362 } 2363 2364 // Test that GetFaviconsFromDB() returns results for the bitmaps which most 2365 // closely match the passed in the desired pixel sizes. 2366 TEST_F(HistoryBackendTest, GetFaviconsFromDBSelectClosestMatch) { 2367 const GURL page_url("http://www.google.com/"); 2368 const GURL icon_url("http://www.google.com/icon1"); 2369 std::vector<SkBitmap> bitmaps; 2370 bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kTinyEdgeSize)); 2371 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 2372 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize)); 2373 2374 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps); 2375 2376 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out; 2377 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, 2378 favicon_base::FAVICON, 2379 GetEdgeSizesSmallAndLarge(), 2380 &bitmap_results_out)); 2381 2382 // The bitmap data for the small and large bitmaps should be returned as their 2383 // sizes match exactly. 2384 EXPECT_EQ(2u, bitmap_results_out.size()); 2385 // No required order for results. 2386 if (bitmap_results_out[0].pixel_size == kLargeSize) { 2387 favicon_base::FaviconRawBitmapResult tmp_result = bitmap_results_out[0]; 2388 bitmap_results_out[0] = bitmap_results_out[1]; 2389 bitmap_results_out[1] = tmp_result; 2390 } 2391 2392 EXPECT_FALSE(bitmap_results_out[0].expired); 2393 EXPECT_TRUE( 2394 BitmapColorEqual(SK_ColorBLUE, bitmap_results_out[0].bitmap_data)); 2395 EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size); 2396 EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url); 2397 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type); 2398 2399 EXPECT_FALSE(bitmap_results_out[1].expired); 2400 EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, bitmap_results_out[1].bitmap_data)); 2401 EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size); 2402 EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url); 2403 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[1].icon_type); 2404 } 2405 2406 // Test the results of GetFaviconsFromDB() when called with different 2407 // |icon_types|. 2408 TEST_F(HistoryBackendTest, GetFaviconsFromDBIconType) { 2409 const GURL page_url("http://www.google.com/"); 2410 const GURL icon_url1("http://www.google.com/icon1.png"); 2411 const GURL icon_url2("http://www.google.com/icon2.png"); 2412 std::vector<SkBitmap> bitmaps; 2413 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 2414 2415 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data; 2416 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps); 2417 backend_->SetFavicons(page_url, favicon_base::TOUCH_ICON, icon_url2, bitmaps); 2418 2419 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out; 2420 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, 2421 favicon_base::FAVICON, 2422 GetEdgeSizesSmallAndLarge(), 2423 &bitmap_results_out)); 2424 2425 EXPECT_EQ(1u, bitmap_results_out.size()); 2426 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type); 2427 EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url); 2428 2429 bitmap_results_out.clear(); 2430 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, 2431 favicon_base::TOUCH_ICON, 2432 GetEdgeSizesSmallAndLarge(), 2433 &bitmap_results_out)); 2434 2435 EXPECT_EQ(1u, bitmap_results_out.size()); 2436 EXPECT_EQ(favicon_base::TOUCH_ICON, bitmap_results_out[0].icon_type); 2437 EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url); 2438 } 2439 2440 // Test that GetFaviconsFromDB() correctly sets the expired flag for bitmap 2441 // reults. 2442 TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) { 2443 const GURL page_url("http://www.google.com/"); 2444 const GURL icon_url("http://www.google.com/icon.png"); 2445 2446 std::vector<unsigned char> data; 2447 data.push_back('a'); 2448 scoped_refptr<base::RefCountedBytes> bitmap_data( 2449 base::RefCountedBytes::TakeVector(&data)); 2450 base::Time last_updated = base::Time::FromTimeT(0); 2451 favicon_base::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon( 2452 icon_url, favicon_base::FAVICON, bitmap_data, last_updated, kSmallSize); 2453 EXPECT_NE(0, icon_id); 2454 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id)); 2455 2456 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out; 2457 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, 2458 favicon_base::FAVICON, 2459 GetEdgeSizesSmallAndLarge(), 2460 &bitmap_results_out)); 2461 2462 EXPECT_EQ(1u, bitmap_results_out.size()); 2463 EXPECT_TRUE(bitmap_results_out[0].expired); 2464 } 2465 2466 // Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is 2467 // no valid thumbnail database. 2468 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) { 2469 // Make the thumbnail database invalid. 2470 backend_->thumbnail_db_.reset(); 2471 2472 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results; 2473 2474 backend_->UpdateFaviconMappingsAndFetch(GURL(), 2475 std::vector<GURL>(), 2476 favicon_base::FAVICON, 2477 GetEdgeSizesSmallAndLarge(), 2478 &bitmap_results); 2479 2480 EXPECT_TRUE(bitmap_results.empty()); 2481 } 2482 2483 TEST_F(HistoryBackendTest, CloneFaviconIsRestrictedToSameDomain) { 2484 const GURL url("http://www.google.com/"); 2485 const GURL same_domain_url("http://www.google.com/subdir/index.html"); 2486 const GURL foreign_domain_url("http://www.not-google.com/"); 2487 const GURL icon_url("http://www.google.com/icon.png"); 2488 std::vector<SkBitmap> bitmaps; 2489 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize)); 2490 2491 // Add a favicon 2492 backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps); 2493 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL( 2494 url, favicon_base::FAVICON, NULL)); 2495 2496 // Validate starting state. 2497 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out; 2498 EXPECT_TRUE(backend_->GetFaviconsFromDB(url, 2499 favicon_base::FAVICON, 2500 GetEdgeSizesSmallAndLarge(), 2501 &bitmap_results_out)); 2502 EXPECT_FALSE(backend_->GetFaviconsFromDB(same_domain_url, 2503 favicon_base::FAVICON, 2504 GetEdgeSizesSmallAndLarge(), 2505 &bitmap_results_out)); 2506 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url, 2507 favicon_base::FAVICON, 2508 GetEdgeSizesSmallAndLarge(), 2509 &bitmap_results_out)); 2510 2511 // Same-domain cloning should work. 2512 backend_->CloneFavicons(url, same_domain_url); 2513 EXPECT_TRUE(backend_->GetFaviconsFromDB(same_domain_url, 2514 favicon_base::FAVICON, 2515 GetEdgeSizesSmallAndLarge(), 2516 &bitmap_results_out)); 2517 2518 // Foreign-domain cloning is forbidden. 2519 backend_->CloneFavicons(url, foreign_domain_url); 2520 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url, 2521 favicon_base::FAVICON, 2522 GetEdgeSizesSmallAndLarge(), 2523 &bitmap_results_out)); 2524 } 2525 2526 TEST_F(HistoryBackendTest, QueryFilteredURLs) { 2527 const char* google = "http://www.google.com/"; 2528 const char* yahoo = "http://www.yahoo.com/"; 2529 const char* yahoo_sports = "http://sports.yahoo.com/"; 2530 const char* yahoo_sports_with_article1 = 2531 "http://sports.yahoo.com/article1.htm"; 2532 const char* yahoo_sports_with_article2 = 2533 "http://sports.yahoo.com/article2.htm"; 2534 const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer"; 2535 const char* apple = "http://www.apple.com/"; 2536 2537 // Clear all history. 2538 backend_->DeleteAllHistory(); 2539 2540 Time tested_time = Time::Now().LocalMidnight() + 2541 base::TimeDelta::FromHours(4); 2542 base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30); 2543 base::TimeDelta one_hour = base::TimeDelta::FromHours(1); 2544 base::TimeDelta one_day = base::TimeDelta::FromDays(1); 2545 2546 const ui::PageTransition kTypedTransition = 2547 ui::PAGE_TRANSITION_TYPED; 2548 const ui::PageTransition kKeywordGeneratedTransition = 2549 ui::PAGE_TRANSITION_KEYWORD_GENERATED; 2550 2551 const char* redirect_sequence[2]; 2552 redirect_sequence[1] = NULL; 2553 2554 redirect_sequence[0] = google; 2555 AddRedirectChainWithTransitionAndTime( 2556 redirect_sequence, 0, kTypedTransition, 2557 tested_time - one_day - half_an_hour * 2); 2558 AddRedirectChainWithTransitionAndTime( 2559 redirect_sequence, 0, 2560 kTypedTransition, tested_time - one_day); 2561 AddRedirectChainWithTransitionAndTime( 2562 redirect_sequence, 0, 2563 kTypedTransition, tested_time - half_an_hour / 2); 2564 AddRedirectChainWithTransitionAndTime( 2565 redirect_sequence, 0, 2566 kTypedTransition, tested_time); 2567 2568 // Add a visit with a transition that will make sure that no segment gets 2569 // created for this page (so the subsequent entries will have different URLIDs 2570 // and SegmentIDs). 2571 redirect_sequence[0] = apple; 2572 AddRedirectChainWithTransitionAndTime( 2573 redirect_sequence, 0, kKeywordGeneratedTransition, 2574 tested_time - one_day + one_hour * 6); 2575 2576 redirect_sequence[0] = yahoo; 2577 AddRedirectChainWithTransitionAndTime( 2578 redirect_sequence, 0, kTypedTransition, 2579 tested_time - one_day + half_an_hour); 2580 AddRedirectChainWithTransitionAndTime( 2581 redirect_sequence, 0, kTypedTransition, 2582 tested_time - one_day + half_an_hour * 2); 2583 2584 redirect_sequence[0] = yahoo_sports; 2585 AddRedirectChainWithTransitionAndTime( 2586 redirect_sequence, 0, kTypedTransition, 2587 tested_time - one_day - half_an_hour * 2); 2588 AddRedirectChainWithTransitionAndTime( 2589 redirect_sequence, 0, kTypedTransition, 2590 tested_time - one_day); 2591 int transition1, transition2; 2592 AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false, 2593 tested_time - one_day + half_an_hour, 2594 &transition1, &transition2); 2595 AddClientRedirect(GURL(yahoo_sports_with_article1), 2596 GURL(yahoo_sports_with_article2), 2597 false, 2598 tested_time - one_day + half_an_hour * 2, 2599 &transition1, &transition2); 2600 2601 redirect_sequence[0] = yahoo_sports_soccer; 2602 AddRedirectChainWithTransitionAndTime(redirect_sequence, 0, 2603 kTypedTransition, 2604 tested_time - half_an_hour); 2605 backend_->Commit(); 2606 2607 VisitFilter filter; 2608 FilteredURLList filtered_list; 2609 // Time limit is |tested_time| +/- 45 min. 2610 base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45); 2611 filter.SetFilterTime(tested_time); 2612 filter.SetFilterWidth(three_quarters_of_an_hour); 2613 backend_->QueryFilteredURLs(100, filter, false, &filtered_list); 2614 2615 ASSERT_EQ(4U, filtered_list.size()); 2616 EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); 2617 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); 2618 EXPECT_EQ(std::string(yahoo), filtered_list[2].url.spec()); 2619 EXPECT_EQ(std::string(yahoo_sports), filtered_list[3].url.spec()); 2620 2621 // Time limit is between |tested_time| and |tested_time| + 2 hours. 2622 filter.SetFilterTime(tested_time + one_hour); 2623 filter.SetFilterWidth(one_hour); 2624 backend_->QueryFilteredURLs(100, filter, false, &filtered_list); 2625 2626 ASSERT_EQ(3U, filtered_list.size()); 2627 EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); 2628 EXPECT_EQ(std::string(yahoo), filtered_list[1].url.spec()); 2629 EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec()); 2630 2631 // Time limit is between |tested_time| - 2 hours and |tested_time|. 2632 filter.SetFilterTime(tested_time - one_hour); 2633 filter.SetFilterWidth(one_hour); 2634 backend_->QueryFilteredURLs(100, filter, false, &filtered_list); 2635 2636 ASSERT_EQ(3U, filtered_list.size()); 2637 EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); 2638 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); 2639 EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec()); 2640 2641 filter.ClearFilters(); 2642 base::Time::Exploded exploded_time; 2643 tested_time.LocalExplode(&exploded_time); 2644 2645 // Today. 2646 filter.SetFilterTime(tested_time); 2647 filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week)); 2648 backend_->QueryFilteredURLs(100, filter, false, &filtered_list); 2649 2650 ASSERT_EQ(2U, filtered_list.size()); 2651 EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); 2652 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); 2653 2654 // Today + time limit - only yahoo_sports_soccer should fit. 2655 filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40)); 2656 filter.SetFilterWidth(base::TimeDelta::FromMinutes(20)); 2657 backend_->QueryFilteredURLs(100, filter, false, &filtered_list); 2658 2659 ASSERT_EQ(1U, filtered_list.size()); 2660 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[0].url.spec()); 2661 2662 // Make sure we get debug data if we request it. 2663 filter.SetFilterTime(tested_time); 2664 filter.SetFilterWidth(one_hour * 2); 2665 backend_->QueryFilteredURLs(100, filter, true, &filtered_list); 2666 2667 // If the SegmentID is used by QueryFilteredURLs when generating the debug 2668 // data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer| 2669 // entry will be zero instead of 1. 2670 ASSERT_GE(filtered_list.size(), 2U); 2671 EXPECT_EQ(std::string(google), filtered_list[0].url.spec()); 2672 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec()); 2673 EXPECT_EQ(4U, filtered_list[0].extended_info.total_visits); 2674 EXPECT_EQ(1U, filtered_list[1].extended_info.total_visits); 2675 } 2676 2677 TEST_F(HistoryBackendTest, UpdateVisitDuration) { 2678 // This unit test will test adding and deleting visit details information. 2679 ASSERT_TRUE(backend_.get()); 2680 2681 GURL url1("http://www.cnn.com"); 2682 std::vector<VisitInfo> visit_info1, visit_info2; 2683 Time start_ts = Time::Now() - base::TimeDelta::FromDays(5); 2684 Time end_ts = start_ts + base::TimeDelta::FromDays(2); 2685 visit_info1.push_back(VisitInfo(start_ts, ui::PAGE_TRANSITION_LINK)); 2686 2687 GURL url2("http://www.example.com"); 2688 visit_info2.push_back(VisitInfo(Time::Now() - base::TimeDelta::FromDays(10), 2689 ui::PAGE_TRANSITION_LINK)); 2690 2691 // Clear all history. 2692 backend_->DeleteAllHistory(); 2693 2694 // Add the visits. 2695 backend_->AddVisits(url1, visit_info1, history::SOURCE_BROWSED); 2696 backend_->AddVisits(url2, visit_info2, history::SOURCE_BROWSED); 2697 2698 // Verify the entries for both visits were added in visit_details. 2699 VisitVector visits1, visits2; 2700 URLRow row; 2701 URLID url_id1 = backend_->db()->GetRowForURL(url1, &row); 2702 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1)); 2703 ASSERT_EQ(1U, visits1.size()); 2704 EXPECT_EQ(0, visits1[0].visit_duration.ToInternalValue()); 2705 2706 URLID url_id2 = backend_->db()->GetRowForURL(url2, &row); 2707 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id2, &visits2)); 2708 ASSERT_EQ(1U, visits2.size()); 2709 EXPECT_EQ(0, visits2[0].visit_duration.ToInternalValue()); 2710 2711 // Update the visit to cnn.com. 2712 backend_->UpdateVisitDuration(visits1[0].visit_id, end_ts); 2713 2714 // Check the duration for visiting cnn.com was correctly updated. 2715 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1)); 2716 ASSERT_EQ(1U, visits1.size()); 2717 base::TimeDelta expected_duration = end_ts - start_ts; 2718 EXPECT_EQ(expected_duration.ToInternalValue(), 2719 visits1[0].visit_duration.ToInternalValue()); 2720 2721 // Remove the visit to cnn.com. 2722 ASSERT_TRUE(backend_->RemoveVisits(visits1)); 2723 } 2724 2725 // Test for migration of adding visit_duration column. 2726 TEST_F(HistoryBackendTest, MigrationVisitDuration) { 2727 ASSERT_TRUE(backend_.get()); 2728 backend_->Closing(); 2729 backend_ = NULL; 2730 2731 base::FilePath old_history_path, old_history; 2732 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path)); 2733 old_history_path = old_history_path.AppendASCII("History"); 2734 old_history = old_history_path.AppendASCII("HistoryNoDuration"); 2735 2736 // Copy history database file to current directory so that it will be deleted 2737 // in Teardown. 2738 base::FilePath new_history_path(test_dir()); 2739 base::DeleteFile(new_history_path, true); 2740 base::CreateDirectory(new_history_path); 2741 base::FilePath new_history_file = 2742 new_history_path.Append(chrome::kHistoryFilename); 2743 ASSERT_TRUE(base::CopyFile(old_history, new_history_file)); 2744 2745 backend_ = new HistoryBackend( 2746 new_history_path, new HistoryBackendTestDelegate(this), &history_client_); 2747 backend_->Init(std::string(), false); 2748 backend_->Closing(); 2749 backend_ = NULL; 2750 2751 // Now the history database should already be migrated. 2752 2753 // Check version in history database first. 2754 int cur_version = HistoryDatabase::GetCurrentVersion(); 2755 sql::Connection db; 2756 ASSERT_TRUE(db.Open(new_history_file)); 2757 sql::Statement s(db.GetUniqueStatement( 2758 "SELECT value FROM meta WHERE key = 'version'")); 2759 ASSERT_TRUE(s.Step()); 2760 int file_version = s.ColumnInt(0); 2761 EXPECT_EQ(cur_version, file_version); 2762 2763 // Check visit_duration column in visits table is created and set to 0. 2764 s.Assign(db.GetUniqueStatement( 2765 "SELECT visit_duration FROM visits LIMIT 1")); 2766 ASSERT_TRUE(s.Step()); 2767 EXPECT_EQ(0, s.ColumnInt(0)); 2768 } 2769 2770 TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) { 2771 ASSERT_TRUE(backend_.get()); 2772 2773 GURL url("http://www.google.com"); 2774 base::string16 title(base::UTF8ToUTF16("Bookmark title")); 2775 backend_->AddPageNoVisitForBookmark(url, title); 2776 2777 URLRow row; 2778 backend_->GetURL(url, &row); 2779 EXPECT_EQ(url, row.url()); 2780 EXPECT_EQ(title, row.title()); 2781 EXPECT_EQ(0, row.visit_count()); 2782 2783 backend_->DeleteURL(url); 2784 backend_->AddPageNoVisitForBookmark(url, base::string16()); 2785 backend_->GetURL(url, &row); 2786 EXPECT_EQ(url, row.url()); 2787 EXPECT_EQ(base::UTF8ToUTF16(url.spec()), row.title()); 2788 EXPECT_EQ(0, row.visit_count()); 2789 } 2790 2791 TEST_F(HistoryBackendTest, ExpireHistoryForTimes) { 2792 ASSERT_TRUE(backend_.get()); 2793 2794 HistoryAddPageArgs args[10]; 2795 for (size_t i = 0; i < arraysize(args); ++i) { 2796 args[i].url = GURL("http://example" + 2797 std::string((i % 2 == 0 ? ".com" : ".net"))); 2798 args[i].time = base::Time::FromInternalValue(i); 2799 backend_->AddPage(args[i]); 2800 } 2801 EXPECT_EQ(base::Time(), backend_->GetFirstRecordedTimeForTest()); 2802 2803 URLRow row; 2804 for (size_t i = 0; i < arraysize(args); ++i) { 2805 EXPECT_TRUE(backend_->GetURL(args[i].url, &row)); 2806 } 2807 2808 std::set<base::Time> times; 2809 times.insert(args[5].time); 2810 backend_->ExpireHistoryForTimes(times, 2811 base::Time::FromInternalValue(2), 2812 base::Time::FromInternalValue(8)); 2813 2814 EXPECT_EQ(base::Time::FromInternalValue(0), 2815 backend_->GetFirstRecordedTimeForTest()); 2816 2817 // Visits to http://example.com are untouched. 2818 VisitVector visit_vector; 2819 EXPECT_TRUE(backend_->GetVisitsForURL( 2820 backend_->db_->GetRowForURL(GURL("http://example.com"), NULL), 2821 &visit_vector)); 2822 ASSERT_EQ(5u, visit_vector.size()); 2823 EXPECT_EQ(base::Time::FromInternalValue(0), visit_vector[0].visit_time); 2824 EXPECT_EQ(base::Time::FromInternalValue(2), visit_vector[1].visit_time); 2825 EXPECT_EQ(base::Time::FromInternalValue(4), visit_vector[2].visit_time); 2826 EXPECT_EQ(base::Time::FromInternalValue(6), visit_vector[3].visit_time); 2827 EXPECT_EQ(base::Time::FromInternalValue(8), visit_vector[4].visit_time); 2828 2829 // Visits to http://example.net between [2,8] are removed. 2830 visit_vector.clear(); 2831 EXPECT_TRUE(backend_->GetVisitsForURL( 2832 backend_->db_->GetRowForURL(GURL("http://example.net"), NULL), 2833 &visit_vector)); 2834 ASSERT_EQ(2u, visit_vector.size()); 2835 EXPECT_EQ(base::Time::FromInternalValue(1), visit_vector[0].visit_time); 2836 EXPECT_EQ(base::Time::FromInternalValue(9), visit_vector[1].visit_time); 2837 2838 EXPECT_EQ(base::Time::FromInternalValue(0), 2839 backend_->GetFirstRecordedTimeForTest()); 2840 } 2841 2842 TEST_F(HistoryBackendTest, ExpireHistory) { 2843 ASSERT_TRUE(backend_.get()); 2844 // Since history operations are dependent on the local timezone, make all 2845 // entries relative to a fixed, local reference time. 2846 base::Time reference_time = base::Time::UnixEpoch().LocalMidnight() + 2847 base::TimeDelta::FromHours(12); 2848 2849 // Insert 4 entries into the database. 2850 HistoryAddPageArgs args[4]; 2851 for (size_t i = 0; i < arraysize(args); ++i) { 2852 args[i].url = GURL("http://example" + base::IntToString(i) + ".com"); 2853 args[i].time = reference_time + base::TimeDelta::FromDays(i); 2854 backend_->AddPage(args[i]); 2855 } 2856 2857 URLRow url_rows[4]; 2858 for (unsigned int i = 0; i < arraysize(args); ++i) 2859 ASSERT_TRUE(backend_->GetURL(args[i].url, &url_rows[i])); 2860 2861 std::vector<ExpireHistoryArgs> expire_list; 2862 VisitVector visits; 2863 2864 // Passing an empty map should be a no-op. 2865 backend_->ExpireHistory(expire_list); 2866 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits); 2867 EXPECT_EQ(4U, visits.size()); 2868 2869 // Trying to delete an unknown URL with the time of the first visit should 2870 // also be a no-op. 2871 expire_list.resize(expire_list.size() + 1); 2872 expire_list[0].SetTimeRangeForOneDay(args[0].time); 2873 expire_list[0].urls.insert(GURL("http://google.does-not-exist")); 2874 backend_->ExpireHistory(expire_list); 2875 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits); 2876 EXPECT_EQ(4U, visits.size()); 2877 2878 // Now add the first URL with the same time -- it should get deleted. 2879 expire_list.back().urls.insert(url_rows[0].url()); 2880 backend_->ExpireHistory(expire_list); 2881 2882 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits); 2883 ASSERT_EQ(3U, visits.size()); 2884 EXPECT_EQ(visits[0].url_id, url_rows[1].id()); 2885 EXPECT_EQ(visits[1].url_id, url_rows[2].id()); 2886 EXPECT_EQ(visits[2].url_id, url_rows[3].id()); 2887 2888 // The first recorded time should also get updated. 2889 EXPECT_EQ(backend_->GetFirstRecordedTimeForTest(), args[1].time); 2890 2891 // Now delete the rest of the visits in one call. 2892 for (unsigned int i = 1; i < arraysize(args); ++i) { 2893 expire_list.resize(expire_list.size() + 1); 2894 expire_list[i].SetTimeRangeForOneDay(args[i].time); 2895 expire_list[i].urls.insert(args[i].url); 2896 } 2897 backend_->ExpireHistory(expire_list); 2898 2899 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits); 2900 ASSERT_EQ(0U, visits.size()); 2901 } 2902 2903 TEST_F(HistoryBackendTest, DeleteMatchingUrlsForKeyword) { 2904 // Set up urls and keyword_search_terms 2905 GURL url1("https://www.bing.com/?q=bar"); 2906 URLRow url_info1(url1); 2907 url_info1.set_visit_count(0); 2908 url_info1.set_typed_count(0); 2909 url_info1.set_last_visit(Time()); 2910 url_info1.set_hidden(false); 2911 const URLID url1_id = backend_->db()->AddURL(url_info1); 2912 EXPECT_NE(0, url1_id); 2913 2914 KeywordID keyword_id = 1; 2915 base::string16 keyword = base::UTF8ToUTF16("bar"); 2916 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL( 2917 url1_id, keyword_id, keyword)); 2918 2919 GURL url2("https://www.google.com/?q=bar"); 2920 URLRow url_info2(url2); 2921 url_info2.set_visit_count(0); 2922 url_info2.set_typed_count(0); 2923 url_info2.set_last_visit(Time()); 2924 url_info2.set_hidden(false); 2925 const URLID url2_id = backend_->db()->AddURL(url_info2); 2926 EXPECT_NE(0, url2_id); 2927 2928 KeywordID keyword_id2 = 2; 2929 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL( 2930 url2_id, keyword_id2, keyword)); 2931 2932 // Add another visit to the same URL 2933 URLRow url_info3(url2); 2934 url_info3.set_visit_count(0); 2935 url_info3.set_typed_count(0); 2936 url_info3.set_last_visit(Time()); 2937 url_info3.set_hidden(false); 2938 const URLID url3_id = backend_->db()->AddURL(url_info3); 2939 EXPECT_NE(0, url3_id); 2940 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL( 2941 url3_id, keyword_id2, keyword)); 2942 2943 // Test that deletion works correctly 2944 backend_->DeleteMatchingURLsForKeyword(keyword_id2, keyword); 2945 2946 // Test that rows 2 and 3 are deleted, while 1 is intact 2947 URLRow row; 2948 EXPECT_TRUE(backend_->db()->GetURLRow(url1_id, &row)); 2949 EXPECT_EQ(url1.spec(), row.url().spec()); 2950 EXPECT_FALSE(backend_->db()->GetURLRow(url2_id, &row)); 2951 EXPECT_FALSE(backend_->db()->GetURLRow(url3_id, &row)); 2952 2953 // Test that corresponding keyword search terms are deleted for rows 2 & 3, 2954 // but not for row 1 2955 EXPECT_TRUE(backend_->db()->GetKeywordSearchTermRow(url1_id, NULL)); 2956 EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url2_id, NULL)); 2957 EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url3_id, NULL)); 2958 } 2959 2960 // Simple test that removes a bookmark. This test exercises the code paths in 2961 // History that block till bookmark bar model is loaded. 2962 TEST_F(HistoryBackendTest, RemoveNotification) { 2963 scoped_ptr<TestingProfile> profile(new TestingProfile()); 2964 2965 // Add a URL. 2966 GURL url("http://www.google.com"); 2967 HistoryClientMock history_client; 2968 history_client.AddBookmark(url); 2969 scoped_ptr<HistoryService> service( 2970 new HistoryService(&history_client, profile.get())); 2971 EXPECT_TRUE(service->Init(profile->GetPath())); 2972 2973 service->AddPage( 2974 url, base::Time::Now(), NULL, 1, GURL(), RedirectList(), 2975 ui::PAGE_TRANSITION_TYPED, SOURCE_BROWSED, false); 2976 2977 // This won't actually delete the URL, rather it'll empty out the visits. 2978 // This triggers blocking on the BookmarkModel. 2979 EXPECT_CALL(history_client, BlockUntilBookmarksLoaded()); 2980 service->DeleteURL(url); 2981 } 2982 2983 // Test DeleteFTSIndexDatabases deletes expected files. 2984 TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) { 2985 ASSERT_TRUE(backend_.get()); 2986 2987 base::FilePath history_path(test_dir()); 2988 base::FilePath db1(history_path.AppendASCII("History Index 2013-05")); 2989 base::FilePath db1_journal(db1.InsertBeforeExtensionASCII("-journal")); 2990 base::FilePath db1_wal(db1.InsertBeforeExtensionASCII("-wal")); 2991 base::FilePath db2_symlink(history_path.AppendASCII("History Index 2013-06")); 2992 base::FilePath db2_actual(history_path.AppendASCII("Underlying DB")); 2993 2994 // Setup dummy index database files. 2995 const char* data = "Dummy"; 2996 const size_t data_len = 5; 2997 ASSERT_TRUE(base::WriteFile(db1, data, data_len)); 2998 ASSERT_TRUE(base::WriteFile(db1_journal, data, data_len)); 2999 ASSERT_TRUE(base::WriteFile(db1_wal, data, data_len)); 3000 ASSERT_TRUE(base::WriteFile(db2_actual, data, data_len)); 3001 #if defined(OS_POSIX) 3002 EXPECT_TRUE(base::CreateSymbolicLink(db2_actual, db2_symlink)); 3003 #endif 3004 3005 // Delete all DTS index databases. 3006 backend_->DeleteFTSIndexDatabases(); 3007 EXPECT_FALSE(base::PathExists(db1)); 3008 EXPECT_FALSE(base::PathExists(db1_wal)); 3009 EXPECT_FALSE(base::PathExists(db1_journal)); 3010 EXPECT_FALSE(base::PathExists(db2_symlink)); 3011 EXPECT_TRUE(base::PathExists(db2_actual)); // Symlinks shouldn't be followed. 3012 } 3013 3014 // Common implementation for the two tests below, given that the only difference 3015 // between them is the type of the notification sent out. 3016 void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows( 3017 int notification_type) { 3018 const char kTestTypedURLAlternativeTitle[] = "Google Search Again"; 3019 const char kTestNonTypedURLAlternativeTitle[] = "Google News Again"; 3020 3021 // Notify the in-memory database that a typed and non-typed URLRow (which were 3022 // never before seen by the cache) have been modified. 3023 URLRow row1(CreateTestTypedURL()); 3024 URLRow row2(CreateTestNonTypedURL()); 3025 SimulateNotification(notification_type, &row1, &row2); 3026 3027 // The in-memory database should only pick up the typed URL, and should ignore 3028 // the non-typed one. The typed URL should retain the ID that was present in 3029 // the notification. 3030 URLRow cached_row1, cached_row2; 3031 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1)); 3032 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2)); 3033 EXPECT_EQ(row1.id(), cached_row1.id()); 3034 3035 // Try changing attributes (other than typed_count) for existing URLRows. 3036 row1.set_title(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle)); 3037 row2.set_title(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle)); 3038 SimulateNotification(notification_type, &row1, &row2); 3039 3040 // URLRows that are cached by the in-memory database should be updated. 3041 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1)); 3042 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2)); 3043 EXPECT_EQ(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle), 3044 cached_row1.title()); 3045 3046 // Now decrease the typed count for the typed URLRow, and increase it for the 3047 // previously non-typed URLRow. 3048 row1.set_typed_count(0); 3049 row2.set_typed_count(2); 3050 SimulateNotification(notification_type, &row1, &row2); 3051 3052 // The in-memory database should stop caching the first URLRow, and start 3053 // caching the second URLRow. 3054 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1)); 3055 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2)); 3056 EXPECT_EQ(row2.id(), cached_row2.id()); 3057 EXPECT_EQ(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle), 3058 cached_row2.title()); 3059 } 3060 3061 TEST_F(InMemoryHistoryBackendTest, OnURLsModified) { 3062 TestAddingAndChangingURLRows(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED); 3063 } 3064 3065 TEST_F(InMemoryHistoryBackendTest, OnURLsVisisted) { 3066 TestAddingAndChangingURLRows(chrome::NOTIFICATION_HISTORY_URL_VISITED); 3067 } 3068 3069 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedPiecewise) { 3070 // Add two typed and one non-typed URLRow to the in-memory database. 3071 URLRow row1(CreateTestTypedURL()); 3072 URLRow row2(CreateAnotherTestTypedURL()); 3073 URLRow row3(CreateTestNonTypedURL()); 3074 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 3075 &row1, &row2, &row3); 3076 3077 // Notify the in-memory database that the second typed URL and the non-typed 3078 // URL has been deleted. 3079 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_DELETED, 3080 &row2, &row3); 3081 3082 // Expect that the first typed URL remains intact, the second typed URL is 3083 // correctly removed, and the non-typed URL does not magically appear. 3084 URLRow cached_row1; 3085 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1)); 3086 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL)); 3087 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL)); 3088 EXPECT_EQ(row1.id(), cached_row1.id()); 3089 } 3090 3091 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedEnMasse) { 3092 // Add two typed and one non-typed URLRow to the in-memory database. 3093 URLRow row1(CreateTestTypedURL()); 3094 URLRow row2(CreateAnotherTestTypedURL()); 3095 URLRow row3(CreateTestNonTypedURL()); 3096 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED, 3097 &row1, &row2, &row3); 3098 3099 // Now notify the in-memory database that all history has been deleted. 3100 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails()); 3101 details->all_history = true; 3102 BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED, 3103 details.PassAs<HistoryDetails>()); 3104 3105 // Expect that everything goes away. 3106 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), NULL)); 3107 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL)); 3108 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL)); 3109 } 3110 3111 void InMemoryHistoryBackendTest::PopulateTestURLsAndSearchTerms( 3112 URLRow* row1, 3113 URLRow* row2, 3114 const base::string16& term1, 3115 const base::string16& term2) { 3116 // Add a typed and a non-typed URLRow to the in-memory database. This time, 3117 // though, do it through the history backend... 3118 URLRows rows; 3119 rows.push_back(*row1); 3120 rows.push_back(*row2); 3121 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED); 3122 backend_->db()->GetRowForURL(row1->url(), row1); // Get effective IDs from 3123 backend_->db()->GetRowForURL(row2->url(), row2); // the database. 3124 3125 // ... so that we can also use that for adding the search terms. This way, we 3126 // not only test that the notifications involved are handled correctly, but 3127 // also that they are fired correctly (in the history backend). 3128 backend_->SetKeywordSearchTermsForURL(row1->url(), kTestKeywordId, term1); 3129 backend_->SetKeywordSearchTermsForURL(row2->url(), kTestKeywordId, term2); 3130 } 3131 3132 TEST_F(InMemoryHistoryBackendTest, SetKeywordSearchTerms) { 3133 URLRow row1(CreateTestTypedURL()); 3134 URLRow row2(CreateTestNonTypedURL()); 3135 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1)); 3136 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2)); 3137 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2); 3138 3139 // Both URLs now have associated search terms, so the in-memory database 3140 // should cache both of them, regardless whether they have been typed or not. 3141 URLRow cached_row1, cached_row2; 3142 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1)); 3143 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2)); 3144 EXPECT_EQ(row1.id(), cached_row1.id()); 3145 EXPECT_EQ(row2.id(), cached_row2.id()); 3146 3147 // Verify that lookups will actually return both search terms; and also check 3148 // at the low level that the rows are there. 3149 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1)); 3150 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2)); 3151 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL)); 3152 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL)); 3153 } 3154 3155 TEST_F(InMemoryHistoryBackendTest, DeleteKeywordSearchTerms) { 3156 URLRow row1(CreateTestTypedURL()); 3157 URLRow row2(CreateTestNonTypedURL()); 3158 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1)); 3159 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2)); 3160 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2); 3161 3162 // Delete both search terms. This should be reflected in the in-memory DB. 3163 backend_->DeleteKeywordSearchTermForURL(row1.url()); 3164 backend_->DeleteKeywordSearchTermForURL(row2.url()); 3165 3166 // The typed URL should remain intact. 3167 // Note: we do not need to guarantee anything about the non-typed URL. 3168 URLRow cached_row1; 3169 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1)); 3170 EXPECT_EQ(row1.id(), cached_row1.id()); 3171 3172 // Verify that the search terms are no longer returned as results, and also 3173 // check at the low level that they are gone for good. 3174 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1)); 3175 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2)); 3176 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL)); 3177 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL)); 3178 } 3179 3180 TEST_F(InMemoryHistoryBackendTest, DeleteAllSearchTermsForKeyword) { 3181 URLRow row1(CreateTestTypedURL()); 3182 URLRow row2(CreateTestNonTypedURL()); 3183 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1)); 3184 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2)); 3185 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2); 3186 3187 // Delete all corresponding search terms from the in-memory database. 3188 KeywordID id = kTestKeywordId; 3189 mem_backend_->DeleteAllSearchTermsForKeyword(id); 3190 3191 // The typed URL should remain intact. 3192 // Note: we do not need to guarantee anything about the non-typed URL. 3193 URLRow cached_row1; 3194 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1)); 3195 EXPECT_EQ(row1.id(), cached_row1.id()); 3196 3197 // Verify that the search terms are no longer returned as results, and also 3198 // check at the low level that they are gone for good. 3199 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1)); 3200 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2)); 3201 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL)); 3202 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL)); 3203 } 3204 3205 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedWithSearchTerms) { 3206 URLRow row1(CreateTestTypedURL()); 3207 URLRow row2(CreateTestNonTypedURL()); 3208 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1)); 3209 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2)); 3210 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2); 3211 3212 // Notify the in-memory database that the second typed URL has been deleted. 3213 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_DELETED, &row2); 3214 3215 // Verify that the second term is no longer returned as result, and also check 3216 // at the low level that it is gone for good. The term corresponding to the 3217 // first URLRow should not be affected. 3218 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1)); 3219 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2)); 3220 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL)); 3221 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL)); 3222 } 3223 3224 } // namespace history 3225