• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 <set>
6 #include <vector>
7 
8 #include "base/command_line.h"
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/path_service.h"
14 #include "base/string16.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/bookmarks/bookmark_model.h"
17 #include "chrome/browser/history/history_backend.h"
18 #include "chrome/browser/history/history_notifications.h"
19 #include "chrome/browser/history/in_memory_database.h"
20 #include "chrome/browser/history/in_memory_history_backend.h"
21 #include "chrome/common/chrome_constants.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/thumbnail_score.h"
24 #include "chrome/tools/profiles/thumbnail-inl.h"
25 #include "content/common/notification_details.h"
26 #include "content/common/notification_source.h"
27 #include "googleurl/src/gurl.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "ui/gfx/codec/jpeg_codec.h"
30 
31 using base::Time;
32 
33 // This file only tests functionality where it is most convenient to call the
34 // backend directly. Most of the history backend functions are tested by the
35 // history unit test. Because of the elaborate callbacks involved, this is no
36 // harder than calling it directly for many things.
37 
38 namespace {
39 
40 // data we'll put into the thumbnail database
41 static const unsigned char blob1[] =
42     "12346102356120394751634516591348710478123649165419234519234512349134";
43 
44 }  // namepace
45 
46 namespace history {
47 
48 class HistoryBackendTest;
49 
50 // This must be a separate object since HistoryBackend manages its lifetime.
51 // This just forwards the messages we're interested in to the test object.
52 class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
53  public:
HistoryBackendTestDelegate(HistoryBackendTest * test)54   explicit HistoryBackendTestDelegate(HistoryBackendTest* test) : test_(test) {}
55 
NotifyProfileError(sql::InitStatus init_status)56   virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
57   virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE;
58   virtual void BroadcastNotifications(NotificationType type,
59                                       HistoryDetails* details) OVERRIDE;
60   virtual void DBLoaded() OVERRIDE;
61   virtual void StartTopSitesMigration() OVERRIDE;
62 
63  private:
64   // Not owned by us.
65   HistoryBackendTest* test_;
66 
67   DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
68 };
69 
70 class HistoryBackendTest : public testing::Test {
71  public:
HistoryBackendTest()72   HistoryBackendTest() : bookmark_model_(NULL), loaded_(false) {}
~HistoryBackendTest()73   virtual ~HistoryBackendTest() {
74   }
75 
76  protected:
77   scoped_refptr<HistoryBackend> backend_;  // Will be NULL on init failure.
78   scoped_ptr<InMemoryHistoryBackend> mem_backend_;
79 
AddRedirectChain(const char * sequence[],int page_id)80   void AddRedirectChain(const char* sequence[], int page_id) {
81     history::RedirectList redirects;
82     for (int i = 0; sequence[i] != NULL; ++i)
83       redirects.push_back(GURL(sequence[i]));
84 
85     int int_scope = 1;
86     void* scope = 0;
87     memcpy(&scope, &int_scope, sizeof(int_scope));
88     scoped_refptr<history::HistoryAddPageArgs> request(
89         new history::HistoryAddPageArgs(
90             redirects.back(), Time::Now(), scope, page_id, GURL(),
91             redirects, PageTransition::LINK, history::SOURCE_BROWSED, true));
92     backend_->AddPage(request);
93   }
94 
95   // Adds CLIENT_REDIRECT page transition.
96   // |url1| is the source URL and |url2| is the destination.
97   // |did_replace| is true if the transition is non-user initiated and the
98   // navigation entry for |url2| has replaced that for |url1|. The possibly
99   // updated transition code of the visit records for |url1| and |url2| is
100   // returned by filling in |*transition1| and |*transition2|, respectively.
AddClientRedirect(const GURL & url1,const GURL & url2,bool did_replace,int * transition1,int * transition2)101   void  AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
102                           int* transition1, int* transition2) {
103     void* const dummy_scope = reinterpret_cast<void*>(0x87654321);
104     history::RedirectList redirects;
105     if (url1.is_valid())
106       redirects.push_back(url1);
107     if (url2.is_valid())
108       redirects.push_back(url2);
109     scoped_refptr<HistoryAddPageArgs> request(
110         new HistoryAddPageArgs(url2, base::Time(), dummy_scope, 0, url1,
111             redirects, PageTransition::CLIENT_REDIRECT,
112             history::SOURCE_BROWSED, did_replace));
113     backend_->AddPage(request);
114 
115     *transition1 = getTransition(url1);
116     *transition2 = getTransition(url2);
117   }
118 
getTransition(const GURL & url)119   int getTransition(const GURL& url) {
120     if (!url.is_valid())
121       return 0;
122     URLRow row;
123     URLID id = backend_->db()->GetRowForURL(url, &row);
124     VisitVector visits;
125     EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
126     return visits[0].transition;
127   }
128 
getTestDir()129   FilePath getTestDir() {
130     return test_dir_;
131   }
132 
GetFavicon(const GURL & url,IconType icon_type)133   FaviconID GetFavicon(const GURL& url, IconType icon_type) {
134     IconMapping icon_mapping;
135     if (backend_->thumbnail_db_->GetIconMappingForPageURL(url, icon_type,
136                                                           &icon_mapping))
137       return icon_mapping.icon_id;
138     else
139       return 0;
140   }
141 
142   BookmarkModel bookmark_model_;
143 
144  protected:
145   bool loaded_;
146 
147  private:
148   friend class HistoryBackendTestDelegate;
149 
150   // testing::Test
SetUp()151   virtual void SetUp() {
152     if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
153                                            &test_dir_))
154       return;
155     backend_ = new HistoryBackend(test_dir_,
156                                   new HistoryBackendTestDelegate(this),
157                                   &bookmark_model_);
158     backend_->Init(std::string(), false);
159   }
TearDown()160   virtual void TearDown() {
161     if (backend_.get())
162       backend_->Closing();
163     backend_ = NULL;
164     mem_backend_.reset();
165     file_util::Delete(test_dir_, true);
166   }
167 
SetInMemoryBackend(InMemoryHistoryBackend * backend)168   void SetInMemoryBackend(InMemoryHistoryBackend* backend) {
169     mem_backend_.reset(backend);
170   }
171 
BroadcastNotifications(NotificationType type,HistoryDetails * details)172   void BroadcastNotifications(NotificationType type,
173                                       HistoryDetails* details) {
174     // Send the notifications directly to the in-memory database.
175     Details<HistoryDetails> det(details);
176     mem_backend_->Observe(type, Source<HistoryBackendTest>(NULL), det);
177 
178     // The backend passes ownership of the details pointer to us.
179     delete details;
180   }
181 
182   MessageLoop message_loop_;
183   FilePath test_dir_;
184 };
185 
SetInMemoryBackend(InMemoryHistoryBackend * backend)186 void HistoryBackendTestDelegate::SetInMemoryBackend(
187     InMemoryHistoryBackend* backend) {
188   test_->SetInMemoryBackend(backend);
189 }
190 
BroadcastNotifications(NotificationType type,HistoryDetails * details)191 void HistoryBackendTestDelegate::BroadcastNotifications(
192     NotificationType type,
193     HistoryDetails* details) {
194   test_->BroadcastNotifications(type, details);
195 }
196 
DBLoaded()197 void HistoryBackendTestDelegate::DBLoaded() {
198   test_->loaded_ = true;
199 }
200 
StartTopSitesMigration()201 void HistoryBackendTestDelegate::StartTopSitesMigration() {
202   test_->backend_->MigrateThumbnailsDatabase();
203 }
204 
TEST_F(HistoryBackendTest,Loaded)205 TEST_F(HistoryBackendTest, Loaded) {
206   ASSERT_TRUE(backend_.get());
207   ASSERT_TRUE(loaded_);
208 }
209 
TEST_F(HistoryBackendTest,DeleteAll)210 TEST_F(HistoryBackendTest, DeleteAll) {
211   ASSERT_TRUE(backend_.get());
212 
213   // Add two favicons, use the characters '1' and '2' for the image data. Note
214   // that we do these in the opposite order. This is so the first one gets ID
215   // 2 autoassigned to the database, which will change when the other one is
216   // deleted. This way we can test that updating works properly.
217   GURL favicon_url1("http://www.google.com/favicon.ico");
218   GURL favicon_url2("http://news.google.com/favicon.ico");
219   FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
220                                                            FAVICON);
221   FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
222                                                            FAVICON);
223 
224   std::vector<unsigned char> data;
225   data.push_back('1');
226   EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
227       new RefCountedBytes(data), Time::Now()));
228 
229   data[0] = '2';
230   EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
231                   favicon2, new RefCountedBytes(data), Time::Now()));
232 
233   // First visit two URLs.
234   URLRow row1(GURL("http://www.google.com/"));
235   row1.set_visit_count(2);
236   row1.set_typed_count(1);
237   row1.set_last_visit(Time::Now());
238   backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
239 
240   URLRow row2(GURL("http://news.google.com/"));
241   row2.set_visit_count(1);
242   row2.set_last_visit(Time::Now());
243   backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
244 
245   std::vector<URLRow> rows;
246   rows.push_back(row2);  // Reversed order for the same reason as favicons.
247   rows.push_back(row1);
248   backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
249 
250   URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
251   URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
252 
253   // Get the two visits for the URLs we just added.
254   VisitVector visits;
255   backend_->db_->GetVisitsForURL(row1_id, &visits);
256   ASSERT_EQ(1U, visits.size());
257   VisitID visit1_id = visits[0].visit_id;
258 
259   visits.clear();
260   backend_->db_->GetVisitsForURL(row2_id, &visits);
261   ASSERT_EQ(1U, visits.size());
262   VisitID visit2_id = visits[0].visit_id;
263 
264   // The in-memory backend should have been set and it should have gotten the
265   // typed URL.
266   ASSERT_TRUE(mem_backend_.get());
267   URLRow outrow1;
268   EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
269 
270   // Add thumbnails for each page.
271   ThumbnailScore score(0.25, true, true);
272   scoped_ptr<SkBitmap> google_bitmap(
273       gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
274 
275   Time time;
276   GURL gurl;
277   backend_->thumbnail_db_->SetPageThumbnail(gurl, row1_id, *google_bitmap,
278                                             score, time);
279   scoped_ptr<SkBitmap> weewar_bitmap(
280      gfx::JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail)));
281   backend_->thumbnail_db_->SetPageThumbnail(gurl, row2_id, *weewar_bitmap,
282                                             score, time);
283 
284   // Star row1.
285   bookmark_model_.AddURL(
286       bookmark_model_.GetBookmarkBarNode(), 0, string16(), row1.url());
287 
288   // Set full text index for each one.
289   backend_->text_database_->AddPageData(row1.url(), row1_id, visit1_id,
290                                         row1.last_visit(),
291                                         UTF8ToUTF16("Title 1"),
292                                         UTF8ToUTF16("Body 1"));
293   backend_->text_database_->AddPageData(row2.url(), row2_id, visit2_id,
294                                         row2.last_visit(),
295                                         UTF8ToUTF16("Title 2"),
296                                         UTF8ToUTF16("Body 2"));
297 
298   // Now finally clear all history.
299   backend_->DeleteAllHistory();
300 
301   // The first URL should be preserved but the time should be cleared.
302   EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
303   EXPECT_EQ(row1.url(), outrow1.url());
304   EXPECT_EQ(0, outrow1.visit_count());
305   EXPECT_EQ(0, outrow1.typed_count());
306   EXPECT_TRUE(Time() == outrow1.last_visit());
307 
308   // The second row should be deleted.
309   URLRow outrow2;
310   EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
311 
312   // All visits should be deleted for both URLs.
313   VisitVector all_visits;
314   backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
315   ASSERT_EQ(0U, all_visits.size());
316 
317   // All thumbnails should be deleted.
318   std::vector<unsigned char> out_data;
319   EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(outrow1.id(),
320                                                          &out_data));
321   EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(row2_id, &out_data));
322 
323   // We should have a favicon for the first URL only. We look them up by favicon
324   // URL since the IDs may hav changed.
325   FaviconID out_favicon1 = backend_->thumbnail_db_->
326       GetFaviconIDForFaviconURL(favicon_url1, FAVICON, NULL);
327   EXPECT_TRUE(out_favicon1);
328   FaviconID out_favicon2 = backend_->thumbnail_db_->
329       GetFaviconIDForFaviconURL(favicon_url2, FAVICON, NULL);
330   EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
331 
332   // The remaining URL should still reference the same favicon, even if its
333   // ID has changed.
334   EXPECT_EQ(out_favicon1, GetFavicon(outrow1.url(), FAVICON));
335 
336   // The first URL should still be bookmarked.
337   EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url()));
338 
339   // The full text database should have no data.
340   std::vector<TextDatabase::Match> text_matches;
341   Time first_time_searched;
342   backend_->text_database_->GetTextMatches(UTF8ToUTF16("Body"),
343                                            QueryOptions(),
344                                            &text_matches,
345                                            &first_time_searched);
346   EXPECT_EQ(0U, text_matches.size());
347 }
348 
TEST_F(HistoryBackendTest,URLsNoLongerBookmarked)349 TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
350   GURL favicon_url1("http://www.google.com/favicon.ico");
351   GURL favicon_url2("http://news.google.com/favicon.ico");
352   FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
353                                                            FAVICON);
354   FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
355                                                            FAVICON);
356 
357   std::vector<unsigned char> data;
358   data.push_back('1');
359   EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
360                   favicon1, new RefCountedBytes(data), Time::Now()));
361 
362   data[0] = '2';
363   EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
364                   favicon2, new RefCountedBytes(data), Time::Now()));
365 
366   // First visit two URLs.
367   URLRow row1(GURL("http://www.google.com/"));
368   row1.set_visit_count(2);
369   row1.set_typed_count(1);
370   row1.set_last_visit(Time::Now());
371   EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
372 
373   URLRow row2(GURL("http://news.google.com/"));
374   row2.set_visit_count(1);
375   row2.set_last_visit(Time::Now());
376   EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
377 
378   std::vector<URLRow> rows;
379   rows.push_back(row2);  // Reversed order for the same reason as favicons.
380   rows.push_back(row1);
381   backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
382 
383   URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
384   URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
385 
386   // Star the two URLs.
387   bookmark_model_.SetURLStarred(row1.url(), string16(), true);
388   bookmark_model_.SetURLStarred(row2.url(), string16(), true);
389 
390   // Delete url 2. Because url 2 is starred this won't delete the URL, only
391   // the visits.
392   backend_->expirer_.DeleteURL(row2.url());
393 
394   // Make sure url 2 is still valid, but has no visits.
395   URLRow tmp_url_row;
396   EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
397   VisitVector visits;
398   backend_->db_->GetVisitsForURL(row2_id, &visits);
399   EXPECT_EQ(0U, visits.size());
400   // The favicon should still be valid.
401   EXPECT_EQ(favicon2,
402       backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
403                                                          FAVICON,
404                                                          NULL));
405 
406   // Unstar row2.
407   bookmark_model_.SetURLStarred(row2.url(), string16(), false);
408   // Tell the backend it was unstarred. We have to explicitly do this as
409   // BookmarkModel isn't wired up to the backend during testing.
410   std::set<GURL> unstarred_urls;
411   unstarred_urls.insert(row2.url());
412   backend_->URLsNoLongerBookmarked(unstarred_urls);
413 
414   // The URL should no longer exist.
415   EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
416   // And the favicon should be deleted.
417   EXPECT_EQ(0,
418       backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
419                                                          FAVICON,
420                                                          NULL));
421 
422   // Unstar row 1.
423   bookmark_model_.SetURLStarred(row1.url(), string16(), false);
424   // Tell the backend it was unstarred. We have to explicitly do this as
425   // BookmarkModel isn't wired up to the backend during testing.
426   unstarred_urls.clear();
427   unstarred_urls.insert(row1.url());
428   backend_->URLsNoLongerBookmarked(unstarred_urls);
429 
430   // The URL should still exist (because there were visits).
431   EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
432 
433   // There should still be visits.
434   visits.clear();
435   backend_->db_->GetVisitsForURL(row1_id, &visits);
436   EXPECT_EQ(1U, visits.size());
437 
438   // The favicon should still be valid.
439   EXPECT_EQ(favicon1,
440       backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url1,
441                                                          FAVICON,
442                                                          NULL));
443 }
444 
445 // Tests a handful of assertions for a navigation with a type of
446 // KEYWORD_GENERATED.
TEST_F(HistoryBackendTest,KeywordGenerated)447 TEST_F(HistoryBackendTest, KeywordGenerated) {
448   ASSERT_TRUE(backend_.get());
449 
450   GURL url("http://google.com");
451 
452   Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
453   scoped_refptr<HistoryAddPageArgs> request(
454       new HistoryAddPageArgs(url, visit_time, NULL, 0, GURL(),
455                              history::RedirectList(),
456                              PageTransition::KEYWORD_GENERATED,
457                              history::SOURCE_BROWSED, false));
458   backend_->AddPage(request);
459 
460   // A row should have been added for the url.
461   URLRow row;
462   URLID url_id = backend_->db()->GetRowForURL(url, &row);
463   ASSERT_NE(0, url_id);
464 
465   // The typed count should be 1.
466   ASSERT_EQ(1, row.typed_count());
467 
468   // KEYWORD_GENERATED urls should not be added to the segment db.
469   std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
470   EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
471 
472   // One visit should be added.
473   VisitVector visits;
474   EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
475   EXPECT_EQ(1U, visits.size());
476 
477   // But no visible visits.
478   visits.clear();
479   backend_->db()->GetVisibleVisitsInRange(base::Time(), base::Time(), 1,
480                                           &visits);
481   EXPECT_TRUE(visits.empty());
482 
483   // Expire the visits.
484   std::set<GURL> restrict_urls;
485   backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
486                                                    visit_time, Time::Now());
487 
488   // The visit should have been nuked.
489   visits.clear();
490   EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
491   EXPECT_TRUE(visits.empty());
492 
493   // As well as the url.
494   ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
495 }
496 
TEST_F(HistoryBackendTest,ClientRedirect)497 TEST_F(HistoryBackendTest, ClientRedirect) {
498   ASSERT_TRUE(backend_.get());
499 
500   int transition1;
501   int transition2;
502 
503   // Initial transition to page A.
504   GURL url_a("http://google.com/a");
505   AddClientRedirect(GURL(), url_a, false, &transition1, &transition2);
506   EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
507 
508   // User initiated redirect to page B.
509   GURL url_b("http://google.com/b");
510   AddClientRedirect(url_a, url_b, false, &transition1, &transition2);
511   EXPECT_TRUE(transition1 & PageTransition::CHAIN_END);
512   EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
513 
514   // Non-user initiated redirect to page C.
515   GURL url_c("http://google.com/c");
516   AddClientRedirect(url_b, url_c, true, &transition1, &transition2);
517   EXPECT_FALSE(transition1 & PageTransition::CHAIN_END);
518   EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
519 }
520 
TEST_F(HistoryBackendTest,ImportedFaviconsTest)521 TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
522   // Setup test data - two Urls in the history, one with favicon assigned and
523   // one without.
524   GURL favicon_url1("http://www.google.com/favicon.ico");
525   FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
526                                                            FAVICON);
527   std::vector<unsigned char> data;
528   data.push_back('1');
529   EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
530       RefCountedBytes::TakeVector(&data), Time::Now()));
531   URLRow row1(GURL("http://www.google.com/"));
532   row1.set_visit_count(1);
533   row1.set_last_visit(Time::Now());
534   EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
535 
536   URLRow row2(GURL("http://news.google.com/"));
537   row2.set_visit_count(1);
538   row2.set_last_visit(Time::Now());
539   std::vector<URLRow> rows;
540   rows.push_back(row1);
541   rows.push_back(row2);
542   backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
543   URLRow url_row1, url_row2;
544   EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
545   EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
546   EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
547   EXPECT_TRUE(GetFavicon(row2.url(), FAVICON) == 0);
548 
549   // Now provide one imported favicon for both URLs already in the registry.
550   // The new favicon should only be used with the URL that doesn't already have
551   // a favicon.
552   std::vector<history::ImportedFaviconUsage> favicons;
553   history::ImportedFaviconUsage favicon;
554   favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
555   favicon.png_data.push_back('2');
556   favicon.urls.insert(row1.url());
557   favicon.urls.insert(row2.url());
558   favicons.push_back(favicon);
559   backend_->SetImportedFavicons(favicons);
560   EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
561   EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
562   EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
563   EXPECT_FALSE(GetFavicon(row2.url(), FAVICON) == 0);
564   EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) ==
565       GetFavicon(row2.url(), FAVICON));
566 
567   // A URL should not be added to history (to store favicon), if
568   // the URL is not bookmarked.
569   GURL url3("http://mail.google.com");
570   favicons.clear();
571   favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
572   favicon.png_data.push_back('3');
573   favicon.urls.insert(url3);
574   favicons.push_back(favicon);
575   backend_->SetImportedFavicons(favicons);
576   URLRow url_row3;
577   EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
578 
579   // If the URL is bookmarked, it should get added to history with 0 visits.
580   bookmark_model_.AddURL(bookmark_model_.GetBookmarkBarNode(), 0, string16(),
581                          url3);
582   backend_->SetImportedFavicons(favicons);
583   EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
584   EXPECT_TRUE(url_row3.visit_count() == 0);
585 }
586 
TEST_F(HistoryBackendTest,StripUsernamePasswordTest)587 TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
588   ASSERT_TRUE(backend_.get());
589 
590   GURL url("http://anyuser:anypass@www.google.com");
591   GURL stripped_url("http://www.google.com");
592 
593   // Clear all history.
594   backend_->DeleteAllHistory();
595 
596   // Visit the url with username, password.
597   backend_->AddPageVisit(url, base::Time::Now(), 0,
598     PageTransition::GetQualifier(PageTransition::TYPED),
599     history::SOURCE_BROWSED);
600 
601   // Fetch the row information about stripped url from history db.
602   VisitVector visits;
603   URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
604   backend_->db_->GetVisitsForURL(row_id, &visits);
605 
606   // Check if stripped url is stored in database.
607   ASSERT_EQ(1U, visits.size());
608 }
609 
TEST_F(HistoryBackendTest,AddPageVisitSource)610 TEST_F(HistoryBackendTest, AddPageVisitSource) {
611   ASSERT_TRUE(backend_.get());
612 
613   GURL url("http://www.google.com");
614 
615   // Clear all history.
616   backend_->DeleteAllHistory();
617 
618   // Assume visiting the url from an externsion.
619   backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
620                          history::SOURCE_EXTENSION);
621   // Assume the url is imported from Firefox.
622   backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
623                          history::SOURCE_FIREFOX_IMPORTED);
624   // Assume this url is also synced.
625   backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
626                          history::SOURCE_SYNCED);
627 
628   // Fetch the row information about the url from history db.
629   VisitVector visits;
630   URLID row_id = backend_->db_->GetRowForURL(url, NULL);
631   backend_->db_->GetVisitsForURL(row_id, &visits);
632 
633   // Check if all the visits to the url are stored in database.
634   ASSERT_EQ(3U, visits.size());
635   VisitSourceMap visit_sources;
636   backend_->db_->GetVisitsSource(visits, &visit_sources);
637   ASSERT_EQ(3U, visit_sources.size());
638   int sources = 0;
639   for (int i = 0; i < 3; i++) {
640     switch (visit_sources[visits[i].visit_id]) {
641       case history::SOURCE_EXTENSION:
642         sources |= 0x1;
643         break;
644       case history::SOURCE_FIREFOX_IMPORTED:
645         sources |= 0x2;
646         break;
647       case history::SOURCE_SYNCED:
648         sources |= 0x4;
649       default:
650         break;
651     }
652   }
653   EXPECT_EQ(0x7, sources);
654 }
655 
TEST_F(HistoryBackendTest,AddPageArgsSource)656 TEST_F(HistoryBackendTest, AddPageArgsSource) {
657   ASSERT_TRUE(backend_.get());
658 
659   GURL url("http://testpageargs.com");
660 
661   // Assume this page is browsed by user.
662   scoped_refptr<HistoryAddPageArgs> request1(
663       new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
664                              history::RedirectList(),
665                              PageTransition::KEYWORD_GENERATED,
666                              history::SOURCE_BROWSED, false));
667   backend_->AddPage(request1);
668   // Assume this page is synced.
669   scoped_refptr<HistoryAddPageArgs> request2(
670       new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
671                              history::RedirectList(),
672                              PageTransition::LINK,
673                              history::SOURCE_SYNCED, false));
674   backend_->AddPage(request2);
675   // Assume this page is browsed again.
676   scoped_refptr<HistoryAddPageArgs> request3(
677       new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
678                              history::RedirectList(),
679                              PageTransition::TYPED,
680                              history::SOURCE_BROWSED, false));
681   backend_->AddPage(request3);
682 
683   // Three visits should be added with proper sources.
684   VisitVector visits;
685   URLRow row;
686   URLID id = backend_->db()->GetRowForURL(url, &row);
687   ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
688   ASSERT_EQ(3U, visits.size());
689   VisitSourceMap visit_sources;
690   backend_->db_->GetVisitsSource(visits, &visit_sources);
691   ASSERT_EQ(1U, visit_sources.size());
692   EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
693 }
694 
TEST_F(HistoryBackendTest,AddVisitsSource)695 TEST_F(HistoryBackendTest, AddVisitsSource) {
696   ASSERT_TRUE(backend_.get());
697 
698   GURL url1("http://www.cnn.com");
699   std::vector<base::Time> visits1;
700   visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
701   visits1.push_back(Time::Now() - base::TimeDelta::FromDays(1));
702   visits1.push_back(Time::Now());
703 
704   GURL url2("http://www.example.com");
705   std::vector<base::Time> visits2;
706   visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
707   visits2.push_back(Time::Now());
708 
709   // Clear all history.
710   backend_->DeleteAllHistory();
711 
712   // Add the visits.
713   backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
714   backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
715 
716   // Verify the visits were added with their sources.
717   VisitVector visits;
718   URLRow row;
719   URLID id = backend_->db()->GetRowForURL(url1, &row);
720   ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
721   ASSERT_EQ(3U, visits.size());
722   VisitSourceMap visit_sources;
723   backend_->db_->GetVisitsSource(visits, &visit_sources);
724   ASSERT_EQ(3U, visit_sources.size());
725   for (int i = 0; i < 3; i++)
726     EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
727   id = backend_->db()->GetRowForURL(url2, &row);
728   ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
729   ASSERT_EQ(2U, visits.size());
730   backend_->db_->GetVisitsSource(visits, &visit_sources);
731   ASSERT_EQ(2U, visit_sources.size());
732   for (int i = 0; i < 2; i++)
733     EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
734 }
735 
TEST_F(HistoryBackendTest,RemoveVisitsSource)736 TEST_F(HistoryBackendTest, RemoveVisitsSource) {
737   ASSERT_TRUE(backend_.get());
738 
739   GURL url1("http://www.cnn.com");
740   std::vector<base::Time> visits1;
741   visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
742   visits1.push_back(Time::Now());
743 
744   GURL url2("http://www.example.com");
745   std::vector<base::Time> visits2;
746   visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
747   visits2.push_back(Time::Now());
748 
749   // Clear all history.
750   backend_->DeleteAllHistory();
751 
752   // Add the visits.
753   backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
754   backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
755 
756   // Verify the visits of url1 were added.
757   VisitVector visits;
758   URLRow row;
759   URLID id = backend_->db()->GetRowForURL(url1, &row);
760   ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
761   ASSERT_EQ(2U, visits.size());
762   // Remove these visits.
763   ASSERT_TRUE(backend_->RemoveVisits(visits));
764 
765   // Now check only url2's source in visit_source table.
766   VisitSourceMap visit_sources;
767   backend_->db_->GetVisitsSource(visits, &visit_sources);
768   ASSERT_EQ(0U, visit_sources.size());
769   id = backend_->db()->GetRowForURL(url2, &row);
770   ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
771   ASSERT_EQ(2U, visits.size());
772   backend_->db_->GetVisitsSource(visits, &visit_sources);
773   ASSERT_EQ(2U, visit_sources.size());
774   for (int i = 0; i < 2; i++)
775     EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
776 }
777 
778 // Test for migration of adding visit_source table.
TEST_F(HistoryBackendTest,MigrationVisitSource)779 TEST_F(HistoryBackendTest, MigrationVisitSource) {
780   ASSERT_TRUE(backend_.get());
781   backend_->Closing();
782   backend_ = NULL;
783 
784   FilePath old_history_path;
785   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
786   old_history_path = old_history_path.AppendASCII("History");
787   old_history_path = old_history_path.AppendASCII("HistoryNoSource");
788 
789   // Copy history database file to current directory so that it will be deleted
790   // in Teardown.
791   FilePath new_history_path(getTestDir());
792   file_util::Delete(new_history_path, true);
793   file_util::CreateDirectory(new_history_path);
794   FilePath new_history_file = new_history_path.Append(chrome::kHistoryFilename);
795   ASSERT_TRUE(file_util::CopyFile(old_history_path, new_history_file));
796 
797   backend_ = new HistoryBackend(new_history_path,
798                                 new HistoryBackendTestDelegate(this),
799                                 &bookmark_model_);
800   backend_->Init(std::string(), false);
801   backend_->Closing();
802   backend_ = NULL;
803 
804   // Now the database should already be migrated.
805   // Check version first.
806   int cur_version = HistoryDatabase::GetCurrentVersion();
807   sql::Connection db;
808   ASSERT_TRUE(db.Open(new_history_file));
809   sql::Statement s(db.GetUniqueStatement(
810       "SELECT value FROM meta WHERE key = 'version'"));
811   ASSERT_TRUE(s.Step());
812   int file_version = s.ColumnInt(0);
813   EXPECT_EQ(cur_version, file_version);
814 
815   // Check visit_source table is created and empty.
816   s.Assign(db.GetUniqueStatement(
817       "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
818   ASSERT_TRUE(s.Step());
819   s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
820   EXPECT_FALSE(s.Step());
821 }
822 
TEST_F(HistoryBackendTest,SetFaviconMapping)823 TEST_F(HistoryBackendTest, SetFaviconMapping) {
824   // Init recent_redirects_
825   const GURL url1("http://www.google.com");
826   const GURL url2("http://www.google.com/m");
827   URLRow url_info1(url1);
828   url_info1.set_visit_count(0);
829   url_info1.set_typed_count(0);
830   url_info1.set_last_visit(base::Time());
831   url_info1.set_hidden(false);
832   backend_->db_->AddURL(url_info1);
833 
834   URLRow url_info2(url2);
835   url_info2.set_visit_count(0);
836   url_info2.set_typed_count(0);
837   url_info2.set_last_visit(base::Time());
838   url_info2.set_hidden(false);
839   backend_->db_->AddURL(url_info2);
840 
841   history::RedirectList redirects;
842   redirects.push_back(url2);
843   redirects.push_back(url1);
844   backend_->recent_redirects_.Put(url1, redirects);
845 
846   const GURL icon_url("http://www.google.com/icon");
847   std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
848   // Add a favicon
849   backend_->SetFavicon(
850       url1, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
851   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
852       url1, FAVICON, NULL));
853   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
854       url2, FAVICON, NULL));
855 
856   // Add a touch_icon
857   backend_->SetFavicon(
858       url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
859   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
860       url1, TOUCH_ICON, NULL));
861   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
862       url2, TOUCH_ICON, NULL));
863   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
864       url1, FAVICON, NULL));
865 
866   // Add a TOUCH_PRECOMPOSED_ICON
867   backend_->SetFavicon(url1,
868                        icon_url,
869                        RefCountedBytes::TakeVector(&data),
870                        TOUCH_PRECOMPOSED_ICON);
871   // The touch_icon was replaced.
872   EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
873       url1, TOUCH_ICON, NULL));
874   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
875       url1, FAVICON, NULL));
876   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
877       url1, TOUCH_PRECOMPOSED_ICON, NULL));
878   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
879       url2, TOUCH_PRECOMPOSED_ICON, NULL));
880 
881   // Add a touch_icon
882   backend_->SetFavicon(
883       url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
884   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
885       url1, TOUCH_ICON, NULL));
886   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
887       url1, FAVICON, NULL));
888   // The TOUCH_PRECOMPOSED_ICON was replaced.
889   EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
890       url1, TOUCH_PRECOMPOSED_ICON, NULL));
891 
892   // Add a favicon
893   const GURL icon_url2("http://www.google.com/icon2");
894   backend_->SetFavicon(
895       url1, icon_url2, RefCountedBytes::TakeVector(&data), FAVICON);
896   FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
897       icon_url2, FAVICON, NULL);
898   EXPECT_NE(0, icon_id);
899   std::vector<IconMapping> icon_mapping;
900   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
901       url1, &icon_mapping));
902   // The old icon was replaced.
903   EXPECT_TRUE(icon_mapping.size() > 1);
904   EXPECT_EQ(icon_id, icon_mapping[1].icon_id);
905 }
906 
TEST_F(HistoryBackendTest,AddOrUpdateIconMapping)907 TEST_F(HistoryBackendTest, AddOrUpdateIconMapping) {
908   // Test the same icon and page mapping will not be added twice. other case
909   // should be covered in TEST_F(HistoryBackendTest, SetFaviconMapping)
910   const GURL url("http://www.google.com/");
911   const GURL icon_url("http://www.google.com/icon");
912   std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
913 
914   backend_->SetFavicon(
915       url, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
916   FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
917       icon_url, FAVICON, NULL);
918 
919   // Add the same mapping
920   FaviconID replaced;
921   EXPECT_FALSE(backend_->AddOrUpdateIconMapping(
922       url, icon_id, FAVICON, &replaced));
923   EXPECT_EQ(0, replaced);
924 
925   std::vector<IconMapping> icon_mapping;
926   EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
927       url, &icon_mapping));
928   EXPECT_EQ(1u, icon_mapping.size());
929 }
930 
931 }  // namespace history
932