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