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