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