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