Home | History | Annotate | Download | only in history
      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:
     54   explicit HistoryBackendTestDelegate(HistoryBackendTest* test) : test_(test) {}
     55 
     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:
     72   HistoryBackendTest() : bookmark_model_(NULL), loaded_(false) {}
     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 
     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.
    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 
    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 
    129   FilePath getTestDir() {
    130     return test_dir_;
    131   }
    132 
    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
    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   }
    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 
    168   void SetInMemoryBackend(InMemoryHistoryBackend* backend) {
    169     mem_backend_.reset(backend);
    170   }
    171 
    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 
    186 void HistoryBackendTestDelegate::SetInMemoryBackend(
    187     InMemoryHistoryBackend* backend) {
    188   test_->SetInMemoryBackend(backend);
    189 }
    190 
    191 void HistoryBackendTestDelegate::BroadcastNotifications(
    192     NotificationType type,
    193     HistoryDetails* details) {
    194   test_->BroadcastNotifications(type, details);
    195 }
    196 
    197 void HistoryBackendTestDelegate::DBLoaded() {
    198   test_->loaded_ = true;
    199 }
    200 
    201 void HistoryBackendTestDelegate::StartTopSitesMigration() {
    202   test_->backend_->MigrateThumbnailsDatabase();
    203 }
    204 
    205 TEST_F(HistoryBackendTest, Loaded) {
    206   ASSERT_TRUE(backend_.get());
    207   ASSERT_TRUE(loaded_);
    208 }
    209 
    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 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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