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/file_util.h"
15 #include "base/files/file_path.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/path_service.h"
19 #include "base/run_loop.h"
20 #include "base/strings/string16.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "chrome/browser/bookmarks/bookmark_model.h"
24 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
25 #include "chrome/browser/bookmarks/bookmark_test_helpers.h"
26 #include "chrome/browser/bookmarks/bookmark_utils.h"
27 #include "chrome/browser/history/history_notifications.h"
28 #include "chrome/browser/history/history_service.h"
29 #include "chrome/browser/history/history_service_factory.h"
30 #include "chrome/browser/history/in_memory_database.h"
31 #include "chrome/browser/history/in_memory_history_backend.h"
32 #include "chrome/browser/history/visit_filter.h"
33 #include "chrome/common/chrome_constants.h"
34 #include "chrome/common/chrome_paths.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/importer/imported_favicon_usage.h"
37 #include "chrome/test/base/testing_profile.h"
38 #include "content/public/browser/notification_details.h"
39 #include "content/public/browser/notification_source.h"
40 #include "content/public/test/test_browser_thread.h"
41 #include "testing/gtest/include/gtest/gtest.h"
45 using base::TimeDelta;
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 static const gfx::Size kTinySize = gfx::Size(10, 10);
55 static const gfx::Size kSmallSize = gfx::Size(16, 16);
56 static const gfx::Size kLargeSize = gfx::Size(32, 32);
58 // Comparison functions as to make it easier to check results of
59 // GetFaviconBitmaps() and GetIconMappingsForPageURL().
60 bool IconMappingLessThan(const history::IconMapping& a,
61 const history::IconMapping& b) {
62 return a.icon_url < b.icon_url;
65 bool FaviconBitmapLessThan(const history::FaviconBitmap& a,
66 const history::FaviconBitmap& b) {
67 return a.pixel_size.GetArea() < b.pixel_size.GetArea();
74 class HistoryBackendTest;
76 // This must be a separate object since HistoryBackend manages its lifetime.
77 // This just forwards the messages we're interested in to the test object.
78 class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
80 explicit HistoryBackendTestDelegate(HistoryBackendTest* test) : test_(test) {}
82 virtual void NotifyProfileError(int backend_id,
83 sql::InitStatus init_status) OVERRIDE {}
84 virtual void SetInMemoryBackend(int backend_id,
85 InMemoryHistoryBackend* backend) OVERRIDE;
86 virtual void BroadcastNotifications(int type,
87 HistoryDetails* details) OVERRIDE;
88 virtual void DBLoaded(int backend_id) OVERRIDE;
89 virtual void NotifyVisitDBObserversOnAddVisit(
90 const BriefVisitInfo& info) OVERRIDE {}
94 HistoryBackendTest* test_;
96 DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
99 class HistoryBackendCancelableRequest
100 : public CancelableRequestProvider,
101 public CancelableRequestConsumerTSimple<int> {
103 HistoryBackendCancelableRequest() {}
105 template<class RequestType>
106 CancelableRequestProvider::Handle MockScheduleOfRequest(
107 RequestType* request) {
108 AddRequest(request, this);
109 return request->handle();
113 class HistoryBackendTest : public testing::Test {
116 : bookmark_model_(NULL),
118 num_broadcasted_notifications_(0),
119 ui_thread_(content::BrowserThread::UI, &message_loop_) {
122 virtual ~HistoryBackendTest() {
125 // Callback for QueryMostVisited.
126 void OnQueryMostVisited(CancelableRequestProvider::Handle handle,
127 history::MostVisitedURLList data) {
128 most_visited_list_.swap(data);
131 // Callback for QueryFiltered.
132 void OnQueryFiltered(CancelableRequestProvider::Handle handle,
133 const history::FilteredURLList& data) {
134 filtered_list_ = data;
137 const history::MostVisitedURLList& get_most_visited_list() const {
138 return most_visited_list_;
141 const history::FilteredURLList& get_filtered_list() const {
142 return filtered_list_;
145 int num_broadcasted_notifications() const {
146 return num_broadcasted_notifications_;
150 scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
151 scoped_ptr<InMemoryHistoryBackend> mem_backend_;
153 void AddRedirectChain(const char* sequence[], int page_id) {
154 AddRedirectChainWithTransitionAndTime(sequence, page_id,
155 content::PAGE_TRANSITION_LINK,
159 void AddRedirectChainWithTransitionAndTime(
160 const char* sequence[],
162 content::PageTransition transition,
164 history::RedirectList redirects;
165 for (int i = 0; sequence[i] != NULL; ++i)
166 redirects.push_back(GURL(sequence[i]));
170 memcpy(&scope, &int_scope, sizeof(int_scope));
171 history::HistoryAddPageArgs request(
172 redirects.back(), time, scope, page_id, GURL(),
173 redirects, transition, history::SOURCE_BROWSED,
175 backend_->AddPage(request);
178 // Adds CLIENT_REDIRECT page transition.
179 // |url1| is the source URL and |url2| is the destination.
180 // |did_replace| is true if the transition is non-user initiated and the
181 // navigation entry for |url2| has replaced that for |url1|. The possibly
182 // updated transition code of the visit records for |url1| and |url2| is
183 // returned by filling in |*transition1| and |*transition2|, respectively.
184 // |time| is a time of the redirect.
185 void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
187 int* transition1, int* transition2) {
188 void* const dummy_scope = reinterpret_cast<void*>(0x87654321);
189 history::RedirectList redirects;
191 redirects.push_back(url1);
193 redirects.push_back(url2);
194 HistoryAddPageArgs request(
195 url2, time, dummy_scope, 0, url1,
196 redirects, content::PAGE_TRANSITION_CLIENT_REDIRECT,
197 history::SOURCE_BROWSED, did_replace);
198 backend_->AddPage(request);
200 *transition1 = getTransition(url1);
201 *transition2 = getTransition(url2);
204 int getTransition(const GURL& url) {
208 URLID id = backend_->db()->GetRowForURL(url, &row);
210 EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
211 return visits[0].transition;
214 base::FilePath getTestDir() {
218 // Returns a gfx::Size vector with small size.
219 const std::vector<gfx::Size> GetSizesSmall() {
220 std::vector<gfx::Size> sizes_small;
221 sizes_small.push_back(kSmallSize);
225 // Returns a gfx::Size vector with large size.
226 const std::vector<gfx::Size> GetSizesLarge() {
227 std::vector<gfx::Size> sizes_large;
228 sizes_large.push_back(kLargeSize);
232 // Returns a gfx::Size vector with small and large sizes.
233 const std::vector<gfx::Size> GetSizesSmallAndLarge() {
234 std::vector<gfx::Size> sizes_small_and_large;
235 sizes_small_and_large.push_back(kSmallSize);
236 sizes_small_and_large.push_back(kLargeSize);
237 return sizes_small_and_large;
240 // Returns a gfx::Size vector with tiny, small and large sizes.
241 const std::vector<gfx::Size> GetSizesTinySmallAndLarge() {
242 std::vector<gfx::Size> sizes_tiny_small_and_large;
243 sizes_tiny_small_and_large.push_back(kTinySize);
244 sizes_tiny_small_and_large.push_back(kSmallSize);
245 sizes_tiny_small_and_large.push_back(kLargeSize);
246 return sizes_tiny_small_and_large;
249 // Returns 1x and 2x scale factors.
250 const std::vector<ui::ScaleFactor> GetScaleFactors1x2x() {
251 std::vector<ui::ScaleFactor> scale_factors_1x_2x;
252 scale_factors_1x_2x.push_back(ui::SCALE_FACTOR_100P);
253 scale_factors_1x_2x.push_back(ui::SCALE_FACTOR_200P);
254 return scale_factors_1x_2x;
257 // Returns the number of icon mappings of |icon_type| to |page_url|.
258 size_t NumIconMappingsForPageURL(const GURL& page_url,
259 chrome::IconType icon_type) {
260 std::vector<IconMapping> icon_mappings;
261 backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type,
263 return icon_mappings.size();
266 // Returns the icon mappings for |page_url| sorted alphabetically by icon
267 // URL in ascending order. Returns true if there is at least one icon
269 bool GetSortedIconMappingsForPageURL(
270 const GURL& page_url,
271 std::vector<IconMapping>* icon_mappings) {
272 if (!backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
276 std::sort(icon_mappings->begin(), icon_mappings->end(),
277 IconMappingLessThan);
281 // Returns the favicon bitmaps for |icon_id| sorted by pixel size in
282 // ascending order. Returns true if there is at least one favicon bitmap.
283 bool GetSortedFaviconBitmaps(chrome::FaviconID icon_id,
284 std::vector<FaviconBitmap>* favicon_bitmaps) {
285 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
287 std::sort(favicon_bitmaps->begin(), favicon_bitmaps->end(),
288 FaviconBitmapLessThan);
292 // Returns true if there is exactly one favicon bitmap associated to
293 // |favicon_id|. If true, returns favicon bitmap in output parameter.
294 bool GetOnlyFaviconBitmap(const chrome::FaviconID icon_id,
295 FaviconBitmap* favicon_bitmap) {
296 std::vector<FaviconBitmap> favicon_bitmaps;
297 if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
299 if (favicon_bitmaps.size() != 1)
301 *favicon_bitmap = favicon_bitmaps[0];
305 // Generates |favicon_bitmap_data| with entries for the icon_urls and sizes
306 // specified. The bitmap_data for entries are lowercase letters of the
307 // alphabet starting at 'a' for the entry at index 0.
308 void GenerateFaviconBitmapData(
309 const GURL& icon_url1,
310 const std::vector<gfx::Size>& icon_url1_sizes,
311 std::vector<chrome::FaviconBitmapData>* favicon_bitmap_data) {
312 GenerateFaviconBitmapData(icon_url1, icon_url1_sizes, GURL(),
313 std::vector<gfx::Size>(), favicon_bitmap_data);
316 void GenerateFaviconBitmapData(
317 const GURL& icon_url1,
318 const std::vector<gfx::Size>& icon_url1_sizes,
319 const GURL& icon_url2,
320 const std::vector<gfx::Size>& icon_url2_sizes,
321 std::vector<chrome::FaviconBitmapData>* favicon_bitmap_data) {
322 favicon_bitmap_data->clear();
324 char bitmap_char = 'a';
325 for (size_t i = 0; i < icon_url1_sizes.size(); ++i) {
326 std::vector<unsigned char> data;
327 data.push_back(bitmap_char);
328 chrome::FaviconBitmapData bitmap_data_element;
329 bitmap_data_element.bitmap_data =
330 base::RefCountedBytes::TakeVector(&data);
331 bitmap_data_element.pixel_size = icon_url1_sizes[i];
332 bitmap_data_element.icon_url = icon_url1;
333 favicon_bitmap_data->push_back(bitmap_data_element);
338 for (size_t i = 0; i < icon_url2_sizes.size(); ++i) {
339 std::vector<unsigned char> data;
340 data.push_back(bitmap_char);
341 chrome::FaviconBitmapData bitmap_data_element;
342 bitmap_data_element.bitmap_data =
343 base::RefCountedBytes::TakeVector(&data);
344 bitmap_data_element.pixel_size = icon_url2_sizes[i];
345 bitmap_data_element.icon_url = icon_url2;
346 favicon_bitmap_data->push_back(bitmap_data_element);
352 // Returns true if |bitmap_data| is equal to |expected_data|.
353 bool BitmapDataEqual(char expected_data,
354 scoped_refptr<base::RefCountedMemory> bitmap_data) {
355 return bitmap_data.get() &&
356 bitmap_data->size() == 1u &&
357 *bitmap_data->front() == expected_data;
360 BookmarkModel bookmark_model_;
364 virtual void SetUp() {
365 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
368 backend_ = new HistoryBackend(test_dir_,
370 new HistoryBackendTestDelegate(this),
372 backend_->Init(std::string(), false);
378 friend class HistoryBackendTestDelegate;
380 virtual void TearDown() {
384 mem_backend_.reset();
385 base::DeleteFile(test_dir_, true);
386 base::RunLoop().RunUntilIdle();
389 void SetInMemoryBackend(int backend_id, InMemoryHistoryBackend* backend) {
390 mem_backend_.reset(backend);
393 void BroadcastNotifications(int type,
394 HistoryDetails* details) {
395 ++num_broadcasted_notifications_;
397 // Send the notifications directly to the in-memory database.
398 content::Details<HistoryDetails> det(details);
399 mem_backend_->Observe(type, content::Source<HistoryBackendTest>(NULL), det);
401 // The backend passes ownership of the details pointer to us.
405 // The number of notifications which were broadcasted.
406 int num_broadcasted_notifications_;
408 base::MessageLoop message_loop_;
409 base::FilePath test_dir_;
410 history::MostVisitedURLList most_visited_list_;
411 history::FilteredURLList filtered_list_;
412 content::TestBrowserThread ui_thread_;
415 void HistoryBackendTestDelegate::SetInMemoryBackend(int backend_id,
416 InMemoryHistoryBackend* backend) {
417 test_->SetInMemoryBackend(backend_id, backend);
420 void HistoryBackendTestDelegate::BroadcastNotifications(
422 HistoryDetails* details) {
423 test_->BroadcastNotifications(type, details);
426 void HistoryBackendTestDelegate::DBLoaded(int backend_id) {
427 test_->loaded_ = true;
430 // http://crbug.com/114287
432 #define MAYBE_Loaded DISABLED_Loaded
434 #define MAYBE_Loaded Loaded
435 #endif // defined(OS_WIN)
436 TEST_F(HistoryBackendTest, MAYBE_Loaded) {
437 ASSERT_TRUE(backend_.get());
438 ASSERT_TRUE(loaded_);
441 TEST_F(HistoryBackendTest, DeleteAll) {
442 ASSERT_TRUE(backend_.get());
444 // Add two favicons, each with two bitmaps. Note that we add favicon2 before
445 // adding favicon1. This is so that favicon1 one gets ID 2 autoassigned to
446 // the database, which will change when the other one is deleted. This way
447 // we can test that updating works properly.
448 GURL favicon_url1("http://www.google.com/favicon.ico");
449 GURL favicon_url2("http://news.google.com/favicon.ico");
450 chrome::FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
452 chrome::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
455 std::vector<unsigned char> data;
457 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
458 new base::RefCountedBytes(data), Time::Now(), kSmallSize));
460 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
461 new base::RefCountedBytes(data), Time::Now(), kLargeSize));
464 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
465 new base::RefCountedBytes(data), Time::Now(), kSmallSize));
467 EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
468 new base::RefCountedBytes(data), Time::Now(), kLargeSize));
470 // First visit two URLs.
471 URLRow row1(GURL("http://www.google.com/"));
472 row1.set_visit_count(2);
473 row1.set_typed_count(1);
474 row1.set_last_visit(Time::Now());
475 backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
477 URLRow row2(GURL("http://news.google.com/"));
478 row2.set_visit_count(1);
479 row2.set_last_visit(Time::Now());
480 backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
483 rows.push_back(row2); // Reversed order for the same reason as favicons.
484 rows.push_back(row1);
485 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
487 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
488 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
490 // Get the two visits for the URLs we just added.
492 backend_->db_->GetVisitsForURL(row1_id, &visits);
493 ASSERT_EQ(1U, visits.size());
496 backend_->db_->GetVisitsForURL(row2_id, &visits);
497 ASSERT_EQ(1U, visits.size());
499 // The in-memory backend should have been set and it should have gotten the
501 ASSERT_TRUE(mem_backend_.get());
503 EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
506 bookmark_model_.AddURL(
507 bookmark_model_.bookmark_bar_node(), 0, string16(), row1.url());
509 // Now finally clear all history.
510 backend_->DeleteAllHistory();
512 // The first URL should be preserved but the time should be cleared.
513 EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
514 EXPECT_EQ(row1.url(), outrow1.url());
515 EXPECT_EQ(0, outrow1.visit_count());
516 EXPECT_EQ(0, outrow1.typed_count());
517 EXPECT_TRUE(Time() == outrow1.last_visit());
519 // The second row should be deleted.
521 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
523 // All visits should be deleted for both URLs.
524 VisitVector all_visits;
525 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
526 ASSERT_EQ(0U, all_visits.size());
528 // We should have a favicon and favicon bitmaps for the first URL only. We
529 // look them up by favicon URL since the IDs may have changed.
530 chrome::FaviconID out_favicon1 = backend_->thumbnail_db_->
531 GetFaviconIDForFaviconURL(favicon_url1, chrome::FAVICON, NULL);
532 EXPECT_TRUE(out_favicon1);
534 std::vector<FaviconBitmap> favicon_bitmaps;
535 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
536 out_favicon1, &favicon_bitmaps));
537 ASSERT_EQ(2u, favicon_bitmaps.size());
539 FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0];
540 FaviconBitmap favicon_bitmap2 = favicon_bitmaps[1];
542 // Favicon bitmaps do not need to be in particular order.
543 if (favicon_bitmap1.pixel_size == kLargeSize) {
544 FaviconBitmap tmp_favicon_bitmap = favicon_bitmap1;
545 favicon_bitmap1 = favicon_bitmap2;
546 favicon_bitmap2 = tmp_favicon_bitmap;
549 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap1.bitmap_data));
550 EXPECT_EQ(kSmallSize, favicon_bitmap1.pixel_size);
552 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap2.bitmap_data));
553 EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size);
555 chrome::FaviconID out_favicon2 = backend_->thumbnail_db_->
556 GetFaviconIDForFaviconURL(favicon_url2, chrome::FAVICON, NULL);
557 EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
559 // The remaining URL should still reference the same favicon, even if its
561 std::vector<IconMapping> mappings;
562 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
563 outrow1.url(), chrome::FAVICON, &mappings));
564 EXPECT_EQ(1u, mappings.size());
565 EXPECT_EQ(out_favicon1, mappings[0].icon_id);
567 // The first URL should still be bookmarked.
568 EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url()));
571 // Checks that adding a visit, then calling DeleteAll, and then trying to add
572 // data for the visited page works. This can happen when clearing the history
573 // immediately after visiting a page.
574 TEST_F(HistoryBackendTest, DeleteAllThenAddData) {
575 ASSERT_TRUE(backend_.get());
577 Time visit_time = Time::Now();
578 GURL url("http://www.google.com/");
579 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
580 history::RedirectList(),
581 content::PAGE_TRANSITION_KEYWORD_GENERATED,
582 history::SOURCE_BROWSED, false);
583 backend_->AddPage(request);
585 // Check that a row was added.
587 EXPECT_TRUE(backend_->db_->GetRowForURL(url, &outrow));
589 // Check that the visit was added.
590 VisitVector all_visits;
591 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
592 ASSERT_EQ(1U, all_visits.size());
594 // Clear all history.
595 backend_->DeleteAllHistory();
597 // The row should be deleted.
598 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
600 // The visit should be deleted.
601 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
602 ASSERT_EQ(0U, all_visits.size());
604 // Try and set the title.
605 backend_->SetPageTitle(url, UTF8ToUTF16("Title"));
607 // The row should still be deleted.
608 EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
610 // The visit should still be deleted.
611 backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
612 ASSERT_EQ(0U, all_visits.size());
615 TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
616 GURL favicon_url1("http://www.google.com/favicon.ico");
617 GURL favicon_url2("http://news.google.com/favicon.ico");
619 std::vector<unsigned char> data;
621 chrome::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
624 new base::RefCountedBytes(data),
629 chrome::FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(
632 new base::RefCountedBytes(data),
636 // First visit two URLs.
637 URLRow row1(GURL("http://www.google.com/"));
638 row1.set_visit_count(2);
639 row1.set_typed_count(1);
640 row1.set_last_visit(Time::Now());
641 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
643 URLRow row2(GURL("http://news.google.com/"));
644 row2.set_visit_count(1);
645 row2.set_last_visit(Time::Now());
646 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
649 rows.push_back(row2); // Reversed order for the same reason as favicons.
650 rows.push_back(row1);
651 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
653 URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
654 URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
656 // Star the two URLs.
657 bookmark_utils::AddIfNotBookmarked(&bookmark_model_, row1.url(), string16());
658 bookmark_utils::AddIfNotBookmarked(&bookmark_model_, row2.url(), string16());
660 // Delete url 2. Because url 2 is starred this won't delete the URL, only
662 backend_->expirer_.DeleteURL(row2.url());
664 // Make sure url 2 is still valid, but has no visits.
666 EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
668 backend_->db_->GetVisitsForURL(row2_id, &visits);
669 EXPECT_EQ(0U, visits.size());
670 // The favicon should still be valid.
672 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
677 bookmark_utils::RemoveAllBookmarks(&bookmark_model_, row2.url());
679 // Tell the backend it was unstarred. We have to explicitly do this as
680 // BookmarkModel isn't wired up to the backend during testing.
681 std::set<GURL> unstarred_urls;
682 unstarred_urls.insert(row2.url());
683 backend_->URLsNoLongerBookmarked(unstarred_urls);
685 // The URL should no longer exist.
686 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
687 // And the favicon should be deleted.
689 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
694 bookmark_utils::RemoveAllBookmarks(&bookmark_model_, row1.url());
695 // Tell the backend it was unstarred. We have to explicitly do this as
696 // BookmarkModel isn't wired up to the backend during testing.
697 unstarred_urls.clear();
698 unstarred_urls.insert(row1.url());
699 backend_->URLsNoLongerBookmarked(unstarred_urls);
701 // The URL should still exist (because there were visits).
702 EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
704 // There should still be visits.
706 backend_->db_->GetVisitsForURL(row1_id, &visits);
707 EXPECT_EQ(1U, visits.size());
709 // The favicon should still be valid.
711 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url1,
716 // Tests a handful of assertions for a navigation with a type of
717 // KEYWORD_GENERATED.
718 TEST_F(HistoryBackendTest, KeywordGenerated) {
719 ASSERT_TRUE(backend_.get());
721 GURL url("http://google.com");
723 Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
724 HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
725 history::RedirectList(),
726 content::PAGE_TRANSITION_KEYWORD_GENERATED,
727 history::SOURCE_BROWSED, false);
728 backend_->AddPage(request);
730 // A row should have been added for the url.
732 URLID url_id = backend_->db()->GetRowForURL(url, &row);
733 ASSERT_NE(0, url_id);
735 // The typed count should be 1.
736 ASSERT_EQ(1, row.typed_count());
738 // KEYWORD_GENERATED urls should not be added to the segment db.
739 std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
740 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
742 // One visit should be added.
744 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
745 EXPECT_EQ(1U, visits.size());
747 // But no visible visits.
749 QueryOptions query_options;
750 query_options.max_count = 1;
751 backend_->db()->GetVisibleVisitsInRange(query_options, &visits);
752 EXPECT_TRUE(visits.empty());
754 // Expire the visits.
755 std::set<GURL> restrict_urls;
756 backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
757 visit_time, Time::Now());
759 // The visit should have been nuked.
761 EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
762 EXPECT_TRUE(visits.empty());
764 // As well as the url.
765 ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
768 TEST_F(HistoryBackendTest, ClientRedirect) {
769 ASSERT_TRUE(backend_.get());
774 // Initial transition to page A.
775 GURL url_a("http://google.com/a");
776 AddClientRedirect(GURL(), url_a, false, base::Time(),
777 &transition1, &transition2);
778 EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
780 // User initiated redirect to page B.
781 GURL url_b("http://google.com/b");
782 AddClientRedirect(url_a, url_b, false, base::Time(),
783 &transition1, &transition2);
784 EXPECT_TRUE(transition1 & content::PAGE_TRANSITION_CHAIN_END);
785 EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
787 // Non-user initiated redirect to page C.
788 GURL url_c("http://google.com/c");
789 AddClientRedirect(url_b, url_c, true, base::Time(),
790 &transition1, &transition2);
791 EXPECT_FALSE(transition1 & content::PAGE_TRANSITION_CHAIN_END);
792 EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
795 TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
796 // Setup test data - two Urls in the history, one with favicon assigned and
798 GURL favicon_url1("http://www.google.com/favicon.ico");
799 std::vector<unsigned char> data;
801 chrome::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
804 base::RefCountedBytes::TakeVector(&data),
807 URLRow row1(GURL("http://www.google.com/"));
808 row1.set_visit_count(1);
809 row1.set_last_visit(Time::Now());
810 EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
812 URLRow row2(GURL("http://news.google.com/"));
813 row2.set_visit_count(1);
814 row2.set_last_visit(Time::Now());
816 rows.push_back(row1);
817 rows.push_back(row2);
818 backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
819 URLRow url_row1, url_row2;
820 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
821 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
822 EXPECT_EQ(1u, NumIconMappingsForPageURL(row1.url(), chrome::FAVICON));
823 EXPECT_EQ(0u, NumIconMappingsForPageURL(row2.url(), chrome::FAVICON));
825 // Now provide one imported favicon for both URLs already in the registry.
826 // The new favicon should only be used with the URL that doesn't already have
828 std::vector<ImportedFaviconUsage> favicons;
829 ImportedFaviconUsage favicon;
830 favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
831 favicon.png_data.push_back('2');
832 favicon.urls.insert(row1.url());
833 favicon.urls.insert(row2.url());
834 favicons.push_back(favicon);
835 backend_->SetImportedFavicons(favicons);
836 EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
837 EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
839 std::vector<IconMapping> mappings;
840 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
841 row1.url(), chrome::FAVICON, &mappings));
842 EXPECT_EQ(1u, mappings.size());
843 EXPECT_EQ(favicon1, mappings[0].icon_id);
844 EXPECT_EQ(favicon_url1, mappings[0].icon_url);
847 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
848 row2.url(), chrome::FAVICON, &mappings));
849 EXPECT_EQ(1u, mappings.size());
850 EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url);
852 // A URL should not be added to history (to store favicon), if
853 // the URL is not bookmarked.
854 GURL url3("http://mail.google.com");
856 favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
857 favicon.png_data.push_back('3');
858 favicon.urls.insert(url3);
859 favicons.push_back(favicon);
860 backend_->SetImportedFavicons(favicons);
862 EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
864 // If the URL is bookmarked, it should get added to history with 0 visits.
865 bookmark_model_.AddURL(bookmark_model_.bookmark_bar_node(), 0, string16(),
867 backend_->SetImportedFavicons(favicons);
868 EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
869 EXPECT_TRUE(url_row3.visit_count() == 0);
872 TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
873 ASSERT_TRUE(backend_.get());
875 GURL url("http://anyuser:anypass@www.google.com");
876 GURL stripped_url("http://www.google.com");
878 // Clear all history.
879 backend_->DeleteAllHistory();
881 // Visit the url with username, password.
882 backend_->AddPageVisit(url, base::Time::Now(), 0,
883 content::PageTransitionFromInt(
884 content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
885 history::SOURCE_BROWSED);
887 // Fetch the row information about stripped url from history db.
889 URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
890 backend_->db_->GetVisitsForURL(row_id, &visits);
892 // Check if stripped url is stored in database.
893 ASSERT_EQ(1U, visits.size());
896 TEST_F(HistoryBackendTest, AddPageVisitSource) {
897 ASSERT_TRUE(backend_.get());
899 GURL url("http://www.google.com");
901 // Clear all history.
902 backend_->DeleteAllHistory();
904 // Assume visiting the url from an externsion.
905 backend_->AddPageVisit(
906 url, base::Time::Now(), 0, content::PAGE_TRANSITION_TYPED,
907 history::SOURCE_EXTENSION);
908 // Assume the url is imported from Firefox.
909 backend_->AddPageVisit(url, base::Time::Now(), 0,
910 content::PAGE_TRANSITION_TYPED,
911 history::SOURCE_FIREFOX_IMPORTED);
912 // Assume this url is also synced.
913 backend_->AddPageVisit(url, base::Time::Now(), 0,
914 content::PAGE_TRANSITION_TYPED,
915 history::SOURCE_SYNCED);
917 // Fetch the row information about the url from history db.
919 URLID row_id = backend_->db_->GetRowForURL(url, NULL);
920 backend_->db_->GetVisitsForURL(row_id, &visits);
922 // Check if all the visits to the url are stored in database.
923 ASSERT_EQ(3U, visits.size());
924 VisitSourceMap visit_sources;
925 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
926 ASSERT_EQ(3U, visit_sources.size());
928 for (int i = 0; i < 3; i++) {
929 switch (visit_sources[visits[i].visit_id]) {
930 case history::SOURCE_EXTENSION:
933 case history::SOURCE_FIREFOX_IMPORTED:
936 case history::SOURCE_SYNCED:
942 EXPECT_EQ(0x7, sources);
945 TEST_F(HistoryBackendTest, AddPageVisitNotLastVisit) {
946 ASSERT_TRUE(backend_.get());
948 GURL url("http://www.google.com");
950 // Clear all history.
951 backend_->DeleteAllHistory();
953 // Create visit times
954 base::Time recent_time = base::Time::Now();
955 base::TimeDelta visit_age = base::TimeDelta::FromDays(3);
956 base::Time older_time = recent_time - visit_age;
958 // Visit the url with recent time.
959 backend_->AddPageVisit(url, recent_time, 0,
960 content::PageTransitionFromInt(
961 content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
962 history::SOURCE_BROWSED);
964 // Add to the url a visit with older time (could be syncing from another
966 backend_->AddPageVisit(url, older_time, 0,
967 content::PageTransitionFromInt(
968 content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
969 history::SOURCE_SYNCED);
971 // Fetch the row information about url from history db.
974 URLID row_id = backend_->db_->GetRowForURL(url, &row);
975 backend_->db_->GetVisitsForURL(row_id, &visits);
977 // Last visit time should be the most recent time, not the most recently added
979 ASSERT_EQ(2U, visits.size());
980 ASSERT_EQ(recent_time, row.last_visit());
983 TEST_F(HistoryBackendTest, AddPageArgsSource) {
984 ASSERT_TRUE(backend_.get());
986 GURL url("http://testpageargs.com");
988 // Assume this page is browsed by user.
989 HistoryAddPageArgs request1(url, base::Time::Now(), NULL, 0, GURL(),
990 history::RedirectList(),
991 content::PAGE_TRANSITION_KEYWORD_GENERATED,
992 history::SOURCE_BROWSED, false);
993 backend_->AddPage(request1);
994 // Assume this page is synced.
995 HistoryAddPageArgs request2(url, base::Time::Now(), NULL, 0, GURL(),
996 history::RedirectList(),
997 content::PAGE_TRANSITION_LINK,
998 history::SOURCE_SYNCED, false);
999 backend_->AddPage(request2);
1000 // Assume this page is browsed again.
1001 HistoryAddPageArgs request3(url, base::Time::Now(), NULL, 0, GURL(),
1002 history::RedirectList(),
1003 content::PAGE_TRANSITION_TYPED,
1004 history::SOURCE_BROWSED, false);
1005 backend_->AddPage(request3);
1007 // Three visits should be added with proper sources.
1010 URLID id = backend_->db()->GetRowForURL(url, &row);
1011 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1012 ASSERT_EQ(3U, visits.size());
1013 VisitSourceMap visit_sources;
1014 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1015 ASSERT_EQ(1U, visit_sources.size());
1016 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
1019 TEST_F(HistoryBackendTest, AddVisitsSource) {
1020 ASSERT_TRUE(backend_.get());
1022 GURL url1("http://www.cnn.com");
1023 std::vector<VisitInfo> visits1, visits2;
1024 visits1.push_back(VisitInfo(
1025 Time::Now() - base::TimeDelta::FromDays(5),
1026 content::PAGE_TRANSITION_LINK));
1027 visits1.push_back(VisitInfo(
1028 Time::Now() - base::TimeDelta::FromDays(1),
1029 content::PAGE_TRANSITION_LINK));
1030 visits1.push_back(VisitInfo(
1031 Time::Now(), content::PAGE_TRANSITION_LINK));
1033 GURL url2("http://www.example.com");
1034 visits2.push_back(VisitInfo(
1035 Time::Now() - base::TimeDelta::FromDays(10),
1036 content::PAGE_TRANSITION_LINK));
1037 visits2.push_back(VisitInfo(Time::Now(), content::PAGE_TRANSITION_LINK));
1039 // Clear all history.
1040 backend_->DeleteAllHistory();
1043 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1044 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
1046 // Verify the visits were added with their sources.
1049 URLID id = backend_->db()->GetRowForURL(url1, &row);
1050 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1051 ASSERT_EQ(3U, visits.size());
1052 VisitSourceMap visit_sources;
1053 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1054 ASSERT_EQ(3U, visit_sources.size());
1055 for (int i = 0; i < 3; i++)
1056 EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
1057 id = backend_->db()->GetRowForURL(url2, &row);
1058 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1059 ASSERT_EQ(2U, visits.size());
1060 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1061 ASSERT_EQ(2U, visit_sources.size());
1062 for (int i = 0; i < 2; i++)
1063 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
1066 TEST_F(HistoryBackendTest, GetMostRecentVisits) {
1067 ASSERT_TRUE(backend_.get());
1069 GURL url1("http://www.cnn.com");
1070 std::vector<VisitInfo> visits1;
1071 visits1.push_back(VisitInfo(
1072 Time::Now() - base::TimeDelta::FromDays(5),
1073 content::PAGE_TRANSITION_LINK));
1074 visits1.push_back(VisitInfo(
1075 Time::Now() - base::TimeDelta::FromDays(1),
1076 content::PAGE_TRANSITION_LINK));
1077 visits1.push_back(VisitInfo(
1078 Time::Now(), content::PAGE_TRANSITION_LINK));
1080 // Clear all history.
1081 backend_->DeleteAllHistory();
1084 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1086 // Verify the visits were added with their sources.
1089 URLID id = backend_->db()->GetRowForURL(url1, &row);
1090 ASSERT_TRUE(backend_->db()->GetMostRecentVisitsForURL(id, 1, &visits));
1091 ASSERT_EQ(1U, visits.size());
1092 EXPECT_EQ(visits1[2].first, visits[0].visit_time);
1095 TEST_F(HistoryBackendTest, RemoveVisitsTransitions) {
1096 ASSERT_TRUE(backend_.get());
1098 // Clear all history.
1099 backend_->DeleteAllHistory();
1101 GURL url1("http://www.cnn.com");
1102 VisitInfo typed_visit(
1103 Time::Now() - base::TimeDelta::FromDays(6),
1104 content::PAGE_TRANSITION_TYPED);
1105 VisitInfo reload_visit(
1106 Time::Now() - base::TimeDelta::FromDays(5),
1107 content::PAGE_TRANSITION_RELOAD);
1108 VisitInfo link_visit(
1109 Time::Now() - base::TimeDelta::FromDays(4),
1110 content::PAGE_TRANSITION_LINK);
1111 std::vector<VisitInfo> visits_to_add;
1112 visits_to_add.push_back(typed_visit);
1113 visits_to_add.push_back(reload_visit);
1114 visits_to_add.push_back(link_visit);
1117 backend_->AddVisits(url1, visits_to_add, history::SOURCE_SYNCED);
1119 // Verify that the various counts are what we expect.
1122 URLID id = backend_->db()->GetRowForURL(url1, &row);
1123 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1124 ASSERT_EQ(3U, visits.size());
1125 ASSERT_EQ(1, row.typed_count());
1126 ASSERT_EQ(2, row.visit_count());
1128 // Now, delete the typed visit and verify that typed_count is updated.
1129 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1130 id = backend_->db()->GetRowForURL(url1, &row);
1131 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1132 ASSERT_EQ(2U, visits.size());
1133 ASSERT_EQ(0, row.typed_count());
1134 ASSERT_EQ(1, row.visit_count());
1136 // Delete the reload visit now and verify that none of the counts have
1138 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1139 id = backend_->db()->GetRowForURL(url1, &row);
1140 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1141 ASSERT_EQ(1U, visits.size());
1142 ASSERT_EQ(0, row.typed_count());
1143 ASSERT_EQ(1, row.visit_count());
1145 // Delete the last visit and verify that we delete the URL.
1146 ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
1147 ASSERT_EQ(0, backend_->db()->GetRowForURL(url1, &row));
1150 TEST_F(HistoryBackendTest, RemoveVisitsSource) {
1151 ASSERT_TRUE(backend_.get());
1153 GURL url1("http://www.cnn.com");
1154 std::vector<VisitInfo> visits1, visits2;
1155 visits1.push_back(VisitInfo(
1156 Time::Now() - base::TimeDelta::FromDays(5),
1157 content::PAGE_TRANSITION_LINK));
1158 visits1.push_back(VisitInfo(Time::Now(),
1159 content::PAGE_TRANSITION_LINK));
1161 GURL url2("http://www.example.com");
1162 visits2.push_back(VisitInfo(
1163 Time::Now() - base::TimeDelta::FromDays(10),
1164 content::PAGE_TRANSITION_LINK));
1165 visits2.push_back(VisitInfo(Time::Now(), content::PAGE_TRANSITION_LINK));
1167 // Clear all history.
1168 backend_->DeleteAllHistory();
1171 backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
1172 backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
1174 // Verify the visits of url1 were added.
1177 URLID id = backend_->db()->GetRowForURL(url1, &row);
1178 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1179 ASSERT_EQ(2U, visits.size());
1180 // Remove these visits.
1181 ASSERT_TRUE(backend_->RemoveVisits(visits));
1183 // Now check only url2's source in visit_source table.
1184 VisitSourceMap visit_sources;
1185 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1186 ASSERT_EQ(0U, visit_sources.size());
1187 id = backend_->db()->GetRowForURL(url2, &row);
1188 ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
1189 ASSERT_EQ(2U, visits.size());
1190 ASSERT_TRUE(backend_->GetVisitsSource(visits, &visit_sources));
1191 ASSERT_EQ(2U, visit_sources.size());
1192 for (int i = 0; i < 2; i++)
1193 EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
1196 // Test for migration of adding visit_source table.
1197 TEST_F(HistoryBackendTest, MigrationVisitSource) {
1198 ASSERT_TRUE(backend_.get());
1199 backend_->Closing();
1202 base::FilePath old_history_path;
1203 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
1204 old_history_path = old_history_path.AppendASCII("History");
1205 old_history_path = old_history_path.AppendASCII("HistoryNoSource");
1207 // Copy history database file to current directory so that it will be deleted
1209 base::FilePath new_history_path(getTestDir());
1210 base::DeleteFile(new_history_path, true);
1211 file_util::CreateDirectory(new_history_path);
1212 base::FilePath new_history_file =
1213 new_history_path.Append(chrome::kHistoryFilename);
1214 ASSERT_TRUE(base::CopyFile(old_history_path, new_history_file));
1216 backend_ = new HistoryBackend(new_history_path,
1218 new HistoryBackendTestDelegate(this),
1220 backend_->Init(std::string(), false);
1221 backend_->Closing();
1224 // Now the database should already be migrated.
1225 // Check version first.
1226 int cur_version = HistoryDatabase::GetCurrentVersion();
1228 ASSERT_TRUE(db.Open(new_history_file));
1229 sql::Statement s(db.GetUniqueStatement(
1230 "SELECT value FROM meta WHERE key = 'version'"));
1231 ASSERT_TRUE(s.Step());
1232 int file_version = s.ColumnInt(0);
1233 EXPECT_EQ(cur_version, file_version);
1235 // Check visit_source table is created and empty.
1236 s.Assign(db.GetUniqueStatement(
1237 "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
1238 ASSERT_TRUE(s.Step());
1239 s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
1240 EXPECT_FALSE(s.Step());
1243 // Test that SetFaviconMappingsForPageAndRedirects correctly updates icon
1244 // mappings based on redirects, icon URLs and icon types.
1245 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirects) {
1246 // Init recent_redirects_
1247 const GURL url1("http://www.google.com");
1248 const GURL url2("http://www.google.com/m");
1249 URLRow url_info1(url1);
1250 url_info1.set_visit_count(0);
1251 url_info1.set_typed_count(0);
1252 url_info1.set_last_visit(base::Time());
1253 url_info1.set_hidden(false);
1254 backend_->db_->AddURL(url_info1);
1256 URLRow url_info2(url2);
1257 url_info2.set_visit_count(0);
1258 url_info2.set_typed_count(0);
1259 url_info2.set_last_visit(base::Time());
1260 url_info2.set_hidden(false);
1261 backend_->db_->AddURL(url_info2);
1263 history::RedirectList redirects;
1264 redirects.push_back(url2);
1265 redirects.push_back(url1);
1266 backend_->recent_redirects_.Put(url1, redirects);
1268 const GURL icon_url1("http://www.google.com/icon");
1269 const GURL icon_url2("http://www.google.com/icon2");
1271 // Generate bitmap data for a page with two favicons.
1272 std::vector<chrome::FaviconBitmapData> two_favicon_bitmap_data;
1273 GenerateFaviconBitmapData(icon_url1, GetSizesSmallAndLarge(),
1274 icon_url2, GetSizesSmallAndLarge(), &two_favicon_bitmap_data);
1276 // Generate bitmap data for a page with a single favicon.
1277 std::vector<chrome::FaviconBitmapData> one_favicon_bitmap_data;
1278 GenerateFaviconBitmapData(icon_url1, GetSizesSmallAndLarge(),
1279 &one_favicon_bitmap_data);
1282 backend_->SetFavicons(url1, chrome::FAVICON, two_favicon_bitmap_data);
1283 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
1284 EXPECT_EQ(2u, NumIconMappingsForPageURL(url2, chrome::FAVICON));
1286 // Add one touch_icon
1287 backend_->SetFavicons(url1, chrome::TOUCH_ICON, one_favicon_bitmap_data);
1288 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
1289 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, chrome::TOUCH_ICON));
1290 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
1292 // Add one TOUCH_PRECOMPOSED_ICON
1293 backend_->SetFavicons(
1294 url1, chrome::TOUCH_PRECOMPOSED_ICON, one_favicon_bitmap_data);
1295 // The touch_icon was replaced.
1296 EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
1297 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
1299 NumIconMappingsForPageURL(url1, chrome::TOUCH_PRECOMPOSED_ICON));
1301 NumIconMappingsForPageURL(url2, chrome::TOUCH_PRECOMPOSED_ICON));
1303 // Add a touch_icon.
1304 backend_->SetFavicons(url1, chrome::TOUCH_ICON, one_favicon_bitmap_data);
1305 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
1306 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
1307 // The TOUCH_PRECOMPOSED_ICON was replaced.
1309 NumIconMappingsForPageURL(url1, chrome::TOUCH_PRECOMPOSED_ICON));
1311 // Add a single favicon.
1312 backend_->SetFavicons(url1, chrome::FAVICON, one_favicon_bitmap_data);
1313 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
1314 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
1315 EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, chrome::FAVICON));
1317 // Add two favicons.
1318 backend_->SetFavicons(url1, chrome::FAVICON, two_favicon_bitmap_data);
1319 EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
1320 EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
1323 // Test that there is no churn in icon mappings from calling
1324 // SetFavicons() twice with the same |favicon_bitmap_data| parameter.
1325 TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) {
1326 const GURL url("http://www.google.com/");
1327 const GURL icon_url("http://www.google.com/icon");
1329 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1330 GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
1331 &favicon_bitmap_data);
1333 backend_->SetFavicons(url, chrome::FAVICON, favicon_bitmap_data);
1335 std::vector<IconMapping> icon_mappings;
1336 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1337 url, chrome::FAVICON, &icon_mappings));
1338 EXPECT_EQ(1u, icon_mappings.size());
1339 IconMappingID mapping_id = icon_mappings[0].mapping_id;
1341 backend_->SetFavicons(url, chrome::FAVICON, favicon_bitmap_data);
1343 icon_mappings.clear();
1344 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1345 url, chrome::FAVICON, &icon_mappings));
1346 EXPECT_EQ(1u, icon_mappings.size());
1348 // The same row in the icon_mapping table should be used for the mapping as
1350 EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id);
1353 // Test that calling SetFavicons() with FaviconBitmapData of different pixel
1354 // sizes than the initially passed in FaviconBitmapData deletes the no longer
1355 // used favicon bitmaps.
1356 TEST_F(HistoryBackendTest, SetFaviconsDeleteBitmaps) {
1357 const GURL page_url("http://www.google.com/");
1358 const GURL icon_url("http://www.google.com/icon");
1360 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1361 GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
1362 &favicon_bitmap_data);
1363 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1365 // Test initial state.
1366 std::vector<IconMapping> icon_mappings;
1367 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url, &icon_mappings));
1368 EXPECT_EQ(1u, icon_mappings.size());
1369 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1370 EXPECT_EQ(chrome::FAVICON, icon_mappings[0].icon_type);
1371 chrome::FaviconID favicon_id = icon_mappings[0].icon_id;
1373 std::vector<FaviconBitmap> favicon_bitmaps;
1374 EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps));
1375 EXPECT_EQ(2u, favicon_bitmaps.size());
1376 FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id;
1377 EXPECT_NE(0, small_bitmap_id);
1378 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmaps[0].bitmap_data));
1379 EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size);
1380 FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id;
1381 EXPECT_NE(0, large_bitmap_id);
1382 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
1383 EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size);
1385 // Call SetFavicons() with bitmap data for only the large bitmap. Check that
1386 // the small bitmap is in fact deleted.
1387 GenerateFaviconBitmapData(icon_url, GetSizesLarge(), &favicon_bitmap_data);
1388 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1390 scoped_refptr<base::RefCountedMemory> bitmap_data_out;
1391 gfx::Size pixel_size_out;
1392 EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(small_bitmap_id,
1393 NULL, &bitmap_data_out, &pixel_size_out));
1394 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id,
1395 NULL, &bitmap_data_out, &pixel_size_out));
1396 EXPECT_TRUE(BitmapDataEqual('a', bitmap_data_out));
1397 EXPECT_EQ(kLargeSize, pixel_size_out);
1399 icon_mappings.clear();
1400 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1402 EXPECT_EQ(1u, icon_mappings.size());
1403 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1405 // Call SetFavicons() with no bitmap data. Check that the bitmaps and icon
1406 // mappings are deleted.
1407 favicon_bitmap_data.clear();
1408 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1410 EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id, NULL,
1412 icon_mappings.clear();
1413 EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1416 // Notifications should have been broadcast for each call to SetFavicons().
1417 EXPECT_EQ(3, num_broadcasted_notifications());
1420 // Test updating a single favicon bitmap's data via SetFavicons.
1421 TEST_F(HistoryBackendTest, SetFaviconsReplaceBitmapData) {
1422 const GURL page_url("http://www.google.com/");
1423 const GURL icon_url("http://www.google.com/icon");
1425 std::vector<unsigned char> data_initial;
1426 data_initial.push_back('a');
1428 chrome::FaviconBitmapData bitmap_data_element;
1429 bitmap_data_element.bitmap_data =
1430 base::RefCountedBytes::TakeVector(&data_initial);
1431 bitmap_data_element.pixel_size = kSmallSize;
1432 bitmap_data_element.icon_url = icon_url;
1433 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1434 favicon_bitmap_data.push_back(bitmap_data_element);
1436 // Add bitmap to the database.
1437 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1439 chrome::FaviconID original_favicon_id =
1440 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1441 icon_url, chrome::FAVICON, NULL);
1442 EXPECT_NE(0, original_favicon_id);
1443 FaviconBitmap original_favicon_bitmap;
1445 GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap));
1446 EXPECT_TRUE(BitmapDataEqual('a', original_favicon_bitmap.bitmap_data));
1448 EXPECT_EQ(1, num_broadcasted_notifications());
1450 // Call SetFavicons() with completely identical data.
1451 std::vector<unsigned char> updated_data;
1452 updated_data.push_back('a');
1453 favicon_bitmap_data[0].bitmap_data = new base::RefCountedBytes(updated_data);
1454 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1456 chrome::FaviconID updated_favicon_id =
1457 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1458 icon_url, chrome::FAVICON, NULL);
1459 EXPECT_NE(0, updated_favicon_id);
1460 FaviconBitmap updated_favicon_bitmap;
1462 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
1463 EXPECT_TRUE(BitmapDataEqual('a', updated_favicon_bitmap.bitmap_data));
1465 // Because the bitmap data is byte equivalent, no notifications should have
1466 // been broadcasted.
1467 EXPECT_EQ(1, num_broadcasted_notifications());
1469 // Call SetFavicons() with identical data but a different bitmap.
1470 updated_data[0] = 'b';
1471 favicon_bitmap_data[0].bitmap_data = new base::RefCountedBytes(updated_data);
1472 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1474 updated_favicon_id =
1475 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1476 icon_url, chrome::FAVICON, NULL);
1477 EXPECT_NE(0, updated_favicon_id);
1479 GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
1480 EXPECT_TRUE(BitmapDataEqual('b', updated_favicon_bitmap.bitmap_data));
1482 // There should be no churn in FaviconIDs or FaviconBitmapIds even though
1483 // the bitmap data changed.
1484 EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id);
1485 EXPECT_EQ(original_favicon_bitmap.bitmap_id,
1486 updated_favicon_bitmap.bitmap_id);
1488 // A notification should have been broadcasted as the favicon bitmap data has
1490 EXPECT_EQ(2, num_broadcasted_notifications());
1493 // Test that if two pages share the same FaviconID, changing the favicon for
1494 // one page does not affect the other.
1495 TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
1496 GURL icon_url("http://www.google.com/favicon.ico");
1497 GURL icon_url_new("http://www.google.com/favicon2.ico");
1498 GURL page_url1("http://www.google.com");
1499 GURL page_url2("http://www.google.ca");
1501 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1502 GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
1503 &favicon_bitmap_data);
1505 backend_->SetFavicons(page_url1, chrome::FAVICON, favicon_bitmap_data);
1507 std::vector<GURL> icon_urls;
1508 icon_urls.push_back(icon_url);
1510 std::vector<chrome::FaviconBitmapResult> bitmap_results;
1511 backend_->UpdateFaviconMappingsAndFetch(
1512 page_url2, icon_urls, chrome::FAVICON, kSmallSize.width(),
1513 GetScaleFactors1x2x(), &bitmap_results);
1515 // Check that the same FaviconID is mapped to both page URLs.
1516 std::vector<IconMapping> icon_mappings;
1517 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1518 page_url1, &icon_mappings));
1519 EXPECT_EQ(1u, icon_mappings.size());
1520 chrome::FaviconID favicon_id = icon_mappings[0].icon_id;
1521 EXPECT_NE(0, favicon_id);
1523 icon_mappings.clear();
1524 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1525 page_url2, &icon_mappings));
1526 EXPECT_EQ(1u, icon_mappings.size());
1527 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1529 // Change the icon URL that |page_url1| is mapped to.
1530 GenerateFaviconBitmapData(icon_url_new, GetSizesSmall(),
1531 &favicon_bitmap_data);
1532 backend_->SetFavicons(page_url1, chrome::FAVICON, favicon_bitmap_data);
1534 // |page_url1| should map to a new FaviconID and have valid bitmap data.
1535 icon_mappings.clear();
1536 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1537 page_url1, &icon_mappings));
1538 EXPECT_EQ(1u, icon_mappings.size());
1539 EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
1540 EXPECT_NE(favicon_id, icon_mappings[0].icon_id);
1542 std::vector<FaviconBitmap> favicon_bitmaps;
1543 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
1544 icon_mappings[0].icon_id, &favicon_bitmaps));
1545 EXPECT_EQ(1u, favicon_bitmaps.size());
1547 // |page_url2| should still map to the same FaviconID and have valid bitmap
1549 icon_mappings.clear();
1550 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
1551 page_url2, &icon_mappings));
1552 EXPECT_EQ(1u, icon_mappings.size());
1553 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1555 favicon_bitmaps.clear();
1556 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id,
1558 EXPECT_EQ(2u, favicon_bitmaps.size());
1560 // A notification should have been broadcast for each call to SetFavicons()
1561 // and each call to UpdateFaviconMappingsAndFetch().
1562 EXPECT_EQ(3, num_broadcasted_notifications());
1565 // Test that no notifications are broadcast as a result of calling
1566 // UpdateFaviconMappingsAndFetch() for an icon URL which is already
1567 // mapped to the passed in page URL.
1568 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoChange) {
1569 GURL page_url("http://www.google.com");
1570 GURL icon_url("http://www.google.com/favicon.ico");
1571 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1572 GenerateFaviconBitmapData(icon_url, GetSizesSmall(), &favicon_bitmap_data);
1574 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1576 chrome::FaviconID icon_id =
1577 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1578 icon_url, chrome::FAVICON, NULL);
1579 EXPECT_NE(0, icon_id);
1580 EXPECT_EQ(1, num_broadcasted_notifications());
1582 std::vector<GURL> icon_urls;
1583 icon_urls.push_back(icon_url);
1585 std::vector<chrome::FaviconBitmapResult> bitmap_results;
1586 backend_->UpdateFaviconMappingsAndFetch(
1587 page_url, icon_urls, chrome::FAVICON, kSmallSize.width(),
1588 GetScaleFactors1x2x(), &bitmap_results);
1590 EXPECT_EQ(icon_id, backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1591 icon_url, chrome::FAVICON, NULL));
1593 // No notification should have been broadcast as no icon mapping, favicon,
1594 // or favicon bitmap was updated, added or removed.
1595 EXPECT_EQ(1, num_broadcasted_notifications());
1598 // Test repeatedly calling MergeFavicon(). |page_url| is initially not known
1600 TEST_F(HistoryBackendTest, MergeFaviconPageURLNotInDB) {
1601 GURL page_url("http://www.google.com");
1602 GURL icon_url("http:/www.google.com/favicon.ico");
1604 std::vector<unsigned char> data;
1605 data.push_back('a');
1606 scoped_refptr<base::RefCountedBytes> bitmap_data(
1607 new base::RefCountedBytes(data));
1609 backend_->MergeFavicon(
1610 page_url, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
1612 // |page_url| should now be mapped to |icon_url| and the favicon bitmap should
1614 std::vector<IconMapping> icon_mappings;
1615 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1617 EXPECT_EQ(1u, icon_mappings.size());
1618 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1620 FaviconBitmap favicon_bitmap;
1621 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1622 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1623 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1624 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1627 bitmap_data = new base::RefCountedBytes(data);
1628 backend_->MergeFavicon(
1629 page_url, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
1631 // |page_url| should still have a single favicon bitmap. The bitmap data
1632 // should be updated.
1633 icon_mappings.clear();
1634 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1636 EXPECT_EQ(1u, icon_mappings.size());
1637 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1639 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1640 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1641 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
1642 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1645 // Test calling MergeFavicon() when |page_url| is known to the database.
1646 TEST_F(HistoryBackendTest, MergeFaviconPageURLInDB) {
1647 GURL page_url("http://www.google.com");
1648 GURL icon_url1("http:/www.google.com/favicon.ico");
1649 GURL icon_url2("http://www.google.com/favicon2.ico");
1651 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1652 GenerateFaviconBitmapData(icon_url1, GetSizesSmall(),
1653 &favicon_bitmap_data);
1655 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1657 // Test initial state.
1658 std::vector<IconMapping> icon_mappings;
1659 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1661 EXPECT_EQ(1u, icon_mappings.size());
1662 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1664 FaviconBitmap favicon_bitmap;
1665 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1666 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1667 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1668 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1670 EXPECT_EQ(1, num_broadcasted_notifications());
1672 // 1) Merge identical favicon bitmap.
1673 std::vector<unsigned char> data;
1674 data.push_back('a');
1675 scoped_refptr<base::RefCountedBytes> bitmap_data(
1676 new base::RefCountedBytes(data));
1677 backend_->MergeFavicon(
1678 page_url, icon_url1, chrome::FAVICON, bitmap_data, kSmallSize);
1680 // All the data should stay the same and no notifications should have been
1682 icon_mappings.clear();
1683 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1685 EXPECT_EQ(1u, icon_mappings.size());
1686 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1688 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1689 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1690 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1691 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1693 EXPECT_EQ(1, num_broadcasted_notifications());
1695 // 2) Merge favicon bitmap of the same size.
1697 bitmap_data = new base::RefCountedBytes(data);
1698 backend_->MergeFavicon(
1699 page_url, icon_url1, chrome::FAVICON, bitmap_data, kSmallSize);
1701 // The small favicon bitmap at |icon_url1| should be overwritten.
1702 icon_mappings.clear();
1703 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1705 EXPECT_EQ(1u, icon_mappings.size());
1706 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1708 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1709 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1710 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
1711 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1713 // 3) Merge favicon for the same icon URL, but a pixel size for which there is
1714 // no favicon bitmap.
1716 bitmap_data = new base::RefCountedBytes(data);
1717 backend_->MergeFavicon(
1718 page_url, icon_url1, chrome::FAVICON, bitmap_data, kTinySize);
1720 // A new favicon bitmap should be created and the preexisting favicon bitmap
1721 // ('b') should be expired.
1722 icon_mappings.clear();
1723 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1725 EXPECT_EQ(1u, icon_mappings.size());
1726 EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
1728 std::vector<FaviconBitmap> favicon_bitmaps;
1729 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
1731 EXPECT_NE(base::Time(), favicon_bitmaps[0].last_updated);
1732 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
1733 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
1734 EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
1735 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
1736 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
1738 // 4) Merge favicon for an icon URL different from the icon URLs already
1739 // mapped to page URL.
1741 bitmap_data = new base::RefCountedBytes(data);
1742 backend_->MergeFavicon(
1743 page_url, icon_url2, chrome::FAVICON, bitmap_data, kSmallSize);
1745 // The existing favicon bitmaps should be copied over to the newly created
1746 // favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
1747 icon_mappings.clear();
1748 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1750 EXPECT_EQ(1u, icon_mappings.size());
1751 EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);
1753 favicon_bitmaps.clear();
1754 EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
1756 EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
1757 EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
1758 EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
1759 // The favicon being merged should take precedence over the preexisting
1761 EXPECT_NE(base::Time(), favicon_bitmaps[1].last_updated);
1762 EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data));
1763 EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
1765 // A notification should have been broadcast for each call to SetFavicons()
1766 // and MergeFavicon().
1767 EXPECT_EQ(4, num_broadcasted_notifications());
1770 // Test calling MergeFavicon() when |icon_url| is known to the database but not
1771 // mapped to |page_url|.
1772 TEST_F(HistoryBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) {
1773 GURL page_url1("http://www.google.com");
1774 GURL page_url2("http://news.google.com");
1775 GURL page_url3("http://maps.google.com");
1776 GURL icon_url("http:/www.google.com/favicon.ico");
1778 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1779 GenerateFaviconBitmapData(icon_url, GetSizesSmall(),
1780 &favicon_bitmap_data);
1782 backend_->SetFavicons(page_url1, chrome::FAVICON, favicon_bitmap_data);
1784 // Test initial state.
1785 std::vector<IconMapping> icon_mappings;
1786 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
1788 EXPECT_EQ(1u, icon_mappings.size());
1789 EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
1791 FaviconBitmap favicon_bitmap;
1792 EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
1793 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1794 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1795 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1797 // 1) Merge in an identical favicon bitmap data but for a different page URL.
1798 std::vector<unsigned char> data;
1799 data.push_back('a');
1800 scoped_refptr<base::RefCountedBytes> bitmap_data(
1801 new base::RefCountedBytes(data));
1803 backend_->MergeFavicon(
1804 page_url2, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
1806 chrome::FaviconID favicon_id =
1807 backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1808 icon_url, chrome::FAVICON, NULL);
1809 EXPECT_NE(0, favicon_id);
1811 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
1812 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1813 EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
1814 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1816 // 2) Merging a favicon bitmap with different bitmap data for the same icon
1817 // URL should overwrite the small favicon bitmap at |icon_url|.
1818 bitmap_data->data()[0] = 'b';
1819 backend_->MergeFavicon(
1820 page_url3, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
1822 favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
1823 icon_url, chrome::FAVICON, NULL);
1824 EXPECT_NE(0, favicon_id);
1826 EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
1827 EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
1828 EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
1829 EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
1831 // |icon_url| should be mapped to all three page URLs.
1832 icon_mappings.clear();
1833 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
1835 EXPECT_EQ(1u, icon_mappings.size());
1836 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1838 icon_mappings.clear();
1839 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url2,
1841 EXPECT_EQ(1u, icon_mappings.size());
1842 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1844 icon_mappings.clear();
1845 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url3,
1847 EXPECT_EQ(1u, icon_mappings.size());
1848 EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
1850 // A notification should have been broadcast for each call to SetFavicons()
1851 // and MergeFavicon().
1852 EXPECT_EQ(3, num_broadcasted_notifications());
1855 // Test that MergeFavicon() does not add more than
1856 // |kMaxFaviconBitmapsPerIconURL| to a favicon.
1857 TEST_F(HistoryBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) {
1858 GURL page_url("http://www.google.com");
1859 std::string icon_url_string("http://www.google.com/favicon.ico");
1860 size_t replace_index = icon_url_string.size() - 1;
1862 std::vector<unsigned char> data;
1863 data.push_back('a');
1864 scoped_refptr<base::RefCountedMemory> bitmap_data =
1865 base::RefCountedBytes::TakeVector(&data);
1868 for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) {
1869 icon_url_string[replace_index] = '0' + i;
1870 GURL icon_url(icon_url_string);
1872 backend_->MergeFavicon(page_url, icon_url, chrome::FAVICON, bitmap_data,
1873 gfx::Size(pixel_size, pixel_size));
1877 // There should be a single favicon mapped to |page_url| with exactly
1878 // kMaxFaviconBitmapsPerIconURL favicon bitmaps.
1879 std::vector<IconMapping> icon_mappings;
1880 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
1882 EXPECT_EQ(1u, icon_mappings.size());
1883 std::vector<FaviconBitmap> favicon_bitmaps;
1884 EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
1885 icon_mappings[0].icon_id, &favicon_bitmaps));
1886 EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
1889 // Tests that the favicon set by MergeFavicon() shows up in the result of
1890 // GetFaviconsForURL().
1891 TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) {
1892 GURL page_url("http://www.google.com");
1893 GURL icon_url("http://www.google.com/favicon.ico");
1894 GURL merged_icon_url("http://wwww.google.com/favicon2.ico");
1896 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1897 GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
1898 &favicon_bitmap_data);
1900 // Set some preexisting favicons for |page_url|.
1901 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1903 // Merge small favicon.
1904 std::vector<unsigned char> data;
1905 data.push_back('c');
1906 scoped_refptr<base::RefCountedBytes> bitmap_data(
1907 new base::RefCountedBytes(data));
1908 backend_->MergeFavicon(
1909 page_url, merged_icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
1911 // Request favicon bitmaps for both 1x and 2x to simulate request done by
1912 // BookmarkModel::GetFavicon().
1913 std::vector<chrome::FaviconBitmapResult> bitmap_results;
1914 backend_->GetFaviconsForURL(page_url, chrome::FAVICON, kSmallSize.width(),
1915 GetScaleFactors1x2x(), &bitmap_results);
1917 EXPECT_EQ(2u, bitmap_results.size());
1918 const chrome::FaviconBitmapResult& first_result = bitmap_results[0];
1919 const chrome::FaviconBitmapResult& result =
1920 (first_result.pixel_size == kSmallSize) ? first_result
1921 : bitmap_results[1];
1922 EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data));
1925 // Tests GetFaviconsForURL with icon_types priority,
1926 TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) {
1927 GURL page_url("http://www.google.com");
1928 GURL icon_url("http://www.google.com/favicon.ico");
1929 GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
1931 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1932 std::vector<gfx::Size> favicon_size;
1933 favicon_size.push_back(gfx::Size(16, 16));
1934 favicon_size.push_back(gfx::Size(32, 32));
1935 GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
1936 ASSERT_EQ(2u, favicon_bitmap_data.size());
1938 std::vector<chrome::FaviconBitmapData> touch_icon_bitmap_data;
1939 std::vector<gfx::Size> touch_icon_size;
1940 touch_icon_size.push_back(gfx::Size(64, 64));
1941 GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data);
1942 ASSERT_EQ(1u, touch_icon_bitmap_data.size());
1944 // Set some preexisting favicons for |page_url|.
1945 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1946 backend_->SetFavicons(page_url, chrome::TOUCH_ICON, touch_icon_bitmap_data);
1948 chrome::FaviconBitmapResult result;
1949 std::vector<int> icon_types;
1950 icon_types.push_back(chrome::FAVICON);
1951 icon_types.push_back(chrome::TOUCH_ICON);
1953 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
1955 // Verify the result icon is 32x32 favicon.
1956 EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
1957 EXPECT_EQ(chrome::FAVICON, result.icon_type);
1959 // Change Minimal size to 32x32 and verify the 64x64 touch icon returned.
1960 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
1961 EXPECT_EQ(gfx::Size(64, 64), result.pixel_size);
1962 EXPECT_EQ(chrome::TOUCH_ICON, result.icon_type);
1965 // Test the the first types of icon is returned if its size equal to the
1966 // second types icon.
1967 TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFavicon) {
1968 GURL page_url("http://www.google.com");
1969 GURL icon_url("http://www.google.com/favicon.ico");
1970 GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
1972 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
1973 std::vector<gfx::Size> favicon_size;
1974 favicon_size.push_back(gfx::Size(16, 16));
1975 favicon_size.push_back(gfx::Size(32, 32));
1976 GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
1977 ASSERT_EQ(2u, favicon_bitmap_data.size());
1979 std::vector<chrome::FaviconBitmapData> touch_icon_bitmap_data;
1980 std::vector<gfx::Size> touch_icon_size;
1981 touch_icon_size.push_back(gfx::Size(32, 32));
1982 GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data);
1983 ASSERT_EQ(1u, touch_icon_bitmap_data.size());
1985 // Set some preexisting favicons for |page_url|.
1986 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
1987 backend_->SetFavicons(page_url, chrome::TOUCH_ICON, touch_icon_bitmap_data);
1989 chrome::FaviconBitmapResult result;
1990 std::vector<int> icon_types;
1991 icon_types.push_back(chrome::FAVICON);
1992 icon_types.push_back(chrome::TOUCH_ICON);
1994 backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
1996 // Verify the result icon is 32x32 favicon.
1997 EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
1998 EXPECT_EQ(chrome::FAVICON, result.icon_type);
2000 // Change minimal size to 32x32 and verify the 32x32 favicon returned.
2001 chrome::FaviconBitmapResult result1;
2002 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1);
2003 EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size);
2004 EXPECT_EQ(chrome::FAVICON, result1.icon_type);
2007 // Test the favicon is returned if its size is smaller than minimal size,
2008 // because it is only one available.
2009 TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) {
2010 GURL page_url("http://www.google.com");
2011 GURL icon_url("http://www.google.com/favicon.ico");
2013 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
2014 std::vector<gfx::Size> favicon_size;
2015 favicon_size.push_back(gfx::Size(16, 16));
2016 GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
2017 ASSERT_EQ(1u, favicon_bitmap_data.size());
2019 // Set preexisting favicons for |page_url|.
2020 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
2022 chrome::FaviconBitmapResult result;
2023 std::vector<int> icon_types;
2024 icon_types.push_back(chrome::FAVICON);
2025 icon_types.push_back(chrome::TOUCH_ICON);
2027 backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
2029 // Verify 16x16 icon is returned, even it small than minimal_size.
2030 EXPECT_EQ(gfx::Size(16, 16), result.pixel_size);
2031 EXPECT_EQ(chrome::FAVICON, result.icon_type);
2034 // Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in.
2035 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) {
2036 GURL page_url1("http://www.google.com");
2037 GURL page_url2("http://news.google.com");
2038 GURL page_url3("http://mail.google.com");
2039 GURL icon_urla("http://www.google.com/favicon1.ico");
2040 GURL icon_urlb("http://www.google.com/favicon2.ico");
2041 GURL icon_urlc("http://www.google.com/favicon3.ico");
2043 // |page_url1| is mapped to |icon_urla| which if of type TOUCH_ICON.
2044 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
2045 GenerateFaviconBitmapData(icon_urla, GetSizesSmall(), &favicon_bitmap_data);
2046 backend_->SetFavicons(page_url1, chrome::TOUCH_ICON, favicon_bitmap_data);
2048 // |page_url2| is mapped to |icon_urlb| and |icon_urlc| which are of type
2049 // TOUCH_PRECOMPOSED_ICON.
2050 GenerateFaviconBitmapData(icon_urlb, GetSizesSmall(), icon_urlc,
2051 GetSizesSmall(), &favicon_bitmap_data);
2052 backend_->SetFavicons(
2053 page_url2, chrome::TOUCH_PRECOMPOSED_ICON, favicon_bitmap_data);
2055 std::vector<GURL> icon_urls;
2056 icon_urls.push_back(icon_urla);
2057 icon_urls.push_back(icon_urlb);
2058 icon_urls.push_back(icon_urlc);
2060 std::vector<chrome::FaviconBitmapResult> bitmap_results;
2061 backend_->UpdateFaviconMappingsAndFetch(
2064 (chrome::TOUCH_ICON | chrome::TOUCH_PRECOMPOSED_ICON),
2066 GetScaleFactors1x2x(),
2069 // |page_url1| and |page_url2| should still be mapped to the same icon URLs.
2070 std::vector<IconMapping> icon_mappings;
2071 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
2073 EXPECT_EQ(1u, icon_mappings.size());
2074 EXPECT_EQ(icon_urla, icon_mappings[0].icon_url);
2075 EXPECT_EQ(chrome::TOUCH_ICON, icon_mappings[0].icon_type);
2077 icon_mappings.clear();
2078 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url2, &icon_mappings));
2079 EXPECT_EQ(2u, icon_mappings.size());
2080 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
2081 EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
2082 EXPECT_EQ(icon_urlc, icon_mappings[1].icon_url);
2083 EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[1].icon_type);
2085 // |page_url3| should be mapped only to |icon_urlb| and |icon_urlc| as
2086 // TOUCH_PRECOMPOSED_ICON is the largest IconType.
2087 icon_mappings.clear();
2088 EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url3, &icon_mappings));
2089 EXPECT_EQ(2u, icon_mappings.size());
2090 EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
2091 EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
2092 EXPECT_EQ(icon_urlc, icon_mappings[1].icon_url);
2093 EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[1].icon_type);
2096 // Test the results of GetFaviconsFromDB() when there are no found
2098 TEST_F(HistoryBackendTest, GetFaviconsFromDBEmpty) {
2099 const GURL page_url("http://www.google.com/");
2101 std::vector<chrome::FaviconBitmapResult> bitmap_results;
2102 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
2103 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results));
2104 EXPECT_TRUE(bitmap_results.empty());
2107 // Test the results of GetFaviconsFromDB() when there are matching favicons
2108 // but there are no associated favicon bitmaps.
2109 TEST_F(HistoryBackendTest, GetFaviconsFromDBNoFaviconBitmaps) {
2110 const GURL page_url("http://www.google.com/");
2111 const GURL icon_url("http://www.google.com/icon1");
2113 chrome::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
2114 icon_url, chrome::FAVICON);
2115 EXPECT_NE(0, icon_id);
2116 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
2118 std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
2119 EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
2120 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2121 EXPECT_TRUE(bitmap_results_out.empty());
2124 // Test that GetFaviconsFromDB() returns results for the bitmaps which most
2125 // closely match the passed in desired size and scale factors.
2126 TEST_F(HistoryBackendTest, GetFaviconsFromDBSelectClosestMatch) {
2127 const GURL page_url("http://www.google.com/");
2128 const GURL icon_url("http://www.google.com/icon1");
2130 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
2131 GenerateFaviconBitmapData(icon_url, GetSizesTinySmallAndLarge(),
2132 &favicon_bitmap_data);
2134 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
2136 std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
2137 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2140 GetScaleFactors1x2x(),
2141 &bitmap_results_out));
2143 // The bitmap data for the small and large bitmaps should be returned as their
2144 // sizes match exactly.
2145 EXPECT_EQ(2u, bitmap_results_out.size());
2146 // No required order for results.
2147 if (bitmap_results_out[0].pixel_size == kLargeSize) {
2148 chrome::FaviconBitmapResult tmp_result = bitmap_results_out[0];
2149 bitmap_results_out[0] = bitmap_results_out[1];
2150 bitmap_results_out[1] = tmp_result;
2153 EXPECT_FALSE(bitmap_results_out[0].expired);
2154 EXPECT_TRUE(BitmapDataEqual('b', bitmap_results_out[0].bitmap_data));
2155 EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size);
2156 EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url);
2157 EXPECT_EQ(chrome::FAVICON, bitmap_results_out[0].icon_type);
2159 EXPECT_FALSE(bitmap_results_out[1].expired);
2160 EXPECT_TRUE(BitmapDataEqual('c', bitmap_results_out[1].bitmap_data));
2161 EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size);
2162 EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url);
2163 EXPECT_EQ(chrome::FAVICON, bitmap_results_out[1].icon_type);
2166 // Test that GetFaviconsFromDB() returns results from the icon URL whose
2167 // bitmaps most closely match the passed in desired size and scale factors.
2168 TEST_F(HistoryBackendTest, GetFaviconsFromDBSingleIconURL) {
2169 const GURL page_url("http://www.google.com/");
2171 const GURL icon_url1("http://www.google.com/icon1");
2172 const GURL icon_url2("http://www.google.com/icon2");
2174 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
2175 GenerateFaviconBitmapData(icon_url1, GetSizesSmall(), icon_url2,
2176 GetSizesLarge(), &favicon_bitmap_data);
2178 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
2180 std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
2181 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
2184 GetScaleFactors1x2x(),
2185 &bitmap_results_out));
2187 // The results should have results for the icon URL with the large bitmap as
2188 // downscaling is preferred to upscaling.
2189 EXPECT_EQ(1u, bitmap_results_out.size());
2190 EXPECT_EQ(kLargeSize, bitmap_results_out[0].pixel_size);
2191 EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
2194 // Test the results of GetFaviconsFromDB() when called with different
2196 TEST_F(HistoryBackendTest, GetFaviconsFromDBIconType) {
2197 const GURL page_url("http://www.google.com/");
2198 const GURL icon_url1("http://www.google.com/icon1.png");
2199 const GURL icon_url2("http://www.google.com/icon2.png");
2201 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
2202 GenerateFaviconBitmapData(icon_url1, GetSizesSmall(), &favicon_bitmap_data);
2203 backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
2205 GenerateFaviconBitmapData(icon_url2, GetSizesSmall(), &favicon_bitmap_data);
2206 backend_->SetFavicons(page_url, chrome::TOUCH_ICON, favicon_bitmap_data);
2208 std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
2209 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
2210 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2212 EXPECT_EQ(1u, bitmap_results_out.size());
2213 EXPECT_EQ(chrome::FAVICON, bitmap_results_out[0].icon_type);
2214 EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url);
2216 bitmap_results_out.clear();
2217 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, chrome::TOUCH_ICON,
2218 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2220 EXPECT_EQ(1u, bitmap_results_out.size());
2221 EXPECT_EQ(chrome::TOUCH_ICON, bitmap_results_out[0].icon_type);
2222 EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
2225 // Test that GetFaviconsFromDB() correctly sets the expired flag for bitmap
2227 TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) {
2228 const GURL page_url("http://www.google.com/");
2229 const GURL icon_url("http://www.google.com/icon.png");
2231 std::vector<unsigned char> data;
2232 data.push_back('a');
2233 scoped_refptr<base::RefCountedBytes> bitmap_data(
2234 base::RefCountedBytes::TakeVector(&data));
2235 base::Time last_updated = base::Time::FromTimeT(0);
2236 chrome::FaviconID icon_id =
2237 backend_->thumbnail_db_->AddFavicon(icon_url,
2242 EXPECT_NE(0, icon_id);
2243 EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
2245 std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
2246 EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
2247 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2249 EXPECT_EQ(1u, bitmap_results_out.size());
2250 EXPECT_TRUE(bitmap_results_out[0].expired);
2253 // Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
2254 // no valid thumbnail database.
2255 TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {
2256 // Make the thumbnail database invalid.
2257 backend_->thumbnail_db_.reset();
2259 std::vector<chrome::FaviconBitmapResult> bitmap_results;
2261 backend_->UpdateFaviconMappingsAndFetch(
2262 GURL(), std::vector<GURL>(), chrome::FAVICON, kSmallSize.width(),
2263 GetScaleFactors1x2x(), &bitmap_results);
2265 EXPECT_TRUE(bitmap_results.empty());
2268 TEST_F(HistoryBackendTest, CloneFaviconIsRestrictedToSameDomain) {
2269 const GURL url("http://www.google.com/");
2270 const GURL same_domain_url("http://www.google.com/subdir/index.html");
2271 const GURL foreign_domain_url("http://www.not-google.com/");
2272 const GURL icon_url("http://www.google.com/icon.png");
2275 std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
2276 GenerateFaviconBitmapData(icon_url, GetSizesSmall(), &favicon_bitmap_data);
2277 backend_->SetFavicons(url, chrome::FAVICON, favicon_bitmap_data);
2278 EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
2279 url, chrome::FAVICON, NULL));
2281 // Validate starting state.
2282 std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
2283 EXPECT_TRUE(backend_->GetFaviconsFromDB(url, chrome::FAVICON,
2284 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2285 EXPECT_FALSE(backend_->GetFaviconsFromDB(same_domain_url, chrome::FAVICON,
2286 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2287 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url, chrome::FAVICON,
2288 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2290 // Same-domain cloning should work.
2291 backend_->CloneFavicons(url, same_domain_url);
2292 EXPECT_TRUE(backend_->GetFaviconsFromDB(same_domain_url, chrome::FAVICON,
2293 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2295 // Foreign-domain cloning is forbidden.
2296 backend_->CloneFavicons(url, foreign_domain_url);
2297 EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url, chrome::FAVICON,
2298 kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
2301 TEST_F(HistoryBackendTest, QueryFilteredURLs) {
2302 const char* google = "http://www.google.com/";
2303 const char* yahoo = "http://www.yahoo.com/";
2304 const char* yahoo_sports = "http://sports.yahoo.com/";
2305 const char* yahoo_sports_with_article1 =
2306 "http://sports.yahoo.com/article1.htm";
2307 const char* yahoo_sports_with_article2 =
2308 "http://sports.yahoo.com/article2.htm";
2309 const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer";
2310 const char* apple = "http://www.apple.com/";
2312 // Clear all history.
2313 backend_->DeleteAllHistory();
2315 Time tested_time = Time::Now().LocalMidnight() +
2316 base::TimeDelta::FromHours(4);
2317 base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30);
2318 base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
2319 base::TimeDelta one_day = base::TimeDelta::FromDays(1);
2321 const content::PageTransition kTypedTransition =
2322 content::PAGE_TRANSITION_TYPED;
2323 const content::PageTransition kKeywordGeneratedTransition =
2324 content::PAGE_TRANSITION_KEYWORD_GENERATED;
2326 const char* redirect_sequence[2];
2327 redirect_sequence[1] = NULL;
2329 redirect_sequence[0] = google;
2330 AddRedirectChainWithTransitionAndTime(
2331 redirect_sequence, 0, kTypedTransition,
2332 tested_time - one_day - half_an_hour * 2);
2333 AddRedirectChainWithTransitionAndTime(
2334 redirect_sequence, 0,
2335 kTypedTransition, tested_time - one_day);
2336 AddRedirectChainWithTransitionAndTime(
2337 redirect_sequence, 0,
2338 kTypedTransition, tested_time - half_an_hour / 2);
2339 AddRedirectChainWithTransitionAndTime(
2340 redirect_sequence, 0,
2341 kTypedTransition, tested_time);
2343 // Add a visit with a transition that will make sure that no segment gets
2344 // created for this page (so the subsequent entries will have different URLIDs
2346 redirect_sequence[0] = apple;
2347 AddRedirectChainWithTransitionAndTime(
2348 redirect_sequence, 0, kKeywordGeneratedTransition,
2349 tested_time - one_day + one_hour * 6);
2351 redirect_sequence[0] = yahoo;
2352 AddRedirectChainWithTransitionAndTime(
2353 redirect_sequence, 0, kTypedTransition,
2354 tested_time - one_day + half_an_hour);
2355 AddRedirectChainWithTransitionAndTime(
2356 redirect_sequence, 0, kTypedTransition,
2357 tested_time - one_day + half_an_hour * 2);
2359 redirect_sequence[0] = yahoo_sports;
2360 AddRedirectChainWithTransitionAndTime(
2361 redirect_sequence, 0, kTypedTransition,
2362 tested_time - one_day - half_an_hour * 2);
2363 AddRedirectChainWithTransitionAndTime(
2364 redirect_sequence, 0, kTypedTransition,
2365 tested_time - one_day);
2366 int transition1, transition2;
2367 AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false,
2368 tested_time - one_day + half_an_hour,
2369 &transition1, &transition2);
2370 AddClientRedirect(GURL(yahoo_sports_with_article1),
2371 GURL(yahoo_sports_with_article2),
2373 tested_time - one_day + half_an_hour * 2,
2374 &transition1, &transition2);
2376 redirect_sequence[0] = yahoo_sports_soccer;
2377 AddRedirectChainWithTransitionAndTime(redirect_sequence, 0,
2379 tested_time - half_an_hour);
2382 scoped_refptr<QueryFilteredURLsRequest> request1 =
2383 new history::QueryFilteredURLsRequest(
2384 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2385 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2386 HistoryBackendCancelableRequest cancellable_request;
2387 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2391 // Time limit is |tested_time| +/- 45 min.
2392 base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45);
2393 filter.SetFilterTime(tested_time);
2394 filter.SetFilterWidth(three_quarters_of_an_hour);
2395 backend_->QueryFilteredURLs(request1, 100, filter, false);
2397 ASSERT_EQ(4U, get_filtered_list().size());
2398 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2399 EXPECT_EQ(std::string(yahoo_sports_soccer),
2400 get_filtered_list()[1].url.spec());
2401 EXPECT_EQ(std::string(yahoo), get_filtered_list()[2].url.spec());
2402 EXPECT_EQ(std::string(yahoo_sports),
2403 get_filtered_list()[3].url.spec());
2405 // Time limit is between |tested_time| and |tested_time| + 2 hours.
2406 scoped_refptr<QueryFilteredURLsRequest> request2 =
2407 new history::QueryFilteredURLsRequest(
2408 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2409 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2410 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2412 filter.SetFilterTime(tested_time + one_hour);
2413 filter.SetFilterWidth(one_hour);
2414 backend_->QueryFilteredURLs(request2, 100, filter, false);
2416 ASSERT_EQ(3U, get_filtered_list().size());
2417 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2418 EXPECT_EQ(std::string(yahoo), get_filtered_list()[1].url.spec());
2419 EXPECT_EQ(std::string(yahoo_sports), get_filtered_list()[2].url.spec());
2421 // Time limit is between |tested_time| - 2 hours and |tested_time|.
2422 scoped_refptr<QueryFilteredURLsRequest> request3 =
2423 new history::QueryFilteredURLsRequest(
2424 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2425 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2426 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2428 filter.SetFilterTime(tested_time - one_hour);
2429 filter.SetFilterWidth(one_hour);
2430 backend_->QueryFilteredURLs(request3, 100, filter, false);
2432 ASSERT_EQ(3U, get_filtered_list().size());
2433 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2434 EXPECT_EQ(std::string(yahoo_sports_soccer),
2435 get_filtered_list()[1].url.spec());
2436 EXPECT_EQ(std::string(yahoo_sports), get_filtered_list()[2].url.spec());
2438 filter.ClearFilters();
2439 base::Time::Exploded exploded_time;
2440 tested_time.LocalExplode(&exploded_time);
2443 scoped_refptr<QueryFilteredURLsRequest> request4 =
2444 new history::QueryFilteredURLsRequest(
2445 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2446 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2447 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2449 filter.SetFilterTime(tested_time);
2450 filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week));
2451 backend_->QueryFilteredURLs(request4, 100, filter, false);
2453 ASSERT_EQ(2U, get_filtered_list().size());
2454 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2455 EXPECT_EQ(std::string(yahoo_sports_soccer),
2456 get_filtered_list()[1].url.spec());
2458 // Today + time limit - only yahoo_sports_soccer should fit.
2459 scoped_refptr<QueryFilteredURLsRequest> request5 =
2460 new history::QueryFilteredURLsRequest(
2461 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2462 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2463 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2465 filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40));
2466 filter.SetFilterWidth(base::TimeDelta::FromMinutes(20));
2467 backend_->QueryFilteredURLs(request5, 100, filter, false);
2469 ASSERT_EQ(1U, get_filtered_list().size());
2470 EXPECT_EQ(std::string(yahoo_sports_soccer),
2471 get_filtered_list()[0].url.spec());
2473 // Make sure we get debug data if we request it.
2474 scoped_refptr<QueryFilteredURLsRequest> request6 =
2475 new history::QueryFilteredURLsRequest(
2476 base::Bind(&HistoryBackendTest::OnQueryFiltered,
2477 base::Unretained(static_cast<HistoryBackendTest*>(this))));
2478 cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
2480 filter.SetFilterTime(tested_time);
2481 filter.SetFilterWidth(one_hour * 2);
2482 backend_->QueryFilteredURLs(request6, 100, filter, true);
2484 // If the SegmentID is used by QueryFilteredURLs when generating the debug
2485 // data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer|
2486 // entry will be zero instead of 1.
2487 ASSERT_GE(get_filtered_list().size(), 2U);
2488 EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
2489 EXPECT_EQ(std::string(yahoo_sports_soccer),
2490 get_filtered_list()[1].url.spec());
2491 EXPECT_EQ(4U, get_filtered_list()[0].extended_info.total_visits);
2492 EXPECT_EQ(1U, get_filtered_list()[1].extended_info.total_visits);
2495 TEST_F(HistoryBackendTest, UpdateVisitDuration) {
2496 // This unit test will test adding and deleting visit details information.
2497 ASSERT_TRUE(backend_.get());
2499 GURL url1("http://www.cnn.com");
2500 std::vector<VisitInfo> visit_info1, visit_info2;
2501 Time start_ts = Time::Now() - base::TimeDelta::FromDays(5);
2502 Time end_ts = start_ts + base::TimeDelta::FromDays(2);
2503 visit_info1.push_back(VisitInfo(start_ts, content::PAGE_TRANSITION_LINK));
2505 GURL url2("http://www.example.com");
2506 visit_info2.push_back(VisitInfo(Time::Now() - base::TimeDelta::FromDays(10),
2507 content::PAGE_TRANSITION_LINK));
2509 // Clear all history.
2510 backend_->DeleteAllHistory();
2513 backend_->AddVisits(url1, visit_info1, history::SOURCE_BROWSED);
2514 backend_->AddVisits(url2, visit_info2, history::SOURCE_BROWSED);
2516 // Verify the entries for both visits were added in visit_details.
2517 VisitVector visits1, visits2;
2519 URLID url_id1 = backend_->db()->GetRowForURL(url1, &row);
2520 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
2521 ASSERT_EQ(1U, visits1.size());
2522 EXPECT_EQ(0, visits1[0].visit_duration.ToInternalValue());
2524 URLID url_id2 = backend_->db()->GetRowForURL(url2, &row);
2525 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id2, &visits2));
2526 ASSERT_EQ(1U, visits2.size());
2527 EXPECT_EQ(0, visits2[0].visit_duration.ToInternalValue());
2529 // Update the visit to cnn.com.
2530 backend_->UpdateVisitDuration(visits1[0].visit_id, end_ts);
2532 // Check the duration for visiting cnn.com was correctly updated.
2533 ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
2534 ASSERT_EQ(1U, visits1.size());
2535 base::TimeDelta expected_duration = end_ts - start_ts;
2536 EXPECT_EQ(expected_duration.ToInternalValue(),
2537 visits1[0].visit_duration.ToInternalValue());
2539 // Remove the visit to cnn.com.
2540 ASSERT_TRUE(backend_->RemoveVisits(visits1));
2543 // Test for migration of adding visit_duration column.
2544 TEST_F(HistoryBackendTest, MigrationVisitDuration) {
2545 ASSERT_TRUE(backend_.get());
2546 backend_->Closing();
2549 base::FilePath old_history_path, old_history, old_archived;
2550 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
2551 old_history_path = old_history_path.AppendASCII("History");
2552 old_history = old_history_path.AppendASCII("HistoryNoDuration");
2553 old_archived = old_history_path.AppendASCII("ArchivedNoDuration");
2555 // Copy history database file to current directory so that it will be deleted
2557 base::FilePath new_history_path(getTestDir());
2558 base::DeleteFile(new_history_path, true);
2559 file_util::CreateDirectory(new_history_path);
2560 base::FilePath new_history_file =
2561 new_history_path.Append(chrome::kHistoryFilename);
2562 base::FilePath new_archived_file =
2563 new_history_path.Append(chrome::kArchivedHistoryFilename);
2564 ASSERT_TRUE(base::CopyFile(old_history, new_history_file));
2565 ASSERT_TRUE(base::CopyFile(old_archived, new_archived_file));
2567 backend_ = new HistoryBackend(new_history_path,
2569 new HistoryBackendTestDelegate(this),
2571 backend_->Init(std::string(), false);
2572 backend_->Closing();
2575 // Now both history and archived_history databases should already be migrated.
2577 // Check version in history database first.
2578 int cur_version = HistoryDatabase::GetCurrentVersion();
2580 ASSERT_TRUE(db.Open(new_history_file));
2581 sql::Statement s(db.GetUniqueStatement(
2582 "SELECT value FROM meta WHERE key = 'version'"));
2583 ASSERT_TRUE(s.Step());
2584 int file_version = s.ColumnInt(0);
2585 EXPECT_EQ(cur_version, file_version);
2587 // Check visit_duration column in visits table is created and set to 0.
2588 s.Assign(db.GetUniqueStatement(
2589 "SELECT visit_duration FROM visits LIMIT 1"));
2590 ASSERT_TRUE(s.Step());
2591 EXPECT_EQ(0, s.ColumnInt(0));
2593 // Repeat version and visit_duration checks in archived history database
2595 cur_version = ArchivedDatabase::GetCurrentVersion();
2596 sql::Connection archived_db;
2597 ASSERT_TRUE(archived_db.Open(new_archived_file));
2598 sql::Statement s1(archived_db.GetUniqueStatement(
2599 "SELECT value FROM meta WHERE key = 'version'"));
2600 ASSERT_TRUE(s1.Step());
2601 file_version = s1.ColumnInt(0);
2602 EXPECT_EQ(cur_version, file_version);
2604 // Check visit_duration column in visits table is created and set to 0.
2605 s1.Assign(archived_db.GetUniqueStatement(
2606 "SELECT visit_duration FROM visits LIMIT 1"));
2607 ASSERT_TRUE(s1.Step());
2608 EXPECT_EQ(0, s1.ColumnInt(0));
2611 TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) {
2612 ASSERT_TRUE(backend_.get());
2614 GURL url("http://www.google.com");
2615 string16 title(UTF8ToUTF16("Bookmark title"));
2616 backend_->AddPageNoVisitForBookmark(url, title);
2619 backend_->GetURL(url, &row);
2620 EXPECT_EQ(url, row.url());
2621 EXPECT_EQ(title, row.title());
2622 EXPECT_EQ(0, row.visit_count());
2624 backend_->DeleteURL(url);
2625 backend_->AddPageNoVisitForBookmark(url, string16());
2626 backend_->GetURL(url, &row);
2627 EXPECT_EQ(url, row.url());
2628 EXPECT_EQ(UTF8ToUTF16(url.spec()), row.title());
2629 EXPECT_EQ(0, row.visit_count());
2632 TEST_F(HistoryBackendTest, ExpireHistoryForTimes) {
2633 ASSERT_TRUE(backend_.get());
2635 HistoryAddPageArgs args[10];
2636 for (size_t i = 0; i < arraysize(args); ++i) {
2637 args[i].url = GURL("http://example" +
2638 std::string((i % 2 == 0 ? ".com" : ".net")));
2639 args[i].time = base::Time::FromInternalValue(i);
2640 backend_->AddPage(args[i]);
2642 EXPECT_EQ(base::Time(), backend_->GetFirstRecordedTimeForTest());
2645 for (size_t i = 0; i < arraysize(args); ++i) {
2646 EXPECT_TRUE(backend_->GetURL(args[i].url, &row));
2649 std::set<base::Time> times;
2650 times.insert(args[5].time);
2651 backend_->ExpireHistoryForTimes(times,
2652 base::Time::FromInternalValue(2),
2653 base::Time::FromInternalValue(8));
2655 EXPECT_EQ(base::Time::FromInternalValue(0),
2656 backend_->GetFirstRecordedTimeForTest());
2658 // Visits to http://example.com are untouched.
2659 VisitVector visit_vector;
2660 EXPECT_TRUE(backend_->GetVisitsForURL(
2661 backend_->db_->GetRowForURL(GURL("http://example.com"), NULL),
2663 ASSERT_EQ(5u, visit_vector.size());
2664 EXPECT_EQ(base::Time::FromInternalValue(0), visit_vector[0].visit_time);
2665 EXPECT_EQ(base::Time::FromInternalValue(2), visit_vector[1].visit_time);
2666 EXPECT_EQ(base::Time::FromInternalValue(4), visit_vector[2].visit_time);
2667 EXPECT_EQ(base::Time::FromInternalValue(6), visit_vector[3].visit_time);
2668 EXPECT_EQ(base::Time::FromInternalValue(8), visit_vector[4].visit_time);
2670 // Visits to http://example.net between [2,8] are removed.
2671 visit_vector.clear();
2672 EXPECT_TRUE(backend_->GetVisitsForURL(
2673 backend_->db_->GetRowForURL(GURL("http://example.net"), NULL),
2675 ASSERT_EQ(2u, visit_vector.size());
2676 EXPECT_EQ(base::Time::FromInternalValue(1), visit_vector[0].visit_time);
2677 EXPECT_EQ(base::Time::FromInternalValue(9), visit_vector[1].visit_time);
2679 EXPECT_EQ(base::Time::FromInternalValue(0),
2680 backend_->GetFirstRecordedTimeForTest());
2683 TEST_F(HistoryBackendTest, ExpireHistory) {
2684 ASSERT_TRUE(backend_.get());
2685 // Since history operations are dependent on the local timezone, make all
2686 // entries relative to a fixed, local reference time.
2687 base::Time reference_time = base::Time::UnixEpoch().LocalMidnight() +
2688 base::TimeDelta::FromHours(12);
2690 // Insert 4 entries into the database.
2691 HistoryAddPageArgs args[4];
2692 for (size_t i = 0; i < arraysize(args); ++i) {
2693 args[i].url = GURL("http://example" + base::IntToString(i) + ".com");
2694 args[i].time = reference_time + base::TimeDelta::FromDays(i);
2695 backend_->AddPage(args[i]);
2699 for (unsigned int i = 0; i < arraysize(args); ++i)
2700 ASSERT_TRUE(backend_->GetURL(args[i].url, &url_rows[i]));
2702 std::vector<ExpireHistoryArgs> expire_list;
2705 // Passing an empty map should be a no-op.
2706 backend_->ExpireHistory(expire_list);
2707 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2708 EXPECT_EQ(4U, visits.size());
2710 // Trying to delete an unknown URL with the time of the first visit should
2712 expire_list.resize(expire_list.size() + 1);
2713 expire_list[0].SetTimeRangeForOneDay(args[0].time);
2714 expire_list[0].urls.insert(GURL("http://google.does-not-exist"));
2715 backend_->ExpireHistory(expire_list);
2716 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2717 EXPECT_EQ(4U, visits.size());
2719 // Now add the first URL with the same time -- it should get deleted.
2720 expire_list.back().urls.insert(url_rows[0].url());
2721 backend_->ExpireHistory(expire_list);
2723 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2724 ASSERT_EQ(3U, visits.size());
2725 EXPECT_EQ(visits[0].url_id, url_rows[1].id());
2726 EXPECT_EQ(visits[1].url_id, url_rows[2].id());
2727 EXPECT_EQ(visits[2].url_id, url_rows[3].id());
2729 // The first recorded time should also get updated.
2730 EXPECT_EQ(backend_->GetFirstRecordedTimeForTest(), args[1].time);
2732 // Now delete the rest of the visits in one call.
2733 for (unsigned int i = 1; i < arraysize(args); ++i) {
2734 expire_list.resize(expire_list.size() + 1);
2735 expire_list[i].SetTimeRangeForOneDay(args[i].time);
2736 expire_list[i].urls.insert(args[i].url);
2738 backend_->ExpireHistory(expire_list);
2740 backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
2741 ASSERT_EQ(0U, visits.size());
2744 class HistoryBackendSegmentDurationTest : public HistoryBackendTest {
2746 HistoryBackendSegmentDurationTest() {}
2748 virtual void SetUp() {
2749 CommandLine::ForCurrentProcess()->AppendSwitch(
2750 switches::kTrackActiveVisitTime);
2751 HistoryBackendTest::SetUp();
2755 DISALLOW_COPY_AND_ASSIGN(HistoryBackendSegmentDurationTest);
2758 // Assertions around segment durations.
2759 TEST_F(HistoryBackendSegmentDurationTest, SegmentDuration) {
2760 const GURL url1("http://www.google.com");
2761 const GURL url2("http://www.foo.com/m");
2762 const std::string segment1(VisitSegmentDatabase::ComputeSegmentName(url1));
2763 const std::string segment2(VisitSegmentDatabase::ComputeSegmentName(url2));
2765 Time segment_time(VisitSegmentDatabase::SegmentTime(Time::Now()));
2766 URLRow url_info1(url1);
2767 url_info1.set_visit_count(0);
2768 url_info1.set_typed_count(0);
2769 url_info1.set_last_visit(segment_time);
2770 url_info1.set_hidden(false);
2771 const URLID url1_id = backend_->db()->AddURL(url_info1);
2772 EXPECT_NE(0, url1_id);
2774 URLRow url_info2(url2);
2775 url_info2.set_visit_count(0);
2776 url_info2.set_typed_count(0);
2777 url_info2.set_last_visit(Time());
2778 url_info2.set_hidden(false);
2779 const URLID url2_id = backend_->db()->AddURL(url_info2);
2780 EXPECT_NE(0, url2_id);
2781 EXPECT_NE(url1_id, url2_id);
2783 // Should not have any segments for the urls.
2784 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment1));
2785 EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment2));
2787 // Update the duration, which should implicitly create the segments.
2788 const TimeDelta segment1_time_delta(TimeDelta::FromHours(1));
2789 const TimeDelta segment2_time_delta(TimeDelta::FromHours(2));
2790 backend_->IncreaseSegmentDuration(url1, segment_time, segment1_time_delta);
2791 backend_->IncreaseSegmentDuration(url2, segment_time, segment2_time_delta);
2793 // Get the ids of the segments that were created.
2794 const SegmentID segment1_id = backend_->db()->GetSegmentNamed(segment1);
2795 EXPECT_NE(0, segment1_id);
2796 const SegmentID segment2_id = backend_->db()->GetSegmentNamed(segment2);
2797 EXPECT_NE(0, segment2_id);
2798 EXPECT_NE(segment1_id, segment2_id);
2800 // Make sure the values made it to the db.
2801 SegmentDurationID segment1_duration_id;
2802 TimeDelta fetched_delta;
2803 EXPECT_TRUE(backend_->db()->GetSegmentDuration(
2804 segment1_id, segment_time, &segment1_duration_id,
2806 EXPECT_NE(0, segment1_duration_id);
2807 EXPECT_EQ(segment1_time_delta.InHours(), fetched_delta.InHours());
2809 SegmentDurationID segment2_duration_id;
2810 EXPECT_TRUE(backend_->db()->GetSegmentDuration(
2811 segment2_id, segment_time, &segment2_duration_id,
2813 EXPECT_NE(0, segment2_duration_id);
2814 EXPECT_NE(segment1_duration_id, segment2_duration_id);
2815 EXPECT_EQ(segment2_time_delta.InHours(), fetched_delta.InHours());
2817 // Query by duration. |url2| should be first as it has a longer view time.
2818 ScopedVector<PageUsageData> data;
2819 backend_->db()->QuerySegmentDuration(segment_time, 10, &data.get());
2820 ASSERT_EQ(2u, data.size());
2821 EXPECT_EQ(url2.spec(), data[0]->GetURL().spec());
2822 EXPECT_EQ(url2_id, data[0]->GetID());
2823 EXPECT_EQ(segment2_time_delta.InHours(), data[0]->duration().InHours());
2825 EXPECT_EQ(url1.spec(), data[1]->GetURL().spec());
2826 EXPECT_EQ(url1_id, data[1]->GetID());
2827 EXPECT_EQ(segment1_time_delta.InHours(), data[1]->duration().InHours());
2830 // Simple test that removes a bookmark. This test exercises the code paths in
2831 // History that block till bookmark bar model is loaded.
2832 TEST_F(HistoryBackendTest, RemoveNotification) {
2833 scoped_ptr<TestingProfile> profile(new TestingProfile());
2835 ASSERT_TRUE(profile->CreateHistoryService(false, false));
2836 profile->CreateBookmarkModel(true);
2837 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile.get());
2838 test::WaitForBookmarkModelToLoad(model);
2841 GURL url("http://www.google.com");
2842 bookmark_utils::AddIfNotBookmarked(model, url, base::string16());
2844 HistoryService* service = HistoryServiceFactory::GetForProfile(
2845 profile.get(), Profile::EXPLICIT_ACCESS);
2848 url, base::Time::Now(), NULL, 1, GURL(), RedirectList(),
2849 content::PAGE_TRANSITION_TYPED, SOURCE_BROWSED, false);
2851 // This won't actually delete the URL, rather it'll empty out the visits.
2852 // This triggers blocking on the BookmarkModel.
2853 service->DeleteURL(url);
2856 // Test DeleteFTSIndexDatabases deletes expected files.
2857 TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {
2858 ASSERT_TRUE(backend_.get());
2860 base::FilePath history_path(getTestDir());
2861 base::FilePath db1(history_path.AppendASCII("History Index 2013-05"));
2862 base::FilePath db1_journal(db1.InsertBeforeExtensionASCII("-journal"));
2863 base::FilePath db1_wal(db1.InsertBeforeExtensionASCII("-wal"));
2864 base::FilePath db2_symlink(history_path.AppendASCII("History Index 2013-06"));
2865 base::FilePath db2_actual(history_path.AppendASCII("Underlying DB"));
2867 // Setup dummy index database files.
2868 const char* data = "Dummy";
2869 const size_t data_len = 5;
2870 ASSERT_TRUE(file_util::WriteFile(db1, data, data_len));
2871 ASSERT_TRUE(file_util::WriteFile(db1_journal, data, data_len));
2872 ASSERT_TRUE(file_util::WriteFile(db1_wal, data, data_len));
2873 ASSERT_TRUE(file_util::WriteFile(db2_actual, data, data_len));
2874 #if defined(OS_POSIX)
2875 EXPECT_TRUE(file_util::CreateSymbolicLink(db2_actual, db2_symlink));
2878 // Delete all DTS index databases.
2879 backend_->DeleteFTSIndexDatabases();
2880 EXPECT_FALSE(base::PathExists(db1));
2881 EXPECT_FALSE(base::PathExists(db1_wal));
2882 EXPECT_FALSE(base::PathExists(db1_journal));
2883 EXPECT_FALSE(base::PathExists(db2_symlink));
2884 EXPECT_TRUE(base::PathExists(db2_actual)); // Symlinks shouldn't be followed.
2887 } // namespace history