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