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