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.
5 #include "chrome/browser/history/history_backend.h"
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/command_line.h"
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/path_service.h"
20 #include "base/run_loop.h"
21 #include "base/strings/string16.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "chrome/browser/chrome_notification_types.h"
25 #include "chrome/browser/history/history_notifications.h"
26 #include "chrome/browser/history/history_service.h"
27 #include "chrome/browser/history/history_service_factory.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/browser/in_memory_database.h"
35 #include "components/history/core/browser/keyword_search_term.h"
36 #include "components/history/core/test/history_client_fake_bookmarks.h"
37 #include "content/public/browser/notification_details.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/test/test_browser_thread.h"
40 #include "testing/gmock/include/gmock/gmock.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42 #include "third_party/skia/include/core/SkBitmap.h"
43 #include "ui/gfx/codec/png_codec.h"
48 // This file only tests functionality where it is most convenient to call the
49 // backend directly. Most of the history backend functions are tested by the
50 // history unit test. Because of the elaborate callbacks involved, this is no
51 // harder than calling it directly for many things.
55 const int kTinyEdgeSize = 10;
56 const int kSmallEdgeSize = 16;
57 const int kLargeEdgeSize = 32;
59 const gfx::Size kTinySize = gfx::Size(kTinyEdgeSize, kTinyEdgeSize);
60 const gfx::Size kSmallSize = gfx::Size(kSmallEdgeSize, kSmallEdgeSize);
61 const gfx::Size kLargeSize = gfx::Size(kLargeEdgeSize, kLargeEdgeSize);
63 typedef base::Callback<void(const history::URLRow*,
64 const history::URLRow*,
65 const history::URLRow*)>
66 SimulateNotificationCallback;
68 class HistoryClientMock : public history::HistoryClientFakeBookmarks {
70 MOCK_METHOD0(BlockUntilBookmarksLoaded, void());
73 void SimulateNotificationURLVisited(history::HistoryServiceObserver* observer,
74 const history::URLRow* row1,
75 const history::URLRow* row2,
76 const history::URLRow* row3) {
77 history::URLRows rows;
78 rows.push_back(*row1);
80 rows.push_back(*row2);
82 rows.push_back(*row3);
84 base::Time visit_time;
85 history::RedirectList redirects;
86 for (const auto& row : rows) {
87 observer->OnURLVisited(
88 nullptr, ui::PAGE_TRANSITION_LINK, row, redirects, visit_time);
96 class HistoryBackendTestBase;
98 // This must be a separate object since HistoryBackend manages its lifetime.
99 // This just forwards the messages we're interested in to the test object.
100 class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
102 explicit HistoryBackendTestDelegate(HistoryBackendTestBase* test)
105 void NotifyProfileError(sql::InitStatus init_status) override {}
106 void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) override;
107 void NotifyAddVisit(const BriefVisitInfo& info) override {}
108 void NotifyFaviconChanged(const std::set<GURL>& urls) override;
109 void NotifyURLVisited(ui::PageTransition transition,
111 const RedirectList& redirects,
112 base::Time visit_time) override;
113 void BroadcastNotifications(int type,
114 scoped_ptr<HistoryDetails> details) override;
115 void DBLoaded() override;
119 HistoryBackendTestBase* test_;
121 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
124 class HistoryBackendTestBase : public testing::Test {
126 typedef std::vector<std::pair<int, HistoryDetails*> > NotificationList;
127 typedef std::vector<std::pair<ui::PageTransition, URLRow>> URLVisitedList;
129 HistoryBackendTestBase()
131 favicon_changed_notifications_(0),
132 ui_thread_(content::BrowserThread::UI, &message_loop_) {}
134 ~HistoryBackendTestBase() override {
135 STLDeleteValues(&broadcasted_notifications_);
139 int favicon_changed_notifications() const {
140 return favicon_changed_notifications_;
143 void ClearFaviconChangedNotificationCounter() {
144 favicon_changed_notifications_ = 0;
147 int num_url_visited_notifications() const {
148 return url_visited_notifications_.size();
151 const URLVisitedList& url_visited_notifications() const {
152 return url_visited_notifications_;
155 int num_broadcasted_notifications() const {
156 return broadcasted_notifications_.size();
159 const NotificationList& broadcasted_notifications() const {
160 return broadcasted_notifications_;
163 void ClearBroadcastedNotifications() {
164 url_visited_notifications_.clear();
165 STLDeleteValues(&broadcasted_notifications_);
168 base::FilePath test_dir() {
172 void NotifyFaviconChanged(const std::set<GURL>& changed_favicons) {
173 ++favicon_changed_notifications_;
176 void NotifyURLVisited(ui::PageTransition transition,
178 const RedirectList& redirects,
179 base::Time visit_time) {
180 url_visited_notifications_.push_back(std::make_pair(transition, row));
183 void BroadcastNotifications(int type, scoped_ptr<HistoryDetails> details) {
184 // Send the notifications directly to the in-memory database.
185 content::Details<HistoryDetails> det(details.get());
186 mem_backend_->Observe(
187 type, content::Source<HistoryBackendTestBase>(NULL), det);
189 // The backend passes ownership of the details pointer to us.
190 broadcasted_notifications_.push_back(
191 std::make_pair(type, details.release()));
194 history::HistoryClientFakeBookmarks history_client_;
195 scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
196 scoped_ptr<InMemoryHistoryBackend> mem_backend_;
200 friend class HistoryBackendTestDelegate;
203 void SetUp() override {
204 ClearFaviconChangedNotificationCounter();
205 if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
208 backend_ = new HistoryBackend(
209 test_dir_, new HistoryBackendTestDelegate(this), &history_client_);
210 backend_->Init(std::string(), false);
213 void TearDown() override {
217 mem_backend_.reset();
218 base::DeleteFile(test_dir_, true);
219 base::RunLoop().RunUntilIdle();
220 history_client_.ClearAllBookmarks();
223 void SetInMemoryBackend(scoped_ptr<InMemoryHistoryBackend> backend) {
224 mem_backend_.swap(backend);
227 // The types and details of notifications which were broadcasted.
228 NotificationList broadcasted_notifications_;
229 int favicon_changed_notifications_;
230 URLVisitedList url_visited_notifications_;
232 base::MessageLoop message_loop_;
233 base::FilePath test_dir_;
234 content::TestBrowserThread ui_thread_;
236 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestBase);
239 void HistoryBackendTestDelegate::SetInMemoryBackend(
240 scoped_ptr<InMemoryHistoryBackend> backend) {
241 test_->SetInMemoryBackend(backend.Pass());
244 void HistoryBackendTestDelegate::NotifyFaviconChanged(
245 const std::set<GURL>& changed_favicons) {
246 test_->NotifyFaviconChanged(changed_favicons);
249 void HistoryBackendTestDelegate::NotifyURLVisited(ui::PageTransition transition,
251 const RedirectList& redirects,
252 base::Time visit_time) {
253 test_->NotifyURLVisited(transition, row, redirects, visit_time);
256 void HistoryBackendTestDelegate::BroadcastNotifications(
258 scoped_ptr<HistoryDetails> details) {
259 test_->BroadcastNotifications(type, details.Pass());
262 void HistoryBackendTestDelegate::DBLoaded() {
263 test_->loaded_ = true;
266 class HistoryBackendTest : public HistoryBackendTestBase {
268 HistoryBackendTest() {}
269 ~HistoryBackendTest() override {}
272 void AddRedirectChain(const char* sequence[], int page_id) {
273 AddRedirectChainWithTransitionAndTime(sequence, page_id,
274 ui::PAGE_TRANSITION_LINK,
278 void AddRedirectChainWithTransitionAndTime(
279 const char* sequence[],
281 ui::PageTransition transition,
283 history::RedirectList redirects;
284 for (int i = 0; sequence[i] != NULL; ++i)
285 redirects.push_back(GURL(sequence[i]));
287 ContextID context_id = reinterpret_cast<ContextID>(1);
288 history::HistoryAddPageArgs request(
289 redirects.back(), time, context_id, page_id, GURL(),
290 redirects, transition, history::SOURCE_BROWSED,
292 backend_->AddPage(request);
295 // Adds CLIENT_REDIRECT page transition.
296 // |url1| is the source URL and |url2| is the destination.
297 // |did_replace| is true if the transition is non-user initiated and the
298 // navigation entry for |url2| has replaced that for |url1|. The possibly
299 // updated transition code of the visit records for |url1| and |url2| is
300 // returned by filling in |*transition1| and |*transition2|, respectively.
301 // |time| is a time of the redirect.
302 void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
304 int* transition1, int* transition2) {
305 ContextID dummy_context_id = reinterpret_cast<ContextID>(0x87654321);
306 history::RedirectList redirects;
308 redirects.push_back(url1);
310 redirects.push_back(url2);
311 HistoryAddPageArgs request(
312 url2, time, dummy_context_id, 0, url1,
313 redirects, ui::PAGE_TRANSITION_CLIENT_REDIRECT,
314 history::SOURCE_BROWSED, did_replace);
315 backend_->AddPage(request);
317 *transition1 = GetTransition(url1);
318 *transition2 = GetTransition(url2);
321 int GetTransition(const GURL& url) {
325 URLID id = backend_->db()->GetRowForURL(url, &row);
327 EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
328 return visits[0].transition;
331 // Returns a vector with the small and large edge sizes.
332 const std::vector<int> GetEdgeSizesSmallAndLarge() {
333 std::vector<int> sizes_small_and_large;
334 sizes_small_and_large.push_back(kSmallEdgeSize);
335 sizes_small_and_large.push_back(kLargeEdgeSize);
336 return sizes_small_and_large;
339 // Returns the number of icon mappings of |icon_type| to |page_url|.
340 size_t NumIconMappingsForPageURL(const GURL& page_url,
341 favicon_base::IconType icon_type) {
342 std::vector<IconMapping> icon_mappings;
343 backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type,
345 return icon_mappings.size();
348 // Returns the icon mappings for |page_url| sorted alphabetically by icon
349 // URL in ascending order. Returns true if there is at least one icon
351 bool GetSortedIconMappingsForPageURL(
352 const GURL& page_url,
353 std::vector<IconMapping>* icon_mappings) {
354 if (!backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
358 std::sort(icon_mappings->begin(), icon_mappings->end(),
359 [](const history::IconMapping& a, const history::IconMapping& b) {
360 return a.icon_url < b.icon_url;
365 // Returns the favicon bitmaps for |icon_id| sorted by pixel size in
366 // ascending order. Returns true if there is at least one favicon bitmap.
367 bool GetSortedFaviconBitmaps(favicon_base::FaviconID icon_id,
368 std::vector<FaviconBitmap>* favicon_bitmaps) {
369 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
372 favicon_bitmaps->begin(), favicon_bitmaps->end(),
373 [](const history::FaviconBitmap& a, const history::FaviconBitmap& b) {
374 return a.pixel_size.GetArea() < b.pixel_size.GetArea();
379 // Returns true if there is exactly one favicon bitmap associated to
380 // |favicon_id|. If true, returns favicon bitmap in output parameter.
381 bool GetOnlyFaviconBitmap(const favicon_base::FaviconID icon_id,
382 FaviconBitmap* favicon_bitmap) {
383 std::vector<FaviconBitmap> favicon_bitmaps;
384 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
386 if (favicon_bitmaps.size() != 1)
388 *favicon_bitmap = favicon_bitmaps[0];
392 // Creates an |edge_size|x|edge_size| bitmap of |color|.
393 SkBitmap CreateBitmap(SkColor color, int edge_size) {
395 bitmap.allocN32Pixels(edge_size, edge_size);
396 bitmap.eraseColor(color);
400 // Returns true if |bitmap_data| is equal to |expected_data|.
401 bool BitmapDataEqual(char expected_data,
402 scoped_refptr<base::RefCountedMemory> bitmap_data) {
403 return bitmap_data.get() &&
404 bitmap_data->size() == 1u &&
405 *bitmap_data->front() == expected_data;
408 // Returns true if |bitmap_data| is of |color|.
409 bool BitmapColorEqual(SkColor expected_color,
410 scoped_refptr<base::RefCountedMemory> bitmap_data) {
412 if (!gfx::PNGCodec::Decode(
413 bitmap_data->front(), bitmap_data->size(), &bitmap))
415 SkAutoLockPixels bitmap_lock(bitmap);
416 return expected_color == bitmap.getColor(0, 0);
420 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTest);
423 class InMemoryHistoryBackendTest : public HistoryBackendTestBase {
425 InMemoryHistoryBackendTest() {}
426 ~InMemoryHistoryBackendTest() override {}
428 // Public so that the method can be bound in test fixture using
429 // base::Bind(&InMemoryHistoryBackendTest::SimulateNotification, ...).
430 void SimulateNotification(int type,
432 const URLRow* row2 = NULL,
433 const URLRow* row3 = NULL) {
435 rows.push_back(*row1);
436 if (row2) rows.push_back(*row2);
437 if (row3) rows.push_back(*row3);
439 if (type == chrome::NOTIFICATION_HISTORY_URLS_MODIFIED) {
440 scoped_ptr<URLsModifiedDetails> details(new URLsModifiedDetails());
441 details->changed_urls.swap(rows);
442 BroadcastNotifications(type, details.Pass());
443 } else if (type == chrome::NOTIFICATION_HISTORY_URLS_DELETED) {
444 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails());
445 details->rows = rows;
446 BroadcastNotifications(type, details.Pass());
453 size_t GetNumberOfMatchingSearchTerms(const int keyword_id,
454 const base::string16& prefix) {
455 std::vector<KeywordSearchTermVisit> matching_terms;
456 mem_backend_->db()->GetMostRecentKeywordSearchTerms(
457 keyword_id, prefix, 1, &matching_terms);
458 return matching_terms.size();
461 static URLRow CreateTestTypedURL() {
462 URLRow url_row(GURL("https://www.google.com/"));
464 url_row.set_title(base::UTF8ToUTF16("Google Search"));
465 url_row.set_typed_count(1);
466 url_row.set_visit_count(1);
467 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(1));
471 static URLRow CreateAnotherTestTypedURL() {
472 URLRow url_row(GURL("https://maps.google.com/"));
474 url_row.set_title(base::UTF8ToUTF16("Google Maps"));
475 url_row.set_typed_count(2);
476 url_row.set_visit_count(3);
477 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(2));
481 static URLRow CreateTestNonTypedURL() {
482 URLRow url_row(GURL("https://news.google.com/"));
484 url_row.set_title(base::UTF8ToUTF16("Google News"));
485 url_row.set_visit_count(5);
486 url_row.set_last_visit(Time::Now() - base::TimeDelta::FromHours(3));
490 void PopulateTestURLsAndSearchTerms(URLRow* row1,
492 const base::string16& term1,
493 const base::string16& term2);
495 void TestAddingAndChangingURLRows(
496 const SimulateNotificationCallback& callback);
498 static const KeywordID kTestKeywordId;
499 static const char kTestSearchTerm1[];
500 static const char kTestSearchTerm2[];
503 DISALLOW_COPY_AND_ASSIGN(InMemoryHistoryBackendTest);
506 const KeywordID InMemoryHistoryBackendTest::kTestKeywordId = 42;
507 const char InMemoryHistoryBackendTest::kTestSearchTerm1[] = "banana";
508 const char InMemoryHistoryBackendTest::kTestSearchTerm2[] = "orange";
510 // http://crbug.com/114287
512 #define MAYBE_Loaded DISABLED_Loaded
514 #define MAYBE_Loaded Loaded
515 #endif // defined(OS_WIN)
516 TEST_F(HistoryBackendTest, MAYBE_Loaded) {
517 ASSERT_TRUE(backend_.get());
518 ASSERT_TRUE(loaded_);
521 TEST_F(HistoryBackendTest, DeleteAll) {
522 ASSERT_TRUE(backend_.get());
524 // Add two favicons, each with two bitmaps. Note that we add favicon2 before
525 // adding favicon1. This is so that favicon1 one gets ID 2 autoassigned to
526 // the database, which will change when the other one is deleted. This way
527 // we can test that updating works properly.
528 GURL favicon_url1("http://www.google.com/favicon.ico");
529 GURL favicon_url2("http://news.google.com/favicon.ico");
530 favicon_base::FaviconID favicon2 =
531 backend_->thumbnail_db_->AddFavicon(favicon_url2, favicon_base::FAVICON);
532 favicon_base::FaviconID favicon1 =
533 backend_->thumbnail_db_->AddFavicon(favicon_url1, favicon_base::FAVICON);
535 std::vector<unsigned char> data;
537 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
538 new base::RefCountedBytes(data), Time::Now(), kSmallSize));
540 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
541 new base::RefCountedBytes(data), Time::Now(), kLargeSize));
544 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
545 new base::RefCountedBytes(data), Time::Now(), kSmallSize));
547 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
548 new base::RefCountedBytes(data), Time::Now(), kLargeSize));
550 // First visit two URLs.
551 URLRow row1(GURL("http://www.google.com/"));
552 row1.set_visit_count(2);
553 row1.set_typed_count(1);
554 row1.set_last_visit(Time::Now());
555 backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
557 URLRow row2(GURL("http://news.google.com/"));
558 row2.set_visit_count(1);
559 row2.set_last_visit(Time::Now());
560 backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
563 rows.push_back(row2); // Reversed order for the same reason as favicons.
564 rows.push_back(row1);
565 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
567 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
568 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
570 // Get the two visits for the URLs we just added.
572 backend_->db_->GetVisitsForURL(row1_id, &visits);
573 ASSERT_EQ(1U, visits.size());
576 backend_->db_->GetVisitsForURL(row2_id, &visits);
577 ASSERT_EQ(1U, visits.size());
579 // The in-memory backend should have been set and it should have gotten the
581 ASSERT_TRUE(mem_backend_.get());
583 EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
586 history_client_.AddBookmark(row1.url());
588 // Now finally clear all history.
589 ClearBroadcastedNotifications();
590 backend_->DeleteAllHistory();
592 // The first URL should be preserved but the time should be cleared.
593 EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
594 EXPECT_EQ(row1.url(), outrow1.url());
595 EXPECT_EQ(0, outrow1.visit_count());
596 EXPECT_EQ(0, outrow1.typed_count());
597 EXPECT_TRUE(Time() == outrow1.last_visit());
599 // The second row should be deleted.
601 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
603 // All visits should be deleted for both URLs.
604 VisitVector all_visits;
605 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
606 ASSERT_EQ(0U, all_visits.size());
608 // We should have a favicon and favicon bitmaps for the first URL only. We
609 // look them up by favicon URL since the IDs may have changed.
610 favicon_base::FaviconID out_favicon1 =
611 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
612 favicon_url1, favicon_base::FAVICON, NULL);
613 EXPECT_TRUE(out_favicon1);
615 std::vector<FaviconBitmap> favicon_bitmaps;
616 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
617 out_favicon1, &favicon_bitmaps));
618 ASSERT_EQ(2u, favicon_bitmaps.size());
620 FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0];
621 FaviconBitmap favicon_bitmap2 = favicon_bitmaps[1];
623 // Favicon bitmaps do not need to be in particular order.
624 if (favicon_bitmap1.pixel_size == kLargeSize) {
625 FaviconBitmap tmp_favicon_bitmap = favicon_bitmap1;
626 favicon_bitmap1 = favicon_bitmap2;
627 favicon_bitmap2 = tmp_favicon_bitmap;
630 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap1.bitmap_data));
631 EXPECT_EQ(kSmallSize, favicon_bitmap1.pixel_size);
633 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap2.bitmap_data));
634 EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size);
636 favicon_base::FaviconID out_favicon2 =
637 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
638 favicon_url2, favicon_base::FAVICON, NULL);
639 EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
641 // The remaining URL should still reference the same favicon, even if its
643 std::vector<IconMapping> mappings;
644 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
645 outrow1.url(), favicon_base::FAVICON, &mappings));
646 EXPECT_EQ(1u, mappings.size());
647 EXPECT_EQ(out_favicon1, mappings[0].icon_id);
649 // The first URL should still be bookmarked.
650 EXPECT_TRUE(history_client_.IsBookmarked(row1.url()));
652 // Check that we fire the notification about all history having been deleted.
653 ASSERT_EQ(1u, broadcasted_notifications().size());
654 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
655 broadcasted_notifications()[0].first);
656 const URLsDeletedDetails* details = static_cast<const URLsDeletedDetails*>(
657 broadcasted_notifications()[0].second);
658 EXPECT_TRUE(details->all_history);
659 EXPECT_FALSE(details->expired);
662 // Checks that adding a visit, then calling DeleteAll, and then trying to add
663 // data for the visited page works. This can happen when clearing the history
664 // immediately after visiting a page.
665 TEST_F(HistoryBackendTest, DeleteAllThenAddData) {
666 ASSERT_TRUE(backend_.get());
668 Time visit_time = Time::Now();
669 GURL url("http://www.google.com/");
670 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
671 history::RedirectList(),
672 ui::PAGE_TRANSITION_KEYWORD_GENERATED,
673 history::SOURCE_BROWSED, false);
674 backend_->AddPage(request);
676 // Check that a row was added.
678 EXPECT_TRUE(backend_->db_->GetRowForURL(url, &outrow));
680 // Check that the visit was added.
681 VisitVector all_visits;
682 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
683 ASSERT_EQ(1U, all_visits.size());
685 // Clear all history.
686 backend_->DeleteAllHistory();
688 // The row should be deleted.
689 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
691 // The visit should be deleted.
692 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
693 ASSERT_EQ(0U, all_visits.size());
695 // Try and set the title.
696 backend_->SetPageTitle(url, base::UTF8ToUTF16("Title"));
698 // The row should still be deleted.
699 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
701 // The visit should still be deleted.
702 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
703 ASSERT_EQ(0U, all_visits.size());
706 TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
707 GURL favicon_url1("http://www.google.com/favicon.ico");
708 GURL favicon_url2("http://news.google.com/favicon.ico");
710 std::vector<unsigned char> data;
712 favicon_base::FaviconID favicon1 =
713 backend_->thumbnail_db_->AddFavicon(favicon_url1,
714 favicon_base::FAVICON,
715 new base::RefCountedBytes(data),
720 favicon_base::FaviconID favicon2 =
721 backend_->thumbnail_db_->AddFavicon(favicon_url2,
722 favicon_base::FAVICON,
723 new base::RefCountedBytes(data),
727 // First visit two URLs.
728 URLRow row1(GURL("http://www.google.com/"));
729 row1.set_visit_count(2);
730 row1.set_typed_count(1);
731 row1.set_last_visit(Time::Now());
732 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
734 URLRow row2(GURL("http://news.google.com/"));
735 row2.set_visit_count(1);
736 row2.set_last_visit(Time::Now());
737 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
740 rows.push_back(row2); // Reversed order for the same reason as favicons.
741 rows.push_back(row1);
742 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
744 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
745 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
747 // Star the two URLs.
748 history_client_.AddBookmark(row1.url());
749 history_client_.AddBookmark(row2.url());
751 // Delete url 2. Because url 2 is starred this won't delete the URL, only
753 backend_->expirer_.DeleteURL(row2.url());
755 // Make sure url 2 is still valid, but has no visits.
757 EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
759 backend_->db_->GetVisitsForURL(row2_id, &visits);
760 EXPECT_EQ(0U, visits.size());
761 // The favicon should still be valid.
763 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
764 favicon_url2, favicon_base::FAVICON, NULL));
767 history_client_.DelBookmark(row2.url());
769 // Tell the backend it was unstarred. We have to explicitly do this as
770 // BookmarkModel isn't wired up to the backend during testing.
771 std::set<GURL> unstarred_urls;
772 unstarred_urls.insert(row2.url());
773 backend_->URLsNoLongerBookmarked(unstarred_urls);
775 // The URL should no longer exist.
776 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
777 // And the favicon should be deleted.
779 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
780 favicon_url2, favicon_base::FAVICON, NULL));
783 history_client_.DelBookmark(row1.url());
785 // Tell the backend it was unstarred. We have to explicitly do this as
786 // BookmarkModel isn't wired up to the backend during testing.
787 unstarred_urls.clear();
788 unstarred_urls.insert(row1.url());
789 backend_->URLsNoLongerBookmarked(unstarred_urls);
791 // The URL should still exist (because there were visits).
792 EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
794 // There should still be visits.
796 backend_->db_->GetVisitsForURL(row1_id, &visits);
797 EXPECT_EQ(1U, visits.size());
799 // The favicon should still be valid.
801 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
802 favicon_url1, favicon_base::FAVICON, NULL));
805 // Tests a handful of assertions for a navigation with a type of
806 // KEYWORD_GENERATED.
807 TEST_F(HistoryBackendTest, KeywordGenerated) {
808 ASSERT_TRUE(backend_.get());
810 GURL url("http://google.com");
812 Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
813 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
814 history::RedirectList(),
815 ui::PAGE_TRANSITION_KEYWORD_GENERATED,
816 history::SOURCE_BROWSED, false);
817 backend_->AddPage(request);
819 // A row should have been added for the url.
821 URLID url_id = backend_->db()->GetRowForURL(url, &row);
822 ASSERT_NE(0, url_id);
824 // The typed count should be 1.
825 ASSERT_EQ(1, row.typed_count());
827 // KEYWORD_GENERATED urls should not be added to the segment db.
828 std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
829 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
831 // One visit should be added.
833 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
834 EXPECT_EQ(1U, visits.size());
836 // But no visible visits.
838 QueryOptions query_options;
839 query_options.max_count = 1;
840 backend_->db()->GetVisibleVisitsInRange(query_options, &visits);
841 EXPECT_TRUE(visits.empty());
843 // Expire the visits.
844 std::set<GURL> restrict_urls;
845 backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
846 visit_time, Time::Now());
848 // The visit should have been nuked.
850 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
851 EXPECT_TRUE(visits.empty());
853 // As well as the url.
854 ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
857 TEST_F(HistoryBackendTest, ClientRedirect) {
858 ASSERT_TRUE(backend_.get());
863 // Initial transition to page A.
864 GURL url_a("http://google.com/a");
865 AddClientRedirect(GURL(), url_a, false, base::Time(),
866 &transition1, &transition2);
867 EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
869 // User initiated redirect to page B.
870 GURL url_b("http://google.com/b");
871 AddClientRedirect(url_a, url_b, false, base::Time(),
872 &transition1, &transition2);
873 EXPECT_TRUE(transition1 & ui::PAGE_TRANSITION_CHAIN_END);
874 EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
876 // Non-user initiated redirect to page C.
877 GURL url_c("http://google.com/c");
878 AddClientRedirect(url_b, url_c, true, base::Time(),
879 &transition1, &transition2);
880 EXPECT_FALSE(transition1 & ui::PAGE_TRANSITION_CHAIN_END);
881 EXPECT_TRUE(transition2 & ui::PAGE_TRANSITION_CHAIN_END);
884 TEST_F(HistoryBackendTest, AddPagesWithDetails) {
885 ASSERT_TRUE(backend_.get());
887 // Import one non-typed URL, and two recent and one expired typed URLs.
888 URLRow row1(GURL("https://news.google.com/"));
889 row1.set_visit_count(1);
890 row1.set_last_visit(Time::Now());
891 URLRow row2(GURL("https://www.google.com/"));
892 row2.set_typed_count(1);
893 row2.set_last_visit(Time::Now());
894 URLRow row3(GURL("https://mail.google.com/"));
895 row3.set_visit_count(1);
896 row3.set_typed_count(1);
897 row3.set_last_visit(Time::Now() - base::TimeDelta::FromDays(7 - 1));
898 URLRow row4(GURL("https://maps.google.com/"));
899 row4.set_visit_count(1);
900 row4.set_typed_count(1);
901 row4.set_last_visit(Time::Now() - base::TimeDelta::FromDays(365 + 2));
904 rows.push_back(row1);
905 rows.push_back(row2);
906 rows.push_back(row3);
907 rows.push_back(row4);
908 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
910 // Verify that recent URLs have ended up in the main |db_|, while the already
911 // expired URL has been ignored.
912 URLRow stored_row1, stored_row2, stored_row3, stored_row4;
913 EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1));
914 EXPECT_NE(0, backend_->db_->GetRowForURL(row2.url(), &stored_row2));
915 EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3));
916 EXPECT_EQ(0, backend_->db_->GetRowForURL(row4.url(), &stored_row4));
918 // Ensure that a notification was fired for both typed and non-typed URLs.
919 // Further verify that the IDs in the notification are set to those that are
920 // in effect in the main database. The InMemoryHistoryBackend relies on this
922 ASSERT_EQ(1u, broadcasted_notifications().size());
923 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
924 broadcasted_notifications()[0].first);
925 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>(
926 broadcasted_notifications()[0].second);
927 EXPECT_EQ(3u, details->changed_urls.size());
929 URLRows::const_iterator it_row1 = std::find_if(
930 details->changed_urls.begin(),
931 details->changed_urls.end(),
932 history::URLRow::URLRowHasURL(row1.url()));
933 ASSERT_NE(details->changed_urls.end(), it_row1);
934 EXPECT_EQ(stored_row1.id(), it_row1->id());
936 URLRows::const_iterator it_row2 = std::find_if(
937 details->changed_urls.begin(),
938 details->changed_urls.end(),
939 history::URLRow::URLRowHasURL(row2.url()));
940 ASSERT_NE(details->changed_urls.end(), it_row2);
941 EXPECT_EQ(stored_row2.id(), it_row2->id());
943 URLRows::const_iterator it_row3 = std::find_if(
944 details->changed_urls.begin(),
945 details->changed_urls.end(),
946 history::URLRow::URLRowHasURL(row3.url()));
947 ASSERT_NE(details->changed_urls.end(), it_row3);
948 EXPECT_EQ(stored_row3.id(), it_row3->id());
951 TEST_F(HistoryBackendTest, UpdateURLs) {
952 ASSERT_TRUE(backend_.get());
954 // Add three pages directly to the database.
955 URLRow row1(GURL("https://news.google.com/"));
956 row1.set_visit_count(1);
957 row1.set_last_visit(Time::Now());
958 URLRow row2(GURL("https://maps.google.com/"));
959 row2.set_visit_count(2);
960 row2.set_last_visit(Time::Now());
961 URLRow row3(GURL("https://www.google.com/"));
962 row3.set_visit_count(3);
963 row3.set_last_visit(Time::Now());
965 backend_->db_->AddURL(row1);
966 backend_->db_->AddURL(row2);
967 backend_->db_->AddURL(row3);
969 // Now create changed versions of all URLRows by incrementing their visit
970 // counts, and in the meantime, also delete the second row from the database.
971 URLRow altered_row1, altered_row2, altered_row3;
972 backend_->db_->GetRowForURL(row1.url(), &altered_row1);
973 altered_row1.set_visit_count(42);
974 backend_->db_->GetRowForURL(row2.url(), &altered_row2);
975 altered_row2.set_visit_count(43);
976 backend_->db_->GetRowForURL(row3.url(), &altered_row3);
977 altered_row3.set_visit_count(44);
979 backend_->db_->DeleteURLRow(altered_row2.id());
981 // Now try to update all three rows at once. The change to the second URLRow
982 // should be ignored, as it is no longer present in the DB.
984 rows.push_back(altered_row1);
985 rows.push_back(altered_row2);
986 rows.push_back(altered_row3);
987 EXPECT_EQ(2u, backend_->UpdateURLs(rows));
989 URLRow stored_row1, stored_row3;
990 EXPECT_NE(0, backend_->db_->GetRowForURL(row1.url(), &stored_row1));
991 EXPECT_NE(0, backend_->db_->GetRowForURL(row3.url(), &stored_row3));
992 EXPECT_EQ(altered_row1.visit_count(), stored_row1.visit_count());
993 EXPECT_EQ(altered_row3.visit_count(), stored_row3.visit_count());
995 // Ensure that a notification was fired, and further verify that the IDs in
996 // the notification are set to those that are in effect in the main database.
997 // The InMemoryHistoryBackend relies on this for caching.
998 ASSERT_EQ(1u, broadcasted_notifications().size());
999 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1000 broadcasted_notifications()[0].first);
1001 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>(
1002 broadcasted_notifications()[0].second);
1003 EXPECT_EQ(2u, details->changed_urls.size());
1005 URLRows::const_iterator it_row1 =
1006 std::find_if(details->changed_urls.begin(),
1007 details->changed_urls.end(),
1008 history::URLRow::URLRowHasURL(row1.url()));
1009 ASSERT_NE(details->changed_urls.end(), it_row1);
1010 EXPECT_EQ(altered_row1.id(), it_row1->id());
1011 EXPECT_EQ(altered_row1.visit_count(), it_row1->visit_count());
1013 URLRows::const_iterator it_row3 =
1014 std::find_if(details->changed_urls.begin(),
1015 details->changed_urls.end(),
1016 history::URLRow::URLRowHasURL(row3.url()));
1017 ASSERT_NE(details->changed_urls.end(), it_row3);
1018 EXPECT_EQ(altered_row3.id(), it_row3->id());
1019 EXPECT_EQ(altered_row3.visit_count(), it_row3->visit_count());
1022 // This verifies that a notification is fired. In-depth testing of logic should
1023 // be done in HistoryTest.SetTitle.
1024 TEST_F(HistoryBackendTest, SetPageTitleFiresNotificationWithCorrectDetails) {
1025 const char kTestUrlTitle[] = "Google Search";
1027 ASSERT_TRUE(backend_.get());
1029 // Add two pages, then change the title of the second one.
1030 URLRow row1(GURL("https://news.google.com/"));
1031 row1.set_typed_count(1);
1032 row1.set_last_visit(Time::Now());
1033 URLRow row2(GURL("https://www.google.com/"));
1034 row2.set_visit_count(2);
1035 row2.set_last_visit(Time::Now());
1038 rows.push_back(row1);
1039 rows.push_back(row2);
1040 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
1042 ClearBroadcastedNotifications();
1043 backend_->SetPageTitle(row2.url(), base::UTF8ToUTF16(kTestUrlTitle));
1045 // Ensure that a notification was fired, and further verify that the IDs in
1046 // the notification are set to those that are in effect in the main database.
1047 // The InMemoryHistoryBackend relies on this for caching.
1049 EXPECT_TRUE(backend_->GetURL(row2.url(), &stored_row2));
1050 ASSERT_EQ(1u, broadcasted_notifications().size());
1051 ASSERT_EQ(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
1052 broadcasted_notifications()[0].first);
1053 const URLsModifiedDetails* details = static_cast<const URLsModifiedDetails*>(
1054 broadcasted_notifications()[0].second);
1055 ASSERT_EQ(1u, details->changed_urls.size());
1056 EXPECT_EQ(base::UTF8ToUTF16(kTestUrlTitle), details->changed_urls[0].title());
1057 EXPECT_EQ(stored_row2.id(), details->changed_urls[0].id());
1060 // There's no importer on Android.
1061 #if !defined(OS_ANDROID)
1062 TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
1063 // Setup test data - two Urls in the history, one with favicon assigned and
1065 GURL favicon_url1("http://www.google.com/favicon.ico");
1066 std::vector<unsigned char> data;
1067 data.push_back('1');
1068 favicon_base::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
1070 favicon_base::FAVICON,
1071 base::RefCountedBytes::TakeVector(&data),
1074 URLRow row1(GURL("http://www.google.com/"));
1075 row1.set_visit_count(1);
1076 row1.set_last_visit(Time::Now());
1077 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
1079 URLRow row2(GURL("http://news.google.com/"));
1080 row2.set_visit_count(1);
1081 row2.set_last_visit(Time::Now());
1083 rows.push_back(row1);
1084 rows.push_back(row2);
1085 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
1086 URLRow url_row1, url_row2;
1087 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
1088 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
1089 EXPECT_EQ(1u, NumIconMappingsForPageURL(row1.url(), favicon_base::FAVICON));
1090 EXPECT_EQ(0u, NumIconMappingsForPageURL(row2.url(), favicon_base::FAVICON));
1092 // Now provide one imported favicon for both URLs already in the registry.
1093 // The new favicon should only be used with the URL that doesn't already have
1095 std::vector<ImportedFaviconUsage> favicons;
1096 ImportedFaviconUsage favicon;
1097 favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
1098 favicon.png_data.push_back('2');
1099 favicon.urls.insert(row1.url());
1100 favicon.urls.insert(row2.url());
1101 favicons.push_back(favicon);
1102 backend_->SetImportedFavicons(favicons);
1103 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
1104 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
1106 std::vector<IconMapping> mappings;
1107 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1108 row1.url(), favicon_base::FAVICON, &mappings));
1109 EXPECT_EQ(1u, mappings.size());
1110 EXPECT_EQ(favicon1, mappings[0].icon_id);
1111 EXPECT_EQ(favicon_url1, mappings[0].icon_url);
1114 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1115 row2.url(), favicon_base::FAVICON, &mappings));
1116 EXPECT_EQ(1u, mappings.size());
1117 EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url);
1119 // A URL should not be added to history (to store favicon), if
1120 // the URL is not bookmarked.
1121 GURL url3("http://mail.google.com");
1123 favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
1124 favicon.png_data.push_back('3');
1125 favicon.urls.insert(url3);
1126 favicons.push_back(favicon);
1127 backend_->SetImportedFavicons(favicons);
1129 EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
1131 // If the URL is bookmarked, it should get added to history with 0 visits.
1132 history_client_.AddBookmark(url3);
1133 backend_->SetImportedFavicons(favicons);
1134 EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
1135 EXPECT_TRUE(url_row3.visit_count() == 0);
1137 #endif // !defined(OS_ANDROID)
1139 TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
1140 ASSERT_TRUE(backend_.get());
1142 GURL url("http://anyuser:anypass@www.google.com");
1143 GURL stripped_url("http://www.google.com");
1145 // Clear all history.
1146 backend_->DeleteAllHistory();
1148 // Visit the url with username, password.
1149 backend_->AddPageVisit(url, base::Time::Now(), 0,
1150 ui::PageTransitionFromInt(
1151 ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
1152 history::SOURCE_BROWSED);
1154 // Fetch the row information about stripped url from history db.
1156 URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
1157 backend_->db_->GetVisitsForURL(row_id, &visits);
1159 // Check if stripped url is stored in database.
1160 ASSERT_EQ(1U, visits.size());
1163 TEST_F(HistoryBackendTest, AddPageVisitSource) {
1164 ASSERT_TRUE(backend_.get());
1166 GURL url("http://www.google.com");
1168 // Clear all history.
1169 backend_->DeleteAllHistory();
1171 // Assume visiting the url from an externsion.
1172 backend_->AddPageVisit(
1173 url, base::Time::Now(), 0, ui::PAGE_TRANSITION_TYPED,
1174 history::SOURCE_EXTENSION);
1175 // Assume the url is imported from Firefox.
1176 backend_->AddPageVisit(url, base::Time::Now(), 0,
1177 ui::PAGE_TRANSITION_TYPED,
1178 history::SOURCE_FIREFOX_IMPORTED);
1179 // Assume this url is also synced.
1180 backend_->AddPageVisit(url, base::Time::Now(), 0,
1181 ui::PAGE_TRANSITION_TYPED,
1182 history::SOURCE_SYNCED);
1184 // Fetch the row information about the url from history db.
1186 URLID row_id = backend_->db_->GetRowForURL(url, NULL);
1187 backend_->db_->GetVisitsForURL(row_id, &visits);
1189 // Check if all the visits to the url are stored in database.
1190 ASSERT_EQ(3U, visits.size());
1191 VisitSourceMap visit_sources;
1192 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1193 ASSERT_EQ(3U, visit_sources.size());
1195 for (int i = 0; i < 3; i++) {
1196 switch (visit_sources[visits[i].visit_id]) {
1197 case history::SOURCE_EXTENSION:
1200 case history::SOURCE_FIREFOX_IMPORTED:
1203 case history::SOURCE_SYNCED:
1209 EXPECT_EQ(0x7, sources);
1212 TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) {
1213 ASSERT_TRUE(backend_.get());
1215 GURL url("http://www.google.com");
1217 // Clear all history.
1218 backend_->DeleteAllHistory();
1220 // Create visit times
1221 base::Time recent_time = base::Time::Now();
1222 base::TimeDelta visit_age = base::TimeDelta::FromDays(3);
1223 base::Time older_time = recent_time - visit_age;
1225 // Visit the url with recent time.
1226 backend_->AddPageVisit(url, recent_time, 0,
1227 ui::PageTransitionFromInt(
1228 ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
1229 history::SOURCE_BROWSED);
1231 // Add to the url a visit with older time (could be syncing from another
1233 backend_->AddPageVisit(url, older_time, 0,
1234 ui::PageTransitionFromInt(
1235 ui::PageTransitionGetQualifier(ui::PAGE_TRANSITION_TYPED)),
1236 history::SOURCE_SYNCED);
1238 // Fetch the row information about url from history db.
1241 URLID row_id = backend_->db_->GetRowForURL(url, &row);
1242 backend_->db_->GetVisitsForURL(row_id, &visits);
1244 // Last visit time should be the most recent time, not the most recently added
1246 ASSERT_EQ(2U, visits.size());
1247 ASSERT_EQ(recent_time, row.last_visit());
1250 TEST_F(HistoryBackendTest, AddPageVisitFiresNotificationWithCorrectDetails) {
1251 ASSERT_TRUE(backend_.get());
1253 GURL url1("http://www.google.com");
1254 GURL url2("http://maps.google.com");
1256 // Clear all history.
1257 backend_->DeleteAllHistory();
1258 ClearBroadcastedNotifications();
1260 // Visit two distinct URLs, the second one twice.
1261 backend_->AddPageVisit(url1, base::Time::Now(), 0,
1262 ui::PAGE_TRANSITION_LINK,
1263 history::SOURCE_BROWSED);
1264 for (int i = 0; i < 2; ++i) {
1265 backend_->AddPageVisit(url2, base::Time::Now(), 0,
1266 ui::PAGE_TRANSITION_TYPED,
1267 history::SOURCE_BROWSED);
1270 URLRow stored_row1, stored_row2;
1271 EXPECT_NE(0, backend_->db_->GetRowForURL(url1, &stored_row1));
1272 EXPECT_NE(0, backend_->db_->GetRowForURL(url2, &stored_row2));
1274 // Expect that HistoryServiceObserver::OnURLVisited has been called 3 times,
1275 // and that each time the URLRows have the correct URLs and IDs set.
1276 ASSERT_EQ(3, num_url_visited_notifications());
1277 EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[0].first,
1278 ui::PAGE_TRANSITION_LINK));
1279 EXPECT_EQ(stored_row1.id(), url_visited_notifications()[0].second.id());
1280 EXPECT_EQ(stored_row1.url(), url_visited_notifications()[0].second.url());
1282 EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[1].first,
1283 ui::PAGE_TRANSITION_TYPED));
1284 EXPECT_EQ(stored_row2.id(), url_visited_notifications()[1].second.id());
1285 EXPECT_EQ(stored_row2.url(), url_visited_notifications()[1].second.url());
1287 EXPECT_TRUE(ui::PageTransitionCoreTypeIs(url_visited_notifications()[2].first,
1288 ui::PAGE_TRANSITION_TYPED));
1289 EXPECT_EQ(stored_row2.id(), url_visited_notifications()[2].second.id());
1290 EXPECT_EQ(stored_row2.url(), url_visited_notifications()[2].second.url());
1293 TEST_F(HistoryBackendTest, AddPageArgsSource) {
1294 ASSERT_TRUE(backend_.get());
1296 GURL url("http://testpageargs.com");
1298 // Assume this page is browsed by user.
1299 HistoryAddPageArgs request1(url, base::Time::Now(), NULL, 0, GURL(),
1300 history::RedirectList(),
1301 ui::PAGE_TRANSITION_KEYWORD_GENERATED,
1302 history::SOURCE_BROWSED, false);
1303 backend_->AddPage(request1);
1304 // Assume this page is synced.
1305 HistoryAddPageArgs request2(url, base::Time::Now(), NULL, 0, GURL(),
1306 history::RedirectList(),
1307 ui::PAGE_TRANSITION_LINK,
1308 history::SOURCE_SYNCED, false);
1309 backend_->AddPage(request2);
1310 // Assume this page is browsed again.
1311 HistoryAddPageArgs request3(url, base::Time::Now(), NULL, 0, GURL(),
1312 history::RedirectList(),
1313 ui::PAGE_TRANSITION_TYPED,
1314 history::SOURCE_BROWSED, false);
1315 backend_->AddPage(request3);
1317 // Three visits should be added with proper sources.
1320 URLID id = backend_->db()->GetRowForURL(url, &row);
1321 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1322 ASSERT_EQ(3U, visits.size());
1323 VisitSourceMap visit_sources;
1324 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1325 ASSERT_EQ(1U, visit_sources.size());
1326 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
1329 TEST_F(HistoryBackendTest, AddVisitsSource) {
1330 ASSERT_TRUE(backend_.get());
1332 GURL url1("http://www.cnn.com");
1333 std::vector<VisitInfo> visits1, visits2;
1334 visits1.push_back(VisitInfo(
1335 Time::Now() - base::TimeDelta::FromDays(5),
1336 ui::PAGE_TRANSITION_LINK));
1337 visits1.push_back(VisitInfo(
1338 Time::Now() - base::TimeDelta::FromDays(1),
1339 ui::PAGE_TRANSITION_LINK));
1340 visits1.push_back(VisitInfo(
1341 Time::Now(), ui::PAGE_TRANSITION_LINK));
1343 GURL url2("http://www.example.com");
1344 visits2.push_back(VisitInfo(
1345 Time::Now() - base::TimeDelta::FromDays(10),
1346 ui::PAGE_TRANSITION_LINK));
1347 visits2.push_back(VisitInfo(Time::Now(), ui::PAGE_TRANSITION_LINK));
1349 // Clear all history.
1350 backend_->DeleteAllHistory();
1353 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1354 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
1356 // Verify the visits were added with their sources.
1359 URLID id = backend_->db()->GetRowForURL(url1, &row);
1360 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1361 ASSERT_EQ(3U, visits.size());
1362 VisitSourceMap visit_sources;
1363 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1364 ASSERT_EQ(3U, visit_sources.size());
1365 for (int i = 0; i < 3; i++)
1366 EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
1367 id = backend_->db()->GetRowForURL(url2, &row);
1368 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1369 ASSERT_EQ(2U, visits.size());
1370 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1371 ASSERT_EQ(2U, visit_sources.size());
1372 for (int i = 0; i < 2; i++)
1373 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
1376 TEST_F(HistoryBackendTest, GetMostRecentVisits) {
1377 ASSERT_TRUE(backend_.get());
1379 GURL url1("http://www.cnn.com");
1380 std::vector<VisitInfo> visits1;
1381 visits1.push_back(VisitInfo(
1382 Time::Now() - base::TimeDelta::FromDays(5),
1383 ui::PAGE_TRANSITION_LINK));
1384 visits1.push_back(VisitInfo(
1385 Time::Now() - base::TimeDelta::FromDays(1),
1386 ui::PAGE_TRANSITION_LINK));
1387 visits1.push_back(VisitInfo(
1388 Time::Now(), ui::PAGE_TRANSITION_LINK));
1390 // Clear all history.
1391 backend_->DeleteAllHistory();
1394 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1396 // Verify the visits were added with their sources.
1399 URLID id = backend_->db()->GetRowForURL(url1, &row);
1400 ASSERT_TRUE(backend_->db()->GetMostRecentVisitsForURL(id, 1, &visits));
1401 ASSERT_EQ(1U, visits.size());
1402 EXPECT_EQ(visits1[2].first, visits[0].visit_time);
1405 TEST_F(HistoryBackendTest, RemoveVisitsTransitions) {
1406 ASSERT_TRUE(backend_.get());
1408 // Clear all history.
1409 backend_->DeleteAllHistory();
1411 GURL url1("http://www.cnn.com");
1412 VisitInfo typed_visit(
1413 Time::Now() - base::TimeDelta::FromDays(6),
1414 ui::PAGE_TRANSITION_TYPED);
1415 VisitInfo reload_visit(
1416 Time::Now() - base::TimeDelta::FromDays(5),
1417 ui::PAGE_TRANSITION_RELOAD);
1418 VisitInfo link_visit(
1419 Time::Now() - base::TimeDelta::FromDays(4),
1420 ui::PAGE_TRANSITION_LINK);
1421 std::vector<VisitInfo> visits_to_add;
1422 visits_to_add.push_back(typed_visit);
1423 visits_to_add.push_back(reload_visit);
1424 visits_to_add.push_back(link_visit);
1427 backend_->AddVisits(url1, visits_to_add, history::SOURCE_SYNCED);
1429 // Verify that the various counts are what we expect.
1432 URLID id = backend_->db()->GetRowForURL(url1, &row);
1433 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1434 ASSERT_EQ(3U, visits.size());
1435 ASSERT_EQ(1, row.typed_count());
1436 ASSERT_EQ(2, row.visit_count());
1438 // Now, delete the typed visit and verify that typed_count is updated.
1439 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1440 id = backend_->db()->GetRowForURL(url1, &row);
1441 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1442 ASSERT_EQ(2U, visits.size());
1443 ASSERT_EQ(0, row.typed_count());
1444 ASSERT_EQ(1, row.visit_count());
1446 // Delete the reload visit now and verify that none of the counts have
1448 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1449 id = backend_->db()->GetRowForURL(url1, &row);
1450 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1451 ASSERT_EQ(1U, visits.size());
1452 ASSERT_EQ(0, row.typed_count());
1453 ASSERT_EQ(1, row.visit_count());
1455 // Delete the last visit and verify that we delete the URL.
1456 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1457 ASSERT_EQ(0, backend_->db()->GetRowForURL(url1, &row));
1460 TEST_F(HistoryBackendTest, RemoveVisitsSource) {
1461 ASSERT_TRUE(backend_.get());
1463 GURL url1("http://www.cnn.com");
1464 std::vector<VisitInfo> visits1, visits2;
1465 visits1.push_back(VisitInfo(
1466 Time::Now() - base::TimeDelta::FromDays(5),
1467 ui::PAGE_TRANSITION_LINK));
1468 visits1.push_back(VisitInfo(Time::Now(),
1469 ui::PAGE_TRANSITION_LINK));
1471 GURL url2("http://www.example.com");
1472 visits2.push_back(VisitInfo(
1473 Time::Now() - base::TimeDelta::FromDays(10),
1474 ui::PAGE_TRANSITION_LINK));
1475 visits2.push_back(VisitInfo(Time::Now(), ui::PAGE_TRANSITION_LINK));
1477 // Clear all history.
1478 backend_->DeleteAllHistory();
1481 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1482 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
1484 // Verify the visits of url1 were added.
1487 URLID id = backend_->db()->GetRowForURL(url1, &row);
1488 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1489 ASSERT_EQ(2U, visits.size());
1490 // Remove these visits.
1491 ASSERT_TRUE(backend_->RemoveVisits(visits));
1493 // Now check only url2's source in visit_source table.
1494 VisitSourceMap visit_sources;
1495 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1496 ASSERT_EQ(0U, visit_sources.size());
1497 id = backend_->db()->GetRowForURL(url2, &row);
1498 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1499 ASSERT_EQ(2U, visits.size());
1500 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1501 ASSERT_EQ(2U, visit_sources.size());
1502 for (int i = 0; i < 2; i++)
1503 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
1506 // Test for migration of adding visit_source table.
1507 TEST_F(HistoryBackendTest, MigrationVisitSource) {
1508 ASSERT_TRUE(backend_.get());
1509 backend_->Closing();
1512 base::FilePath old_history_path;
1513 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
1514 old_history_path = old_history_path.AppendASCII("History");
1515 old_history_path = old_history_path.AppendASCII("HistoryNoSource");
1517 // Copy history database file to current directory so that it will be deleted
1519 base::FilePath new_history_path(test_dir());
1520 base::DeleteFile(new_history_path, true);
1521 base::CreateDirectory(new_history_path);
1522 base::FilePath new_history_file =
1523 new_history_path.Append(chrome::kHistoryFilename);
1524 ASSERT_TRUE(base::CopyFile(old_history_path, new_history_file));
1526 backend_ = new HistoryBackend(
1527 new_history_path, new HistoryBackendTestDelegate(this), &history_client_);
1528 backend_->Init(std::string(), false);
1529 backend_->Closing();
1532 // Now the database should already be migrated.
1533 // Check version first.
1534 int cur_version = HistoryDatabase::GetCurrentVersion();
1536 ASSERT_TRUE(db.Open(new_history_file));
1537 sql::Statement s(db.GetUniqueStatement(
1538 "SELECT value FROM meta WHERE key = 'version'"));
1539 ASSERT_TRUE(s.Step());
1540 int file_version = s.ColumnInt(0);
1541 EXPECT_EQ(cur_version, file_version);
1543 // Check visit_source table is created and empty.
1544 s.Assign(db.GetUniqueStatement(
1545 "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
1546 ASSERT_TRUE(s.Step());
1547 s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
1548 EXPECT_FALSE(s.Step());
1551 // Test that SetFaviconMappingsForPageAndRedirects correctly updates icon
1552 // mappings based on redirects, icon URLs and icon types.
1553 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirects) {
1554 // Init recent_redirects_
1555 const GURL url1("http://www.google.com");
1556 const GURL url2("http://www.google.com/m");
1557 URLRow url_info1(url1);
1558 url_info1.set_visit_count(0);
1559 url_info1.set_typed_count(0);
1560 url_info1.set_last_visit(base::Time());
1561 url_info1.set_hidden(false);
1562 backend_->db_->AddURL(url_info1);
1564 URLRow url_info2(url2);
1565 url_info2.set_visit_count(0);
1566 url_info2.set_typed_count(0);
1567 url_info2.set_last_visit(base::Time());
1568 url_info2.set_hidden(false);
1569 backend_->db_->AddURL(url_info2);
1571 history::RedirectList redirects;
1572 redirects.push_back(url2);
1573 redirects.push_back(url1);
1574 backend_->recent_redirects_.Put(url1, redirects);
1576 const GURL icon_url1("http://www.google.com/icon");
1577 const GURL icon_url2("http://www.google.com/icon2");
1578 std::vector<SkBitmap> bitmaps;
1579 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
1580 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
1583 backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url1, bitmaps);
1584 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1585 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
1587 // Add one touch_icon
1588 backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps);
1589 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1590 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::TOUCH_ICON));
1591 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1593 // Add one TOUCH_PRECOMPOSED_ICON
1594 backend_->SetFavicons(
1595 url1, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_url1, bitmaps);
1596 // The touch_icon was replaced.
1597 EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1598 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1601 NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
1604 NumIconMappingsForPageURL(url2, favicon_base::TOUCH_PRECOMPOSED_ICON));
1606 // Add a touch_icon.
1607 backend_->SetFavicons(url1, favicon_base::TOUCH_ICON, icon_url1, bitmaps);
1608 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1609 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1610 // The TOUCH_PRECOMPOSED_ICON was replaced.
1613 NumIconMappingsForPageURL(url1, favicon_base::TOUCH_PRECOMPOSED_ICON));
1615 // Add a different favicon.
1616 backend_->SetFavicons(url1, favicon_base::FAVICON, icon_url2, bitmaps);
1617 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::TOUCH_ICON));
1618 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, favicon_base::FAVICON));
1619 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, favicon_base::FAVICON));
1622 // Test that there is no churn in icon mappings from calling
1623 // SetFavicons() twice with the same |bitmaps| parameter.
1624 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) {
1625 const GURL url("http://www.google.com/");
1626 const GURL icon_url("http://www.google.com/icon");
1628 std::vector<SkBitmap> bitmaps;
1629 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
1630 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
1632 backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps);
1634 std::vector<IconMapping> icon_mappings;
1635 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1636 url, favicon_base::FAVICON, &icon_mappings));
1637 EXPECT_EQ(1u, icon_mappings.size());
1638 IconMappingID mapping_id = icon_mappings[0].mapping_id;
1640 backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps);
1642 icon_mappings.clear();
1643 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1644 url, favicon_base::FAVICON, &icon_mappings));
1645 EXPECT_EQ(1u, icon_mappings.size());
1647 // The same row in the icon_mapping table should be used for the mapping as
1649 EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id);
1652 // Test that calling SetFavicons() with FaviconBitmapData of different pixel
1653 // sizes than the initially passed in FaviconBitmapData deletes the no longer
1654 // used favicon bitmaps.
1655 TEST_F(HistoryBackendTest, SetFaviconsDeleteBitmaps) {
1656 const GURL page_url("http://www.google.com/");
1657 const GURL icon_url("http://www.google.com/icon");
1659 std::vector<SkBitmap> bitmaps;
1660 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
1661 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
1662 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
1664 // Test initial state.
1665 std::vector<IconMapping> icon_mappings;
1666 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url, &icon_mappings));
1667 EXPECT_EQ(1u, icon_mappings.size());
1668 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1669 EXPECT_EQ(favicon_base::FAVICON, icon_mappings[0].icon_type);
1670 favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
1672 std::vector<FaviconBitmap> favicon_bitmaps;
1673 EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps));
1674 EXPECT_EQ(2u, favicon_bitmaps.size());
1675 FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id;
1676 EXPECT_NE(0, small_bitmap_id);
1677 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmaps[0].bitmap_data));
1678 EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size);
1679 FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id;
1680 EXPECT_NE(0, large_bitmap_id);
1681 EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, favicon_bitmaps[1].bitmap_data));
1682 EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size);
1684 // Call SetFavicons() with bitmap data for only the large bitmap. Check that
1685 // the small bitmap is in fact deleted.
1687 bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kLargeEdgeSize));
1688 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
1690 scoped_refptr<base::RefCountedMemory> bitmap_data_out;
1691 gfx::Size pixel_size_out;
1692 EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(small_bitmap_id,
1693 NULL, &bitmap_data_out, &pixel_size_out));
1694 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id,
1695 NULL, &bitmap_data_out, &pixel_size_out));
1696 EXPECT_TRUE(BitmapColorEqual(SK_ColorWHITE, bitmap_data_out));
1697 EXPECT_EQ(kLargeSize, pixel_size_out);
1699 icon_mappings.clear();
1700 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1702 EXPECT_EQ(1u, icon_mappings.size());
1703 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1705 // Notifications should have been broadcast for each call to SetFavicons().
1706 EXPECT_EQ(2, favicon_changed_notifications());
1709 // Test updating a single favicon bitmap's data via SetFavicons.
1710 TEST_F(HistoryBackendTest, SetFaviconsReplaceBitmapData) {
1711 const GURL page_url("http://www.google.com/");
1712 const GURL icon_url("http://www.google.com/icon");
1713 std::vector<SkBitmap> bitmaps;
1714 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
1716 // Add bitmap to the database.
1717 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
1719 favicon_base::FaviconID original_favicon_id =
1720 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1721 icon_url, favicon_base::FAVICON, NULL);
1722 EXPECT_NE(0, original_favicon_id);
1723 FaviconBitmap original_favicon_bitmap;
1725 GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap));
1727 BitmapColorEqual(SK_ColorBLUE, original_favicon_bitmap.bitmap_data));
1729 EXPECT_EQ(1, favicon_changed_notifications());
1731 // Call SetFavicons() with completely identical data.
1732 bitmaps[0] = CreateBitmap(SK_ColorBLUE, kSmallEdgeSize);
1733 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
1735 favicon_base::FaviconID updated_favicon_id =
1736 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1737 icon_url, favicon_base::FAVICON, NULL);
1738 EXPECT_NE(0, updated_favicon_id);
1739 FaviconBitmap updated_favicon_bitmap;
1741 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
1743 BitmapColorEqual(SK_ColorBLUE, updated_favicon_bitmap.bitmap_data));
1745 // Because the bitmap data is byte equivalent, no notifications should have
1746 // been broadcasted.
1747 EXPECT_EQ(1, favicon_changed_notifications());
1749 // Call SetFavicons() with a different bitmap of the same size.
1750 bitmaps[0] = CreateBitmap(SK_ColorWHITE, kSmallEdgeSize);
1751 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
1753 updated_favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1754 icon_url, favicon_base::FAVICON, NULL);
1755 EXPECT_NE(0, updated_favicon_id);
1757 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
1759 BitmapColorEqual(SK_ColorWHITE, updated_favicon_bitmap.bitmap_data));
1761 // There should be no churn in FaviconIDs or FaviconBitmapIds even though
1762 // the bitmap data changed.
1763 EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id);
1764 EXPECT_EQ(original_favicon_bitmap.bitmap_id,
1765 updated_favicon_bitmap.bitmap_id);
1767 // A notification should have been broadcasted as the favicon bitmap data has
1769 EXPECT_EQ(2, favicon_changed_notifications());
1772 // Test that if two pages share the same FaviconID, changing the favicon for
1773 // one page does not affect the other.
1774 TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
1775 GURL icon_url("http://www.google.com/favicon.ico");
1776 GURL icon_url_new("http://www.google.com/favicon2.ico");
1777 GURL page_url1("http://www.google.com");
1778 GURL page_url2("http://www.google.ca");
1779 std::vector<SkBitmap> bitmaps;
1780 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
1781 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
1783 backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps);
1785 std::vector<GURL> icon_urls;
1786 icon_urls.push_back(icon_url);
1788 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
1789 backend_->UpdateFaviconMappingsAndFetch(page_url2,
1791 favicon_base::FAVICON,
1792 GetEdgeSizesSmallAndLarge(),
1795 // Check that the same FaviconID is mapped to both page URLs.
1796 std::vector<IconMapping> icon_mappings;
1797 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1798 page_url1, &icon_mappings));
1799 EXPECT_EQ(1u, icon_mappings.size());
1800 favicon_base::FaviconID favicon_id = icon_mappings[0].icon_id;
1801 EXPECT_NE(0, favicon_id);
1803 icon_mappings.clear();
1804 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1805 page_url2, &icon_mappings));
1806 EXPECT_EQ(1u, icon_mappings.size());
1807 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1809 // Change the icon URL that |page_url1| is mapped to.
1811 bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kSmallEdgeSize));
1812 backend_->SetFavicons(
1813 page_url1, favicon_base::FAVICON, icon_url_new, bitmaps);
1815 // |page_url1| should map to a new FaviconID and have valid bitmap data.
1816 icon_mappings.clear();
1817 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1818 page_url1, &icon_mappings));
1819 EXPECT_EQ(1u, icon_mappings.size());
1820 EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
1821 EXPECT_NE(favicon_id, icon_mappings[0].icon_id);
1823 std::vector<FaviconBitmap> favicon_bitmaps;
1824 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
1825 icon_mappings[0].icon_id, &favicon_bitmaps));
1826 EXPECT_EQ(1u, favicon_bitmaps.size());
1828 // |page_url2| should still map to the same FaviconID and have valid bitmap
1830 icon_mappings.clear();
1831 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1832 page_url2, &icon_mappings));
1833 EXPECT_EQ(1u, icon_mappings.size());
1834 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1836 favicon_bitmaps.clear();
1837 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id,
1839 EXPECT_EQ(2u, favicon_bitmaps.size());
1841 // A notification should have been broadcast for each call to SetFavicons()
1842 // and each call to UpdateFaviconMappingsAndFetch().
1843 EXPECT_EQ(3, favicon_changed_notifications());
1846 // Test that no notifications are broadcast as a result of calling
1847 // UpdateFaviconMappingsAndFetch() for an icon URL which is already
1848 // mapped to the passed in page URL.
1849 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoChange) {
1850 GURL page_url("http://www.google.com");
1851 GURL icon_url("http://www.google.com/favicon.ico");
1852 std::vector<SkBitmap> bitmaps;
1853 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
1855 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
1857 favicon_base::FaviconID icon_id =
1858 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1859 icon_url, favicon_base::FAVICON, NULL);
1860 EXPECT_NE(0, icon_id);
1861 EXPECT_EQ(1, favicon_changed_notifications());
1863 std::vector<GURL> icon_urls;
1864 icon_urls.push_back(icon_url);
1866 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
1867 backend_->UpdateFaviconMappingsAndFetch(page_url,
1869 favicon_base::FAVICON,
1870 GetEdgeSizesSmallAndLarge(),
1874 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1875 icon_url, favicon_base::FAVICON, NULL));
1877 // No notification should have been broadcast as no icon mapping, favicon,
1878 // or favicon bitmap was updated, added or removed.
1879 EXPECT_EQ(1, favicon_changed_notifications());
1882 // Test repeatedly calling MergeFavicon(). |page_url| is initially not known
1884 TEST_F(HistoryBackendTest, MergeFaviconPageURLNotInDB) {
1885 GURL page_url("http://www.google.com");
1886 GURL icon_url("http:/www.google.com/favicon.ico");
1888 std::vector<unsigned char> data;
1889 data.push_back('a');
1890 scoped_refptr<base::RefCountedBytes> bitmap_data(
1891 new base::RefCountedBytes(data));
1893 backend_->MergeFavicon(
1894 page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
1896 // |page_url| should now be mapped to |icon_url| and the favicon bitmap should
1898 std::vector<IconMapping> icon_mappings;
1899 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1901 EXPECT_EQ(1u, icon_mappings.size());
1902 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1904 FaviconBitmap favicon_bitmap;
1905 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1906 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1907 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1908 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1911 bitmap_data = new base::RefCountedBytes(data);
1912 backend_->MergeFavicon(
1913 page_url, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
1915 // |page_url| should still have a single favicon bitmap. The bitmap data
1916 // should be updated.
1917 icon_mappings.clear();
1918 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1920 EXPECT_EQ(1u, icon_mappings.size());
1921 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1923 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1924 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1925 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
1926 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1929 // Test calling MergeFavicon() when |page_url| is known to the database.
1930 TEST_F(HistoryBackendTest, MergeFaviconPageURLInDB) {
1931 GURL page_url("http://www.google.com");
1932 GURL icon_url1("http:/www.google.com/favicon.ico");
1933 GURL icon_url2("http://www.google.com/favicon2.ico");
1934 std::vector<SkBitmap> bitmaps;
1935 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
1937 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps);
1939 // Test initial state.
1940 std::vector<IconMapping> icon_mappings;
1941 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1943 EXPECT_EQ(1u, icon_mappings.size());
1944 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1946 FaviconBitmap favicon_bitmap;
1947 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1948 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1949 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
1950 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1952 EXPECT_EQ(1, favicon_changed_notifications());
1954 // 1) Merge identical favicon bitmap.
1955 std::vector<unsigned char> data;
1956 gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
1957 scoped_refptr<base::RefCountedBytes> bitmap_data(
1958 new base::RefCountedBytes(data));
1959 backend_->MergeFavicon(
1960 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
1962 // All the data should stay the same and no notifications should have been
1964 icon_mappings.clear();
1965 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1967 EXPECT_EQ(1u, icon_mappings.size());
1968 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1970 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1971 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1972 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
1973 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1975 EXPECT_EQ(1, favicon_changed_notifications());
1977 // 2) Merge favicon bitmap of the same size.
1979 data.push_back('b');
1980 bitmap_data = new base::RefCountedBytes(data);
1981 backend_->MergeFavicon(
1982 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kSmallSize);
1984 // The small favicon bitmap at |icon_url1| should be overwritten.
1985 icon_mappings.clear();
1986 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1988 EXPECT_EQ(1u, icon_mappings.size());
1989 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1991 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1992 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1993 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
1994 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1996 // 3) Merge favicon for the same icon URL, but a pixel size for which there is
1997 // no favicon bitmap.
1999 bitmap_data = new base::RefCountedBytes(data);
2000 backend_->MergeFavicon(
2001 page_url, icon_url1, favicon_base::FAVICON, bitmap_data, kTinySize);
2003 // A new favicon bitmap should be created and the preexisting favicon bitmap
2004 // ('b') should be expired.
2005 icon_mappings.clear();
2006 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
2008 EXPECT_EQ(1u, icon_mappings.size());
2009 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
2011 std::vector<FaviconBitmap> favicon_bitmaps;
2012 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
2014 EXPECT_NE(base::Time(), favicon_bitmaps[0].last_updated);
2015 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
2016 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
2017 EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
2018 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
2019 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
2021 // 4) Merge favicon for an icon URL different from the icon URLs already
2022 // mapped to page URL.
2024 bitmap_data = new base::RefCountedBytes(data);
2025 backend_->MergeFavicon(
2026 page_url, icon_url2, favicon_base::FAVICON, bitmap_data, kSmallSize);
2028 // The existing favicon bitmaps should be copied over to the newly created
2029 // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
2030 icon_mappings.clear();
2031 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
2033 EXPECT_EQ(1u, icon_mappings.size());
2034 EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);
2036 favicon_bitmaps.clear();
2037 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
2039 EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
2040 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
2041 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
2042 // The favicon being merged should take precedence over the preexisting
2044 EXPECT_NE(base::Time(), favicon_bitmaps[1].last_updated);
2045 EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data));
2046 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
2048 // A notification should have been broadcast for each call to SetFavicons()
2049 // and MergeFavicon().
2050 EXPECT_EQ(4, favicon_changed_notifications());
2053 // Test calling MergeFavicon() when |icon_url| is known to the database but not
2054 // mapped to |page_url|.
2055 TEST_F(HistoryBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) {
2056 GURL page_url1("http://www.google.com");
2057 GURL page_url2("http://news.google.com");
2058 GURL page_url3("http://maps.google.com");
2059 GURL icon_url("http:/www.google.com/favicon.ico");
2060 std::vector<SkBitmap> bitmaps;
2061 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
2063 backend_->SetFavicons(page_url1, favicon_base::FAVICON, icon_url, bitmaps);
2065 // Test initial state.
2066 std::vector<IconMapping> icon_mappings;
2067 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
2069 EXPECT_EQ(1u, icon_mappings.size());
2070 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
2072 FaviconBitmap favicon_bitmap;
2073 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
2074 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
2075 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
2076 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
2078 // 1) Merge in an identical favicon bitmap data but for a different page URL.
2079 std::vector<unsigned char> data;
2080 gfx::PNGCodec::EncodeBGRASkBitmap(bitmaps[0], false, &data);
2081 scoped_refptr<base::RefCountedBytes> bitmap_data(
2082 new base::RefCountedBytes(data));
2084 backend_->MergeFavicon(
2085 page_url2, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
2087 favicon_base::FaviconID favicon_id =
2088 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
2089 icon_url, favicon_base::FAVICON, NULL);
2090 EXPECT_NE(0, favicon_id);
2092 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
2093 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
2094 EXPECT_TRUE(BitmapColorEqual(SK_ColorBLUE, favicon_bitmap.bitmap_data));
2095 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
2097 // 2) Merging a favicon bitmap with different bitmap data for the same icon
2098 // URL should overwrite the small favicon bitmap at |icon_url|.
2100 data.push_back('b');
2101 bitmap_data = new base::RefCountedBytes(data);
2102 backend_->MergeFavicon(
2103 page_url3, icon_url, favicon_base::FAVICON, bitmap_data, kSmallSize);
2105 favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
2106 icon_url, favicon_base::FAVICON, NULL);
2107 EXPECT_NE(0, favicon_id);
2109 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
2110 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
2111 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
2112 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
2114 // |icon_url| should be mapped to all three page URLs.
2115 icon_mappings.clear();
2116 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
2118 EXPECT_EQ(1u, icon_mappings.size());
2119 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
2121 icon_mappings.clear();
2122 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url2,
2124 EXPECT_EQ(1u, icon_mappings.size());
2125 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
2127 icon_mappings.clear();
2128 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url3,
2130 EXPECT_EQ(1u, icon_mappings.size());
2131 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
2133 // A notification should have been broadcast for each call to SetFavicons()
2134 // and MergeFavicon().
2135 EXPECT_EQ(3, favicon_changed_notifications());
2138 // Test that MergeFavicon() does not add more than
2139 // |kMaxFaviconBitmapsPerIconURL| to a favicon.
2140 TEST_F(HistoryBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) {
2141 GURL page_url("http://www.google.com");
2142 std::string icon_url_string("http://www.google.com/favicon.ico");
2143 size_t replace_index = icon_url_string.size() - 1;
2145 std::vector<unsigned char> data;
2146 data.push_back('a');
2147 scoped_refptr<base::RefCountedMemory> bitmap_data =
2148 base::RefCountedBytes::TakeVector(&data);
2151 for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) {
2152 icon_url_string[replace_index] = '0' + i;
2153 GURL icon_url(icon_url_string);
2155 backend_->MergeFavicon(page_url,
2157 favicon_base::FAVICON,
2159 gfx::Size(pixel_size, pixel_size));
2163 // There should be a single favicon mapped to |page_url| with exactly
2164 // kMaxFaviconBitmapsPerIconURL favicon bitmaps.
2165 std::vector<IconMapping> icon_mappings;
2166 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
2168 EXPECT_EQ(1u, icon_mappings.size());
2169 std::vector<FaviconBitmap> favicon_bitmaps;
2170 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
2171 icon_mappings[0].icon_id, &favicon_bitmaps));
2172 EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
2175 // Tests that the favicon set by MergeFavicon() shows up in the result of
2176 // GetFaviconsForURL().
2177 TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) {
2178 GURL page_url("http://www.google.com");
2179 GURL icon_url("http://www.google.com/favicon.ico");
2180 GURL merged_icon_url("http://wwww.google.com/favicon2.ico");
2181 std::vector<SkBitmap> bitmaps;
2182 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
2183 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
2185 // Set some preexisting favicons for |page_url|.
2186 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
2188 // Merge small favicon.
2189 std::vector<unsigned char> data;
2190 data.push_back('c');
2191 scoped_refptr<base::RefCountedBytes> bitmap_data(
2192 new base::RefCountedBytes(data));
2193 backend_->MergeFavicon(page_url,
2195 favicon_base::FAVICON,
2199 // Request favicon bitmaps for both 1x and 2x to simulate request done by
2200 // BookmarkModel::GetFavicon().
2201 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2202 backend_->GetFaviconsForURL(page_url,
2203 favicon_base::FAVICON,
2204 GetEdgeSizesSmallAndLarge(),
2207 EXPECT_EQ(2u, bitmap_results.size());
2208 const favicon_base::FaviconRawBitmapResult& first_result = bitmap_results[0];
2209 const favicon_base::FaviconRawBitmapResult& result =
2210 (first_result.pixel_size == kSmallSize) ? first_result
2211 : bitmap_results[1];
2212 EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data));
2215 // Tests GetFaviconsForURL with icon_types priority,
2216 TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) {
2217 GURL page_url("http://www.google.com");
2218 GURL icon_url("http://www.google.com/favicon.ico");
2219 GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
2221 std::vector<SkBitmap> favicon_bitmaps;
2222 favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
2223 favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));
2225 std::vector<SkBitmap> touch_bitmaps;
2226 touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 64));
2228 // Set some preexisting favicons for |page_url|.
2229 backend_->SetFavicons(
2230 page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps);
2231 backend_->SetFavicons(
2232 page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps);
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);
2239 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
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);
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);
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");
2258 std::vector<SkBitmap> favicon_bitmaps;
2259 favicon_bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
2260 favicon_bitmaps.push_back(CreateBitmap(SK_ColorRED, 32));
2262 std::vector<SkBitmap> touch_bitmaps;
2263 touch_bitmaps.push_back(CreateBitmap(SK_ColorWHITE, 32));
2265 // Set some preexisting favicons for |page_url|.
2266 backend_->SetFavicons(
2267 page_url, favicon_base::FAVICON, icon_url, favicon_bitmaps);
2268 backend_->SetFavicons(
2269 page_url, favicon_base::TOUCH_ICON, touch_icon_url, touch_bitmaps);
2271 favicon_base::FaviconRawBitmapResult result;
2272 std::vector<int> icon_types;
2273 icon_types.push_back(favicon_base::FAVICON);
2274 icon_types.push_back(favicon_base::TOUCH_ICON);
2276 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
2278 // Verify the result icon is 32x32 favicon.
2279 EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
2280 EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
2282 // Change minimal size to 32x32 and verify the 32x32 favicon returned.
2283 favicon_base::FaviconRawBitmapResult result1;
2284 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1);
2285 EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size);
2286 EXPECT_EQ(favicon_base::FAVICON, result1.icon_type);
2289 // Test the favicon is returned if its size is smaller than minimal size,
2290 // because it is only one available.
2291 TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) {
2292 GURL page_url("http://www.google.com");
2293 GURL icon_url("http://www.google.com/favicon.ico");
2295 std::vector<SkBitmap> bitmaps;
2296 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, 16));
2298 // Set preexisting favicons for |page_url|.
2299 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
2301 favicon_base::FaviconRawBitmapResult result;
2302 std::vector<int> icon_types;
2303 icon_types.push_back(favicon_base::FAVICON);
2304 icon_types.push_back(favicon_base::TOUCH_ICON);
2306 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
2308 // Verify 16x16 icon is returned, even it small than minimal_size.
2309 EXPECT_EQ(gfx::Size(16, 16), result.pixel_size);
2310 EXPECT_EQ(favicon_base::FAVICON, result.icon_type);
2313 // Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in.
2314 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) {
2315 GURL page_url1("http://www.google.com");
2316 GURL page_url2("http://news.google.com");
2317 GURL page_url3("http://mail.google.com");
2318 GURL icon_urla("http://www.google.com/favicon1.ico");
2319 GURL icon_urlb("http://www.google.com/favicon2.ico");
2320 std::vector<SkBitmap> bitmaps;
2321 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
2323 // |page_url1| is mapped to |icon_urla| which if of type TOUCH_ICON.
2324 backend_->SetFavicons(
2325 page_url1, favicon_base::TOUCH_ICON, icon_urla, bitmaps);
2327 // |page_url2| is mapped to |icon_urlb| which is of type
2328 // TOUCH_PRECOMPOSED_ICON.
2329 backend_->SetFavicons(
2330 page_url2, favicon_base::TOUCH_PRECOMPOSED_ICON, icon_urlb, bitmaps);
2332 std::vector<GURL> icon_urls;
2333 icon_urls.push_back(icon_urla);
2334 icon_urls.push_back(icon_urlb);
2336 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2337 backend_->UpdateFaviconMappingsAndFetch(
2340 (favicon_base::TOUCH_ICON | favicon_base::TOUCH_PRECOMPOSED_ICON),
2341 GetEdgeSizesSmallAndLarge(),
2344 // |page_url1| and |page_url2| should still be mapped to the same icon URLs.
2345 std::vector<IconMapping> icon_mappings;
2346 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
2348 EXPECT_EQ(1u, icon_mappings.size());
2349 EXPECT_EQ(icon_urla, icon_mappings[0].icon_url);
2350 EXPECT_EQ(favicon_base::TOUCH_ICON, icon_mappings[0].icon_type);
2352 icon_mappings.clear();
2353 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url2, &icon_mappings));
2354 EXPECT_EQ(1u, icon_mappings.size());
2355 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
2356 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
2358 // |page_url3| should be mapped only to |icon_urlb| as TOUCH_PRECOMPOSED_ICON
2359 // is the largest IconType.
2360 icon_mappings.clear();
2361 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url3, &icon_mappings));
2362 EXPECT_EQ(1u, icon_mappings.size());
2363 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
2364 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
2367 // Test the results of GetFaviconsFromDB() when there are no found favicons.
2368 TEST_F(HistoryBackendTest, GetFaviconsFromDBEmpty) {
2369 const GURL page_url("http://www.google.com/");
2371 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2372 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
2373 favicon_base::FAVICON,
2374 GetEdgeSizesSmallAndLarge(),
2376 EXPECT_TRUE(bitmap_results.empty());
2379 // Test the results of GetFaviconsFromDB() when there are matching favicons
2380 // but there are no associated favicon bitmaps.
2381 TEST_F(HistoryBackendTest, GetFaviconsFromDBNoFaviconBitmaps) {
2382 const GURL page_url("http://www.google.com/");
2383 const GURL icon_url("http://www.google.com/icon1");
2385 favicon_base::FaviconID icon_id =
2386 backend_->thumbnail_db_->AddFavicon(icon_url, favicon_base::FAVICON);
2387 EXPECT_NE(0, icon_id);
2388 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
2390 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2391 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url,
2392 favicon_base::FAVICON,
2393 GetEdgeSizesSmallAndLarge(),
2394 &bitmap_results_out));
2395 EXPECT_TRUE(bitmap_results_out.empty());
2398 // Test that GetFaviconsFromDB() returns results for the bitmaps which most
2399 // closely match the passed in the desired pixel sizes.
2400 TEST_F(HistoryBackendTest, GetFaviconsFromDBSelectClosestMatch) {
2401 const GURL page_url("http://www.google.com/");
2402 const GURL icon_url("http://www.google.com/icon1");
2403 std::vector<SkBitmap> bitmaps;
2404 bitmaps.push_back(CreateBitmap(SK_ColorWHITE, kTinyEdgeSize));
2405 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
2406 bitmaps.push_back(CreateBitmap(SK_ColorRED, kLargeEdgeSize));
2408 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url, bitmaps);
2410 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2411 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2412 favicon_base::FAVICON,
2413 GetEdgeSizesSmallAndLarge(),
2414 &bitmap_results_out));
2416 // The bitmap data for the small and large bitmaps should be returned as their
2417 // sizes match exactly.
2418 EXPECT_EQ(2u, bitmap_results_out.size());
2419 // No required order for results.
2420 if (bitmap_results_out[0].pixel_size == kLargeSize) {
2421 favicon_base::FaviconRawBitmapResult tmp_result = bitmap_results_out[0];
2422 bitmap_results_out[0] = bitmap_results_out[1];
2423 bitmap_results_out[1] = tmp_result;
2426 EXPECT_FALSE(bitmap_results_out[0].expired);
2428 BitmapColorEqual(SK_ColorBLUE, bitmap_results_out[0].bitmap_data));
2429 EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size);
2430 EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url);
2431 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
2433 EXPECT_FALSE(bitmap_results_out[1].expired);
2434 EXPECT_TRUE(BitmapColorEqual(SK_ColorRED, bitmap_results_out[1].bitmap_data));
2435 EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size);
2436 EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url);
2437 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[1].icon_type);
2440 // Test the results of GetFaviconsFromDB() when called with different
2442 TEST_F(HistoryBackendTest, GetFaviconsFromDBIconType) {
2443 const GURL page_url("http://www.google.com/");
2444 const GURL icon_url1("http://www.google.com/icon1.png");
2445 const GURL icon_url2("http://www.google.com/icon2.png");
2446 std::vector<SkBitmap> bitmaps;
2447 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
2449 std::vector<favicon_base::FaviconRawBitmapData> favicon_bitmap_data;
2450 backend_->SetFavicons(page_url, favicon_base::FAVICON, icon_url1, bitmaps);
2451 backend_->SetFavicons(page_url, favicon_base::TOUCH_ICON, icon_url2, bitmaps);
2453 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2454 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2455 favicon_base::FAVICON,
2456 GetEdgeSizesSmallAndLarge(),
2457 &bitmap_results_out));
2459 EXPECT_EQ(1u, bitmap_results_out.size());
2460 EXPECT_EQ(favicon_base::FAVICON, bitmap_results_out[0].icon_type);
2461 EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url);
2463 bitmap_results_out.clear();
2464 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2465 favicon_base::TOUCH_ICON,
2466 GetEdgeSizesSmallAndLarge(),
2467 &bitmap_results_out));
2469 EXPECT_EQ(1u, bitmap_results_out.size());
2470 EXPECT_EQ(favicon_base::TOUCH_ICON, bitmap_results_out[0].icon_type);
2471 EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
2474 // Test that GetFaviconsFromDB() correctly sets the expired flag for bitmap
2476 TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) {
2477 const GURL page_url("http://www.google.com/");
2478 const GURL icon_url("http://www.google.com/icon.png");
2480 std::vector<unsigned char> data;
2481 data.push_back('a');
2482 scoped_refptr<base::RefCountedBytes> bitmap_data(
2483 base::RefCountedBytes::TakeVector(&data));
2484 base::Time last_updated = base::Time::FromTimeT(0);
2485 favicon_base::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
2486 icon_url, favicon_base::FAVICON, bitmap_data, last_updated, kSmallSize);
2487 EXPECT_NE(0, icon_id);
2488 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
2490 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2491 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2492 favicon_base::FAVICON,
2493 GetEdgeSizesSmallAndLarge(),
2494 &bitmap_results_out));
2496 EXPECT_EQ(1u, bitmap_results_out.size());
2497 EXPECT_TRUE(bitmap_results_out[0].expired);
2500 // Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
2501 // no valid thumbnail database.
2502 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {
2503 // Make the thumbnail database invalid.
2504 backend_->thumbnail_db_.reset();
2506 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results;
2508 backend_->UpdateFaviconMappingsAndFetch(GURL(),
2509 std::vector<GURL>(),
2510 favicon_base::FAVICON,
2511 GetEdgeSizesSmallAndLarge(),
2514 EXPECT_TRUE(bitmap_results.empty());
2517 TEST_F(HistoryBackendTest, CloneFaviconIsRestrictedToSameDomain) {
2518 const GURL url("http://www.google.com/");
2519 const GURL same_domain_url("http://www.google.com/subdir/index.html");
2520 const GURL foreign_domain_url("http://www.not-google.com/");
2521 const GURL icon_url("http://www.google.com/icon.png");
2522 std::vector<SkBitmap> bitmaps;
2523 bitmaps.push_back(CreateBitmap(SK_ColorBLUE, kSmallEdgeSize));
2526 backend_->SetFavicons(url, favicon_base::FAVICON, icon_url, bitmaps);
2527 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
2528 url, favicon_base::FAVICON, NULL));
2530 // Validate starting state.
2531 std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results_out;
2532 EXPECT_TRUE(backend_->GetFaviconsFromDB(url,
2533 favicon_base::FAVICON,
2534 GetEdgeSizesSmallAndLarge(),
2535 &bitmap_results_out));
2536 EXPECT_FALSE(backend_->GetFaviconsFromDB(same_domain_url,
2537 favicon_base::FAVICON,
2538 GetEdgeSizesSmallAndLarge(),
2539 &bitmap_results_out));
2540 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url,
2541 favicon_base::FAVICON,
2542 GetEdgeSizesSmallAndLarge(),
2543 &bitmap_results_out));
2545 // Same-domain cloning should work.
2546 backend_->CloneFavicons(url, same_domain_url);
2547 EXPECT_TRUE(backend_->GetFaviconsFromDB(same_domain_url,
2548 favicon_base::FAVICON,
2549 GetEdgeSizesSmallAndLarge(),
2550 &bitmap_results_out));
2552 // Foreign-domain cloning is forbidden.
2553 backend_->CloneFavicons(url, foreign_domain_url);
2554 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url,
2555 favicon_base::FAVICON,
2556 GetEdgeSizesSmallAndLarge(),
2557 &bitmap_results_out));
2560 TEST_F(HistoryBackendTest, QueryFilteredURLs) {
2561 const char* google = "http://www.google.com/";
2562 const char* yahoo = "http://www.yahoo.com/";
2563 const char* yahoo_sports = "http://sports.yahoo.com/";
2564 const char* yahoo_sports_with_article1 =
2565 "http://sports.yahoo.com/article1.htm";
2566 const char* yahoo_sports_with_article2 =
2567 "http://sports.yahoo.com/article2.htm";
2568 const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer";
2569 const char* apple = "http://www.apple.com/";
2571 // Clear all history.
2572 backend_->DeleteAllHistory();
2574 Time tested_time = Time::Now().LocalMidnight() +
2575 base::TimeDelta::FromHours(4);
2576 base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30);
2577 base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
2578 base::TimeDelta one_day = base::TimeDelta::FromDays(1);
2580 const ui::PageTransition kTypedTransition =
2581 ui::PAGE_TRANSITION_TYPED;
2582 const ui::PageTransition kKeywordGeneratedTransition =
2583 ui::PAGE_TRANSITION_KEYWORD_GENERATED;
2585 const char* redirect_sequence[2];
2586 redirect_sequence[1] = NULL;
2588 redirect_sequence[0] = google;
2589 AddRedirectChainWithTransitionAndTime(
2590 redirect_sequence, 0, kTypedTransition,
2591 tested_time - one_day - half_an_hour * 2);
2592 AddRedirectChainWithTransitionAndTime(
2593 redirect_sequence, 0,
2594 kTypedTransition, tested_time - one_day);
2595 AddRedirectChainWithTransitionAndTime(
2596 redirect_sequence, 0,
2597 kTypedTransition, tested_time - half_an_hour / 2);
2598 AddRedirectChainWithTransitionAndTime(
2599 redirect_sequence, 0,
2600 kTypedTransition, tested_time);
2602 // Add a visit with a transition that will make sure that no segment gets
2603 // created for this page (so the subsequent entries will have different URLIDs
2605 redirect_sequence[0] = apple;
2606 AddRedirectChainWithTransitionAndTime(
2607 redirect_sequence, 0, kKeywordGeneratedTransition,
2608 tested_time - one_day + one_hour * 6);
2610 redirect_sequence[0] = yahoo;
2611 AddRedirectChainWithTransitionAndTime(
2612 redirect_sequence, 0, kTypedTransition,
2613 tested_time - one_day + half_an_hour);
2614 AddRedirectChainWithTransitionAndTime(
2615 redirect_sequence, 0, kTypedTransition,
2616 tested_time - one_day + half_an_hour * 2);
2618 redirect_sequence[0] = yahoo_sports;
2619 AddRedirectChainWithTransitionAndTime(
2620 redirect_sequence, 0, kTypedTransition,
2621 tested_time - one_day - half_an_hour * 2);
2622 AddRedirectChainWithTransitionAndTime(
2623 redirect_sequence, 0, kTypedTransition,
2624 tested_time - one_day);
2625 int transition1, transition2;
2626 AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false,
2627 tested_time - one_day + half_an_hour,
2628 &transition1, &transition2);
2629 AddClientRedirect(GURL(yahoo_sports_with_article1),
2630 GURL(yahoo_sports_with_article2),
2632 tested_time - one_day + half_an_hour * 2,
2633 &transition1, &transition2);
2635 redirect_sequence[0] = yahoo_sports_soccer;
2636 AddRedirectChainWithTransitionAndTime(redirect_sequence, 0,
2638 tested_time - half_an_hour);
2642 FilteredURLList filtered_list;
2643 // Time limit is |tested_time| +/- 45 min.
2644 base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45);
2645 filter.SetFilterTime(tested_time);
2646 filter.SetFilterWidth(three_quarters_of_an_hour);
2647 backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
2649 ASSERT_EQ(4U, filtered_list.size());
2650 EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
2651 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
2652 EXPECT_EQ(std::string(yahoo), filtered_list[2].url.spec());
2653 EXPECT_EQ(std::string(yahoo_sports), filtered_list[3].url.spec());
2655 // Time limit is between |tested_time| and |tested_time| + 2 hours.
2656 filter.SetFilterTime(tested_time + one_hour);
2657 filter.SetFilterWidth(one_hour);
2658 backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
2660 ASSERT_EQ(3U, filtered_list.size());
2661 EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
2662 EXPECT_EQ(std::string(yahoo), filtered_list[1].url.spec());
2663 EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec());
2665 // Time limit is between |tested_time| - 2 hours and |tested_time|.
2666 filter.SetFilterTime(tested_time - one_hour);
2667 filter.SetFilterWidth(one_hour);
2668 backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
2670 ASSERT_EQ(3U, filtered_list.size());
2671 EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
2672 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
2673 EXPECT_EQ(std::string(yahoo_sports), filtered_list[2].url.spec());
2675 filter.ClearFilters();
2676 base::Time::Exploded exploded_time;
2677 tested_time.LocalExplode(&exploded_time);
2680 filter.SetFilterTime(tested_time);
2681 filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week));
2682 backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
2684 ASSERT_EQ(2U, filtered_list.size());
2685 EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
2686 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
2688 // Today + time limit - only yahoo_sports_soccer should fit.
2689 filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40));
2690 filter.SetFilterWidth(base::TimeDelta::FromMinutes(20));
2691 backend_->QueryFilteredURLs(100, filter, false, &filtered_list);
2693 ASSERT_EQ(1U, filtered_list.size());
2694 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[0].url.spec());
2696 // Make sure we get debug data if we request it.
2697 filter.SetFilterTime(tested_time);
2698 filter.SetFilterWidth(one_hour * 2);
2699 backend_->QueryFilteredURLs(100, filter, true, &filtered_list);
2701 // If the SegmentID is used by QueryFilteredURLs when generating the debug
2702 // data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer|
2703 // entry will be zero instead of 1.
2704 ASSERT_GE(filtered_list.size(), 2U);
2705 EXPECT_EQ(std::string(google), filtered_list[0].url.spec());
2706 EXPECT_EQ(std::string(yahoo_sports_soccer), filtered_list[1].url.spec());
2707 EXPECT_EQ(4U, filtered_list[0].extended_info.total_visits);
2708 EXPECT_EQ(1U, filtered_list[1].extended_info.total_visits);
2711 TEST_F(HistoryBackendTest, UpdateVisitDuration) {
2712 // This unit test will test adding and deleting visit details information.
2713 ASSERT_TRUE(backend_.get());
2715 GURL url1("http://www.cnn.com");
2716 std::vector<VisitInfo> visit_info1, visit_info2;
2717 Time start_ts = Time::Now() - base::TimeDelta::FromDays(5);
2718 Time end_ts = start_ts + base::TimeDelta::FromDays(2);
2719 visit_info1.push_back(VisitInfo(start_ts, ui::PAGE_TRANSITION_LINK));
2721 GURL url2("http://www.example.com");
2722 visit_info2.push_back(VisitInfo(Time::Now() - base::TimeDelta::FromDays(10),
2723 ui::PAGE_TRANSITION_LINK));
2725 // Clear all history.
2726 backend_->DeleteAllHistory();
2729 backend_->AddVisits(url1, visit_info1, history::SOURCE_BROWSED);
2730 backend_->AddVisits(url2, visit_info2, history::SOURCE_BROWSED);
2732 // Verify the entries for both visits were added in visit_details.
2733 VisitVector visits1, visits2;
2735 URLID url_id1 = backend_->db()->GetRowForURL(url1, &row);
2736 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
2737 ASSERT_EQ(1U, visits1.size());
2738 EXPECT_EQ(0, visits1[0].visit_duration.ToInternalValue());
2740 URLID url_id2 = backend_->db()->GetRowForURL(url2, &row);
2741 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id2, &visits2));
2742 ASSERT_EQ(1U, visits2.size());
2743 EXPECT_EQ(0, visits2[0].visit_duration.ToInternalValue());
2745 // Update the visit to cnn.com.
2746 backend_->UpdateVisitDuration(visits1[0].visit_id, end_ts);
2748 // Check the duration for visiting cnn.com was correctly updated.
2749 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
2750 ASSERT_EQ(1U, visits1.size());
2751 base::TimeDelta expected_duration = end_ts - start_ts;
2752 EXPECT_EQ(expected_duration.ToInternalValue(),
2753 visits1[0].visit_duration.ToInternalValue());
2755 // Remove the visit to cnn.com.
2756 ASSERT_TRUE(backend_->RemoveVisits(visits1));
2759 // Test for migration of adding visit_duration column.
2760 TEST_F(HistoryBackendTest, MigrationVisitDuration) {
2761 ASSERT_TRUE(backend_.get());
2762 backend_->Closing();
2765 base::FilePath old_history_path, old_history;
2766 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
2767 old_history_path = old_history_path.AppendASCII("History");
2768 old_history = old_history_path.AppendASCII("HistoryNoDuration");
2770 // Copy history database file to current directory so that it will be deleted
2772 base::FilePath new_history_path(test_dir());
2773 base::DeleteFile(new_history_path, true);
2774 base::CreateDirectory(new_history_path);
2775 base::FilePath new_history_file =
2776 new_history_path.Append(chrome::kHistoryFilename);
2777 ASSERT_TRUE(base::CopyFile(old_history, new_history_file));
2779 backend_ = new HistoryBackend(
2780 new_history_path, new HistoryBackendTestDelegate(this), &history_client_);
2781 backend_->Init(std::string(), false);
2782 backend_->Closing();
2785 // Now the history database should already be migrated.
2787 // Check version in history database first.
2788 int cur_version = HistoryDatabase::GetCurrentVersion();
2790 ASSERT_TRUE(db.Open(new_history_file));
2791 sql::Statement s(db.GetUniqueStatement(
2792 "SELECT value FROM meta WHERE key = 'version'"));
2793 ASSERT_TRUE(s.Step());
2794 int file_version = s.ColumnInt(0);
2795 EXPECT_EQ(cur_version, file_version);
2797 // Check visit_duration column in visits table is created and set to 0.
2798 s.Assign(db.GetUniqueStatement(
2799 "SELECT visit_duration FROM visits LIMIT 1"));
2800 ASSERT_TRUE(s.Step());
2801 EXPECT_EQ(0, s.ColumnInt(0));
2804 TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) {
2805 ASSERT_TRUE(backend_.get());
2807 GURL url("http://www.google.com");
2808 base::string16 title(base::UTF8ToUTF16("Bookmark title"));
2809 backend_->AddPageNoVisitForBookmark(url, title);
2812 backend_->GetURL(url, &row);
2813 EXPECT_EQ(url, row.url());
2814 EXPECT_EQ(title, row.title());
2815 EXPECT_EQ(0, row.visit_count());
2817 backend_->DeleteURL(url);
2818 backend_->AddPageNoVisitForBookmark(url, base::string16());
2819 backend_->GetURL(url, &row);
2820 EXPECT_EQ(url, row.url());
2821 EXPECT_EQ(base::UTF8ToUTF16(url.spec()), row.title());
2822 EXPECT_EQ(0, row.visit_count());
2825 TEST_F(HistoryBackendTest, ExpireHistoryForTimes) {
2826 ASSERT_TRUE(backend_.get());
2828 HistoryAddPageArgs args[10];
2829 for (size_t i = 0; i < arraysize(args); ++i) {
2830 args[i].url = GURL("http://example" +
2831 std::string((i % 2 == 0 ? ".com" : ".net")));
2832 args[i].time = base::Time::FromInternalValue(i);
2833 backend_->AddPage(args[i]);
2835 EXPECT_EQ(base::Time(), backend_->GetFirstRecordedTimeForTest());
2838 for (size_t i = 0; i < arraysize(args); ++i) {
2839 EXPECT_TRUE(backend_->GetURL(args[i].url, &row));
2842 std::set<base::Time> times;
2843 times.insert(args[5].time);
2844 backend_->ExpireHistoryForTimes(times,
2845 base::Time::FromInternalValue(2),
2846 base::Time::FromInternalValue(8));
2848 EXPECT_EQ(base::Time::FromInternalValue(0),
2849 backend_->GetFirstRecordedTimeForTest());
2851 // Visits to http://example.com are untouched.
2852 VisitVector visit_vector;
2853 EXPECT_TRUE(backend_->GetVisitsForURL(
2854 backend_->db_->GetRowForURL(GURL("http://example.com"), NULL),
2856 ASSERT_EQ(5u, visit_vector.size());
2857 EXPECT_EQ(base::Time::FromInternalValue(0), visit_vector[0].visit_time);
2858 EXPECT_EQ(base::Time::FromInternalValue(2), visit_vector[1].visit_time);
2859 EXPECT_EQ(base::Time::FromInternalValue(4), visit_vector[2].visit_time);
2860 EXPECT_EQ(base::Time::FromInternalValue(6), visit_vector[3].visit_time);
2861 EXPECT_EQ(base::Time::FromInternalValue(8), visit_vector[4].visit_time);
2863 // Visits to http://example.net between [2,8] are removed.
2864 visit_vector.clear();
2865 EXPECT_TRUE(backend_->GetVisitsForURL(
2866 backend_->db_->GetRowForURL(GURL("http://example.net"), NULL),
2868 ASSERT_EQ(2u, visit_vector.size());
2869 EXPECT_EQ(base::Time::FromInternalValue(1), visit_vector[0].visit_time);
2870 EXPECT_EQ(base::Time::FromInternalValue(9), visit_vector[1].visit_time);
2872 EXPECT_EQ(base::Time::FromInternalValue(0),
2873 backend_->GetFirstRecordedTimeForTest());
2876 TEST_F(HistoryBackendTest, ExpireHistory) {
2877 ASSERT_TRUE(backend_.get());
2878 // Since history operations are dependent on the local timezone, make all
2879 // entries relative to a fixed, local reference time.
2880 base::Time reference_time = base::Time::UnixEpoch().LocalMidnight() +
2881 base::TimeDelta::FromHours(12);
2883 // Insert 4 entries into the database.
2884 HistoryAddPageArgs args[4];
2885 for (size_t i = 0; i < arraysize(args); ++i) {
2886 args[i].url = GURL("http://example" + base::IntToString(i) + ".com");
2887 args[i].time = reference_time + base::TimeDelta::FromDays(i);
2888 backend_->AddPage(args[i]);
2892 for (unsigned int i = 0; i < arraysize(args); ++i)
2893 ASSERT_TRUE(backend_->GetURL(args[i].url, &url_rows[i]));
2895 std::vector<ExpireHistoryArgs> expire_list;
2898 // Passing an empty map should be a no-op.
2899 backend_->ExpireHistory(expire_list);
2900 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2901 EXPECT_EQ(4U, visits.size());
2903 // Trying to delete an unknown URL with the time of the first visit should
2905 expire_list.resize(expire_list.size() + 1);
2906 expire_list[0].SetTimeRangeForOneDay(args[0].time);
2907 expire_list[0].urls.insert(GURL("http://google.does-not-exist"));
2908 backend_->ExpireHistory(expire_list);
2909 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2910 EXPECT_EQ(4U, visits.size());
2912 // Now add the first URL with the same time -- it should get deleted.
2913 expire_list.back().urls.insert(url_rows[0].url());
2914 backend_->ExpireHistory(expire_list);
2916 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2917 ASSERT_EQ(3U, visits.size());
2918 EXPECT_EQ(visits[0].url_id, url_rows[1].id());
2919 EXPECT_EQ(visits[1].url_id, url_rows[2].id());
2920 EXPECT_EQ(visits[2].url_id, url_rows[3].id());
2922 // The first recorded time should also get updated.
2923 EXPECT_EQ(backend_->GetFirstRecordedTimeForTest(), args[1].time);
2925 // Now delete the rest of the visits in one call.
2926 for (unsigned int i = 1; i < arraysize(args); ++i) {
2927 expire_list.resize(expire_list.size() + 1);
2928 expire_list[i].SetTimeRangeForOneDay(args[i].time);
2929 expire_list[i].urls.insert(args[i].url);
2931 backend_->ExpireHistory(expire_list);
2933 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2934 ASSERT_EQ(0U, visits.size());
2937 TEST_F(HistoryBackendTest, DeleteMatchingUrlsForKeyword) {
2938 // Set up urls and keyword_search_terms
2939 GURL url1("https://www.bing.com/?q=bar");
2940 URLRow url_info1(url1);
2941 url_info1.set_visit_count(0);
2942 url_info1.set_typed_count(0);
2943 url_info1.set_last_visit(Time());
2944 url_info1.set_hidden(false);
2945 const URLID url1_id = backend_->db()->AddURL(url_info1);
2946 EXPECT_NE(0, url1_id);
2948 KeywordID keyword_id = 1;
2949 base::string16 keyword = base::UTF8ToUTF16("bar");
2950 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
2951 url1_id, keyword_id, keyword));
2953 GURL url2("https://www.google.com/?q=bar");
2954 URLRow url_info2(url2);
2955 url_info2.set_visit_count(0);
2956 url_info2.set_typed_count(0);
2957 url_info2.set_last_visit(Time());
2958 url_info2.set_hidden(false);
2959 const URLID url2_id = backend_->db()->AddURL(url_info2);
2960 EXPECT_NE(0, url2_id);
2962 KeywordID keyword_id2 = 2;
2963 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
2964 url2_id, keyword_id2, keyword));
2966 // Add another visit to the same URL
2967 URLRow url_info3(url2);
2968 url_info3.set_visit_count(0);
2969 url_info3.set_typed_count(0);
2970 url_info3.set_last_visit(Time());
2971 url_info3.set_hidden(false);
2972 const URLID url3_id = backend_->db()->AddURL(url_info3);
2973 EXPECT_NE(0, url3_id);
2974 ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
2975 url3_id, keyword_id2, keyword));
2977 // Test that deletion works correctly
2978 backend_->DeleteMatchingURLsForKeyword(keyword_id2, keyword);
2980 // Test that rows 2 and 3 are deleted, while 1 is intact
2982 EXPECT_TRUE(backend_->db()->GetURLRow(url1_id, &row));
2983 EXPECT_EQ(url1.spec(), row.url().spec());
2984 EXPECT_FALSE(backend_->db()->GetURLRow(url2_id, &row));
2985 EXPECT_FALSE(backend_->db()->GetURLRow(url3_id, &row));
2987 // Test that corresponding keyword search terms are deleted for rows 2 & 3,
2988 // but not for row 1
2989 EXPECT_TRUE(backend_->db()->GetKeywordSearchTermRow(url1_id, NULL));
2990 EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url2_id, NULL));
2991 EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url3_id, NULL));
2994 // Simple test that removes a bookmark. This test exercises the code paths in
2995 // History that block till bookmark bar model is loaded.
2996 TEST_F(HistoryBackendTest, RemoveNotification) {
2997 scoped_ptr<TestingProfile> profile(new TestingProfile());
3000 GURL url("http://www.google.com");
3001 HistoryClientMock history_client;
3002 history_client.AddBookmark(url);
3003 scoped_ptr<HistoryService> service(
3004 new HistoryService(&history_client, profile.get()));
3005 EXPECT_TRUE(service->Init(profile->GetPath()));
3008 url, base::Time::Now(), NULL, 1, GURL(), RedirectList(),
3009 ui::PAGE_TRANSITION_TYPED, SOURCE_BROWSED, false);
3011 // This won't actually delete the URL, rather it'll empty out the visits.
3012 // This triggers blocking on the BookmarkModel.
3013 EXPECT_CALL(history_client, BlockUntilBookmarksLoaded());
3014 service->DeleteURL(url);
3017 // Test DeleteFTSIndexDatabases deletes expected files.
3018 TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {
3019 ASSERT_TRUE(backend_.get());
3021 base::FilePath history_path(test_dir());
3022 base::FilePath db1(history_path.AppendASCII("History Index 2013-05"));
3023 base::FilePath db1_journal(db1.InsertBeforeExtensionASCII("-journal"));
3024 base::FilePath db1_wal(db1.InsertBeforeExtensionASCII("-wal"));
3025 base::FilePath db2_symlink(history_path.AppendASCII("History Index 2013-06"));
3026 base::FilePath db2_actual(history_path.AppendASCII("Underlying DB"));
3028 // Setup dummy index database files.
3029 const char* data = "Dummy";
3030 const size_t data_len = 5;
3031 ASSERT_TRUE(base::WriteFile(db1, data, data_len));
3032 ASSERT_TRUE(base::WriteFile(db1_journal, data, data_len));
3033 ASSERT_TRUE(base::WriteFile(db1_wal, data, data_len));
3034 ASSERT_TRUE(base::WriteFile(db2_actual, data, data_len));
3035 #if defined(OS_POSIX)
3036 EXPECT_TRUE(base::CreateSymbolicLink(db2_actual, db2_symlink));
3039 // Delete all DTS index databases.
3040 backend_->DeleteFTSIndexDatabases();
3041 EXPECT_FALSE(base::PathExists(db1));
3042 EXPECT_FALSE(base::PathExists(db1_wal));
3043 EXPECT_FALSE(base::PathExists(db1_journal));
3044 EXPECT_FALSE(base::PathExists(db2_symlink));
3045 EXPECT_TRUE(base::PathExists(db2_actual)); // Symlinks shouldn't be followed.
3048 // Common implementation for the two tests below, given that the only difference
3049 // between them is the type of the notification sent out.
3050 void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows(
3051 const SimulateNotificationCallback& callback) {
3052 const char kTestTypedURLAlternativeTitle[] = "Google Search Again";
3053 const char kTestNonTypedURLAlternativeTitle[] = "Google News Again";
3055 // Notify the in-memory database that a typed and non-typed URLRow (which were
3056 // never before seen by the cache) have been modified.
3057 URLRow row1(CreateTestTypedURL());
3058 URLRow row2(CreateTestNonTypedURL());
3059 callback.Run(&row1, &row2, nullptr);
3061 // The in-memory database should only pick up the typed URL, and should ignore
3062 // the non-typed one. The typed URL should retain the ID that was present in
3063 // the notification.
3064 URLRow cached_row1, cached_row2;
3065 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3066 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3067 EXPECT_EQ(row1.id(), cached_row1.id());
3069 // Try changing attributes (other than typed_count) for existing URLRows.
3070 row1.set_title(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle));
3071 row2.set_title(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle));
3072 callback.Run(&row1, &row2, nullptr);
3074 // URLRows that are cached by the in-memory database should be updated.
3075 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3076 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3077 EXPECT_EQ(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle),
3078 cached_row1.title());
3080 // Now decrease the typed count for the typed URLRow, and increase it for the
3081 // previously non-typed URLRow.
3082 row1.set_typed_count(0);
3083 row2.set_typed_count(2);
3084 callback.Run(&row1, &row2, nullptr);
3086 // The in-memory database should stop caching the first URLRow, and start
3087 // caching the second URLRow.
3088 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3089 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3090 EXPECT_EQ(row2.id(), cached_row2.id());
3091 EXPECT_EQ(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle),
3092 cached_row2.title());
3095 TEST_F(InMemoryHistoryBackendTest, OnURLsModified) {
3096 TestAddingAndChangingURLRows(
3097 base::Bind(&InMemoryHistoryBackendTest::SimulateNotification,
3098 base::Unretained(this),
3099 chrome::NOTIFICATION_HISTORY_URLS_MODIFIED));
3102 TEST_F(InMemoryHistoryBackendTest, OnURLsVisisted) {
3103 TestAddingAndChangingURLRows(base::Bind(
3104 &SimulateNotificationURLVisited, base::Unretained(mem_backend_.get())));
3107 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedPiecewise) {
3108 // Add two typed and one non-typed URLRow to the in-memory database.
3109 URLRow row1(CreateTestTypedURL());
3110 URLRow row2(CreateAnotherTestTypedURL());
3111 URLRow row3(CreateTestNonTypedURL());
3112 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
3113 &row1, &row2, &row3);
3115 // Notify the in-memory database that the second typed URL and the non-typed
3116 // URL has been deleted.
3117 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
3120 // Expect that the first typed URL remains intact, the second typed URL is
3121 // correctly removed, and the non-typed URL does not magically appear.
3123 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3124 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
3125 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
3126 EXPECT_EQ(row1.id(), cached_row1.id());
3129 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedEnMasse) {
3130 // Add two typed and one non-typed URLRow to the in-memory database.
3131 URLRow row1(CreateTestTypedURL());
3132 URLRow row2(CreateAnotherTestTypedURL());
3133 URLRow row3(CreateTestNonTypedURL());
3134 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_MODIFIED,
3135 &row1, &row2, &row3);
3137 // Now notify the in-memory database that all history has been deleted.
3138 scoped_ptr<URLsDeletedDetails> details(new URLsDeletedDetails());
3139 details->all_history = true;
3140 BroadcastNotifications(chrome::NOTIFICATION_HISTORY_URLS_DELETED,
3143 // Expect that everything goes away.
3144 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), NULL));
3145 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), NULL));
3146 EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row3.url(), NULL));
3149 void InMemoryHistoryBackendTest::PopulateTestURLsAndSearchTerms(
3152 const base::string16& term1,
3153 const base::string16& term2) {
3154 // Add a typed and a non-typed URLRow to the in-memory database. This time,
3155 // though, do it through the history backend...
3157 rows.push_back(*row1);
3158 rows.push_back(*row2);
3159 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
3160 backend_->db()->GetRowForURL(row1->url(), row1); // Get effective IDs from
3161 backend_->db()->GetRowForURL(row2->url(), row2); // the database.
3163 // ... so that we can also use that for adding the search terms. This way, we
3164 // not only test that the notifications involved are handled correctly, but
3165 // also that they are fired correctly (in the history backend).
3166 backend_->SetKeywordSearchTermsForURL(row1->url(), kTestKeywordId, term1);
3167 backend_->SetKeywordSearchTermsForURL(row2->url(), kTestKeywordId, term2);
3170 TEST_F(InMemoryHistoryBackendTest, SetKeywordSearchTerms) {
3171 URLRow row1(CreateTestTypedURL());
3172 URLRow row2(CreateTestNonTypedURL());
3173 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3174 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3175 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3177 // Both URLs now have associated search terms, so the in-memory database
3178 // should cache both of them, regardless whether they have been typed or not.
3179 URLRow cached_row1, cached_row2;
3180 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3181 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
3182 EXPECT_EQ(row1.id(), cached_row1.id());
3183 EXPECT_EQ(row2.id(), cached_row2.id());
3185 // Verify that lookups will actually return both search terms; and also check
3186 // at the low level that the rows are there.
3187 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3188 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3189 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3190 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3193 TEST_F(InMemoryHistoryBackendTest, DeleteKeywordSearchTerms) {
3194 URLRow row1(CreateTestTypedURL());
3195 URLRow row2(CreateTestNonTypedURL());
3196 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3197 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3198 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3200 // Delete both search terms. This should be reflected in the in-memory DB.
3201 backend_->DeleteKeywordSearchTermForURL(row1.url());
3202 backend_->DeleteKeywordSearchTermForURL(row2.url());
3204 // The typed URL should remain intact.
3205 // Note: we do not need to guarantee anything about the non-typed URL.
3207 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3208 EXPECT_EQ(row1.id(), cached_row1.id());
3210 // Verify that the search terms are no longer returned as results, and also
3211 // check at the low level that they are gone for good.
3212 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3213 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3214 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3215 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3218 TEST_F(InMemoryHistoryBackendTest, DeleteAllSearchTermsForKeyword) {
3219 URLRow row1(CreateTestTypedURL());
3220 URLRow row2(CreateTestNonTypedURL());
3221 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3222 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3223 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3225 // Delete all corresponding search terms from the in-memory database.
3226 KeywordID id = kTestKeywordId;
3227 mem_backend_->DeleteAllSearchTermsForKeyword(id);
3229 // The typed URL should remain intact.
3230 // Note: we do not need to guarantee anything about the non-typed URL.
3232 EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
3233 EXPECT_EQ(row1.id(), cached_row1.id());
3235 // Verify that the search terms are no longer returned as results, and also
3236 // check at the low level that they are gone for good.
3237 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3238 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3239 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3240 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3243 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedWithSearchTerms) {
3244 URLRow row1(CreateTestTypedURL());
3245 URLRow row2(CreateTestNonTypedURL());
3246 base::string16 term1(base::UTF8ToUTF16(kTestSearchTerm1));
3247 base::string16 term2(base::UTF8ToUTF16(kTestSearchTerm2));
3248 PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
3250 // Notify the in-memory database that the second typed URL has been deleted.
3251 SimulateNotification(chrome::NOTIFICATION_HISTORY_URLS_DELETED, &row2);
3253 // Verify that the second term is no longer returned as result, and also check
3254 // at the low level that it is gone for good. The term corresponding to the
3255 // first URLRow should not be affected.
3256 EXPECT_EQ(1u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term1));
3257 EXPECT_EQ(0u, GetNumberOfMatchingSearchTerms(kTestKeywordId, term2));
3258 EXPECT_TRUE(mem_backend_->db()->GetKeywordSearchTermRow(row1.id(), NULL));
3259 EXPECT_FALSE(mem_backend_->db()->GetKeywordSearchTermRow(row2.id(), NULL));
3262 } // namespace history