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