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