Home | History | Annotate | Download | only in history
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <algorithm>
      6 #include <vector>
      7 
      8 #include "base/basictypes.h"
      9 #include "base/files/file_enumerator.h"
     10 #include "base/files/file_path.h"
     11 #include "base/files/scoped_temp_dir.h"
     12 #include "base/memory/ref_counted_memory.h"
     13 #include "base/path_service.h"
     14 #include "chrome/browser/history/thumbnail_database.h"
     15 #include "chrome/common/chrome_paths.h"
     16 #include "sql/connection.h"
     17 #include "sql/recovery.h"
     18 #include "sql/test/scoped_error_ignorer.h"
     19 #include "sql/test/test_helpers.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 #include "third_party/sqlite/sqlite3.h"
     22 #include "url/gurl.h"
     23 
     24 namespace history {
     25 
     26 namespace {
     27 
     28 // Blobs for the bitmap tests.  These aren't real bitmaps.  Golden
     29 // database files store the same blobs (see VersionN tests).
     30 const unsigned char kBlob1[] =
     31     "12346102356120394751634516591348710478123649165419234519234512349134";
     32 const unsigned char kBlob2[] =
     33     "goiwuegrqrcomizqyzkjalitbahxfjytrqvpqeroicxmnlkhlzunacxaneviawrtxcywhgef";
     34 
     35 // Page and icon urls shared by tests.  Present in golden database
     36 // files (see VersionN tests).
     37 const GURL kPageUrl1 = GURL("http://google.com/");
     38 const GURL kPageUrl2 = GURL("http://yahoo.com/");
     39 const GURL kPageUrl3 = GURL("http://www.google.com/");
     40 const GURL kPageUrl4 = GURL("http://www.google.com/blank.html");
     41 const GURL kPageUrl5 = GURL("http://www.bing.com/");
     42 
     43 const GURL kIconUrl1 = GURL("http://www.google.com/favicon.ico");
     44 const GURL kIconUrl2 = GURL("http://www.yahoo.com/favicon.ico");
     45 const GURL kIconUrl3 = GURL("http://www.google.com/touch.ico");
     46 const GURL kIconUrl5 = GURL("http://www.bing.com/favicon.ico");
     47 
     48 const gfx::Size kSmallSize = gfx::Size(16, 16);
     49 const gfx::Size kLargeSize = gfx::Size(32, 32);
     50 
     51 // Create the test database at |db_path| from the golden file at
     52 // |ascii_path| in the "History/" subdir of the test data dir.
     53 WARN_UNUSED_RESULT bool CreateDatabaseFromSQL(const base::FilePath &db_path,
     54                                               const char* ascii_path) {
     55   base::FilePath sql_path;
     56   if (!PathService::Get(chrome::DIR_TEST_DATA, &sql_path))
     57     return false;
     58   sql_path = sql_path.AppendASCII("History").AppendASCII(ascii_path);
     59   return sql::test::CreateDatabaseFromSQL(db_path, sql_path);
     60 }
     61 
     62 // Verify that the up-to-date database has the expected tables and
     63 // columns.  Functional tests only check whether the things which
     64 // should be there are, but do not check if extraneous items are
     65 // present.  Any extraneous items have the potential to interact
     66 // negatively with future schema changes.
     67 void VerifyTablesAndColumns(sql::Connection* db) {
     68   // [meta], [favicons], [favicon_bitmaps], and [icon_mapping].
     69   EXPECT_EQ(4u, sql::test::CountSQLTables(db));
     70 
     71   // Implicit index on [meta], index on [favicons], index on
     72   // [favicon_bitmaps], two indices on [icon_mapping].
     73   EXPECT_EQ(5u, sql::test::CountSQLIndices(db));
     74 
     75   // [key] and [value].
     76   EXPECT_EQ(2u, sql::test::CountTableColumns(db, "meta"));
     77 
     78   // [id], [url], and [icon_type].
     79   EXPECT_EQ(3u, sql::test::CountTableColumns(db, "favicons"));
     80 
     81   // [id], [icon_id], [last_updated], [image_data], [width], and [height].
     82   EXPECT_EQ(6u, sql::test::CountTableColumns(db, "favicon_bitmaps"));
     83 
     84   // [id], [page_url], and [icon_id].
     85   EXPECT_EQ(3u, sql::test::CountTableColumns(db, "icon_mapping"));
     86 }
     87 
     88 void VerifyDatabaseEmpty(sql::Connection* db) {
     89   size_t rows = 0;
     90   EXPECT_TRUE(sql::test::CountTableRows(db, "favicons", &rows));
     91   EXPECT_EQ(0u, rows);
     92   EXPECT_TRUE(sql::test::CountTableRows(db, "favicon_bitmaps", &rows));
     93   EXPECT_EQ(0u, rows);
     94   EXPECT_TRUE(sql::test::CountTableRows(db, "icon_mapping", &rows));
     95   EXPECT_EQ(0u, rows);
     96 }
     97 
     98 // Helper to check that an expected mapping exists.
     99 WARN_UNUSED_RESULT bool CheckPageHasIcon(
    100     ThumbnailDatabase* db,
    101     const GURL& page_url,
    102     chrome::IconType expected_icon_type,
    103     const GURL& expected_icon_url,
    104     const gfx::Size& expected_icon_size,
    105     size_t expected_icon_contents_size,
    106     const unsigned char* expected_icon_contents) {
    107   std::vector<IconMapping> icon_mappings;
    108   if (!db->GetIconMappingsForPageURL(page_url, &icon_mappings)) {
    109     ADD_FAILURE() << "failed GetIconMappingsForPageURL()";
    110     return false;
    111   }
    112 
    113   // Scan for the expected type.
    114   std::vector<IconMapping>::const_iterator iter = icon_mappings.begin();
    115   for (; iter != icon_mappings.end(); ++iter) {
    116     if (iter->icon_type == expected_icon_type)
    117       break;
    118   }
    119   if (iter == icon_mappings.end()) {
    120     ADD_FAILURE() << "failed to find |expected_icon_type|";
    121     return false;
    122   }
    123 
    124   if (expected_icon_url != iter->icon_url) {
    125     EXPECT_EQ(expected_icon_url, iter->icon_url);
    126     return false;
    127   }
    128 
    129   std::vector<FaviconBitmap> favicon_bitmaps;
    130   if (!db->GetFaviconBitmaps(iter->icon_id, &favicon_bitmaps)) {
    131     ADD_FAILURE() << "failed GetFaviconBitmaps()";
    132     return false;
    133   }
    134 
    135   if (1 != favicon_bitmaps.size()) {
    136     EXPECT_EQ(1u, favicon_bitmaps.size());
    137     return false;
    138   }
    139 
    140   if (expected_icon_size != favicon_bitmaps[0].pixel_size) {
    141     EXPECT_EQ(expected_icon_size, favicon_bitmaps[0].pixel_size);
    142     return false;
    143   }
    144 
    145   if (expected_icon_contents_size != favicon_bitmaps[0].bitmap_data->size()) {
    146     EXPECT_EQ(expected_icon_contents_size,
    147               favicon_bitmaps[0].bitmap_data->size());
    148     return false;
    149   }
    150 
    151   if (memcmp(favicon_bitmaps[0].bitmap_data->front(),
    152              expected_icon_contents, expected_icon_contents_size)) {
    153     ADD_FAILURE() << "failed to match |expected_icon_contents|";
    154     return false;
    155   }
    156   return true;
    157 }
    158 
    159 }  // namespace
    160 
    161 class ThumbnailDatabaseTest : public testing::Test {
    162  public:
    163   ThumbnailDatabaseTest() {
    164   }
    165   virtual ~ThumbnailDatabaseTest() {
    166   }
    167 
    168   // Initialize a thumbnail database instance from the SQL file at
    169   // |golden_path| in the "History/" subdirectory of test data.
    170   scoped_ptr<ThumbnailDatabase> LoadFromGolden(const char* golden_path) {
    171     if (!CreateDatabaseFromSQL(file_name_, golden_path)) {
    172       ADD_FAILURE() << "Failed loading " << golden_path;
    173       return scoped_ptr<ThumbnailDatabase>();
    174     }
    175 
    176     scoped_ptr<ThumbnailDatabase> db(new ThumbnailDatabase());
    177     EXPECT_EQ(sql::INIT_OK, db->Init(file_name_));
    178     db->BeginTransaction();
    179 
    180     return db.Pass();
    181   }
    182 
    183  protected:
    184   virtual void SetUp() {
    185     // Get a temporary directory for the test DB files.
    186     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    187 
    188     file_name_ = temp_dir_.path().AppendASCII("TestFavicons.db");
    189   }
    190 
    191   base::ScopedTempDir temp_dir_;
    192   base::FilePath file_name_;
    193 };
    194 
    195 TEST_F(ThumbnailDatabaseTest, AddIconMapping) {
    196   ThumbnailDatabase db;
    197   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    198   db.BeginTransaction();
    199 
    200   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    201   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    202 
    203   GURL url("http://google.com");
    204   base::Time time = base::Time::Now();
    205   chrome::FaviconID id = db.AddFavicon(url,
    206                                        chrome::TOUCH_ICON,
    207                                        favicon,
    208                                        time,
    209                                        gfx::Size());
    210   EXPECT_NE(0, id);
    211 
    212   EXPECT_NE(0, db.AddIconMapping(url, id));
    213   std::vector<IconMapping> icon_mappings;
    214   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mappings));
    215   EXPECT_EQ(1u, icon_mappings.size());
    216   EXPECT_EQ(url, icon_mappings.front().page_url);
    217   EXPECT_EQ(id, icon_mappings.front().icon_id);
    218 }
    219 
    220 TEST_F(ThumbnailDatabaseTest, UpdateIconMapping) {
    221   ThumbnailDatabase db;
    222   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    223   db.BeginTransaction();
    224 
    225   GURL url("http://google.com");
    226   chrome::FaviconID id =
    227       db.AddFavicon(url, chrome::TOUCH_ICON);
    228 
    229   EXPECT_LT(0, db.AddIconMapping(url, id));
    230   std::vector<IconMapping> icon_mapping;
    231   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
    232   ASSERT_EQ(1u, icon_mapping.size());
    233   EXPECT_EQ(url, icon_mapping.front().page_url);
    234   EXPECT_EQ(id, icon_mapping.front().icon_id);
    235 
    236   GURL url1("http://www.google.com/");
    237   chrome::FaviconID new_id =
    238       db.AddFavicon(url1, chrome::TOUCH_ICON);
    239   EXPECT_TRUE(db.UpdateIconMapping(icon_mapping.front().mapping_id, new_id));
    240 
    241   icon_mapping.clear();
    242   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
    243   ASSERT_EQ(1u, icon_mapping.size());
    244   EXPECT_EQ(url, icon_mapping.front().page_url);
    245   EXPECT_EQ(new_id, icon_mapping.front().icon_id);
    246   EXPECT_NE(id, icon_mapping.front().icon_id);
    247 }
    248 
    249 TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
    250   ThumbnailDatabase db;
    251   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    252   db.BeginTransaction();
    253 
    254   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    255   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    256 
    257   GURL url("http://google.com");
    258   chrome::FaviconID id =
    259       db.AddFavicon(url, chrome::TOUCH_ICON);
    260   base::Time time = base::Time::Now();
    261   db.AddFaviconBitmap(id, favicon, time, gfx::Size());
    262   EXPECT_LT(0, db.AddIconMapping(url, id));
    263 
    264   chrome::FaviconID id2 =
    265       db.AddFavicon(url, chrome::FAVICON);
    266   EXPECT_LT(0, db.AddIconMapping(url, id2));
    267   ASSERT_NE(id, id2);
    268 
    269   std::vector<IconMapping> icon_mapping;
    270   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
    271   ASSERT_EQ(2u, icon_mapping.size());
    272   EXPECT_EQ(icon_mapping.front().icon_type, chrome::TOUCH_ICON);
    273   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, chrome::FAVICON, NULL));
    274 
    275   db.DeleteIconMappings(url);
    276 
    277   EXPECT_FALSE(db.GetIconMappingsForPageURL(url, NULL));
    278   EXPECT_FALSE(db.GetIconMappingsForPageURL(url, chrome::FAVICON, NULL));
    279 }
    280 
    281 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) {
    282   ThumbnailDatabase db;
    283   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    284   db.BeginTransaction();
    285 
    286   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    287   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    288 
    289   GURL url("http://google.com");
    290 
    291   chrome::FaviconID id1 = db.AddFavicon(url, chrome::TOUCH_ICON);
    292   base::Time time = base::Time::Now();
    293   db.AddFaviconBitmap(id1, favicon, time, kSmallSize);
    294   db.AddFaviconBitmap(id1, favicon, time, kLargeSize);
    295   EXPECT_LT(0, db.AddIconMapping(url, id1));
    296 
    297   chrome::FaviconID id2 = db.AddFavicon(url, chrome::FAVICON);
    298   EXPECT_NE(id1, id2);
    299   db.AddFaviconBitmap(id2, favicon, time, kSmallSize);
    300   EXPECT_LT(0, db.AddIconMapping(url, id2));
    301 
    302   std::vector<IconMapping> icon_mappings;
    303   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mappings));
    304   ASSERT_EQ(2u, icon_mappings.size());
    305   EXPECT_EQ(id1, icon_mappings[0].icon_id);
    306   EXPECT_EQ(id2, icon_mappings[1].icon_id);
    307 }
    308 
    309 TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrls) {
    310   ThumbnailDatabase db;
    311 
    312   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    313 
    314   db.BeginTransaction();
    315 
    316   // Build a database mapping kPageUrl1 -> kIconUrl1, kPageUrl2 ->
    317   // kIconUrl2, kPageUrl3 -> kIconUrl1, and kPageUrl5 -> kIconUrl5.
    318   // Then retain kPageUrl1, kPageUrl3, and kPageUrl5.  kPageUrl2
    319   // should go away, but the others should be retained correctly.
    320 
    321   // TODO(shess): This would probably make sense as a golden file.
    322 
    323   scoped_refptr<base::RefCountedStaticMemory> favicon1(
    324       new base::RefCountedStaticMemory(kBlob1, sizeof(kBlob1)));
    325   scoped_refptr<base::RefCountedStaticMemory> favicon2(
    326       new base::RefCountedStaticMemory(kBlob2, sizeof(kBlob2)));
    327 
    328   chrome::FaviconID kept_id1 = db.AddFavicon(kIconUrl1, chrome::FAVICON);
    329   db.AddFaviconBitmap(kept_id1, favicon1, base::Time::Now(), kLargeSize);
    330   db.AddIconMapping(kPageUrl1, kept_id1);
    331   db.AddIconMapping(kPageUrl3, kept_id1);
    332 
    333   chrome::FaviconID unkept_id = db.AddFavicon(kIconUrl2, chrome::FAVICON);
    334   db.AddFaviconBitmap(unkept_id, favicon1, base::Time::Now(), kLargeSize);
    335   db.AddIconMapping(kPageUrl2, unkept_id);
    336 
    337   chrome::FaviconID kept_id2 = db.AddFavicon(kIconUrl5, chrome::FAVICON);
    338   db.AddFaviconBitmap(kept_id2, favicon2, base::Time::Now(), kLargeSize);
    339   db.AddIconMapping(kPageUrl5, kept_id2);
    340 
    341   // RetainDataForPageUrls() uses schema manipulations for efficiency.
    342   // Grab a copy of the schema to make sure the final schema matches.
    343   const std::string original_schema = db.db_.GetSchema();
    344 
    345   std::vector<GURL> pages_to_keep;
    346   pages_to_keep.push_back(kPageUrl1);
    347   pages_to_keep.push_back(kPageUrl3);
    348   pages_to_keep.push_back(kPageUrl5);
    349   EXPECT_TRUE(db.RetainDataForPageUrls(pages_to_keep));
    350 
    351   // Mappings from the retained urls should be left.
    352   EXPECT_TRUE(CheckPageHasIcon(&db, kPageUrl1, chrome::FAVICON,
    353                                kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    354   EXPECT_TRUE(CheckPageHasIcon(&db, kPageUrl3, chrome::FAVICON,
    355                                kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    356   EXPECT_TRUE(CheckPageHasIcon(&db, kPageUrl5, chrome::FAVICON,
    357                                kIconUrl5, kLargeSize, sizeof(kBlob2), kBlob2));
    358 
    359   // The one not retained should be missing.
    360   EXPECT_FALSE(db.GetFaviconIDForFaviconURL(kPageUrl2, false, NULL));
    361 
    362   // Schema should be the same.
    363   EXPECT_EQ(original_schema, db.db_.GetSchema());
    364 }
    365 
    366 // Tests that deleting a favicon deletes the favicon row and favicon bitmap
    367 // rows from the database.
    368 TEST_F(ThumbnailDatabaseTest, DeleteFavicon) {
    369   ThumbnailDatabase db;
    370   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    371   db.BeginTransaction();
    372 
    373   std::vector<unsigned char> data1(kBlob1, kBlob1 + sizeof(kBlob1));
    374   scoped_refptr<base::RefCountedBytes> favicon1(
    375       new base::RefCountedBytes(data1));
    376   std::vector<unsigned char> data2(kBlob2, kBlob2 + sizeof(kBlob2));
    377   scoped_refptr<base::RefCountedBytes> favicon2(
    378       new base::RefCountedBytes(data2));
    379 
    380   GURL url("http://google.com");
    381   chrome::FaviconID id = db.AddFavicon(url, chrome::FAVICON);
    382   base::Time last_updated = base::Time::Now();
    383   db.AddFaviconBitmap(id, favicon1, last_updated, kSmallSize);
    384   db.AddFaviconBitmap(id, favicon2, last_updated, kLargeSize);
    385 
    386   EXPECT_TRUE(db.GetFaviconBitmaps(id, NULL));
    387 
    388   EXPECT_TRUE(db.DeleteFavicon(id));
    389   EXPECT_FALSE(db.GetFaviconBitmaps(id, NULL));
    390 }
    391 
    392 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
    393   ThumbnailDatabase db;
    394   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    395   db.BeginTransaction();
    396 
    397   // Add a favicon
    398   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    399   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    400 
    401   GURL page_url("http://google.com");
    402   GURL icon_url("http://google.com/favicon.ico");
    403   base::Time time = base::Time::Now();
    404 
    405   chrome::FaviconID id = db.AddFavicon(icon_url,
    406                                        chrome::FAVICON,
    407                                        favicon,
    408                                        time,
    409                                        gfx::Size());
    410   EXPECT_NE(0, db.AddIconMapping(page_url, id));
    411   std::vector<IconMapping> icon_mappings;
    412   EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
    413 
    414   EXPECT_EQ(page_url, icon_mappings.front().page_url);
    415   EXPECT_EQ(id, icon_mappings.front().icon_id);
    416   EXPECT_EQ(chrome::FAVICON, icon_mappings.front().icon_type);
    417   EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
    418 
    419   // Add a touch icon
    420   std::vector<unsigned char> data2(kBlob2, kBlob2 + sizeof(kBlob2));
    421   scoped_refptr<base::RefCountedBytes> favicon2 =
    422       new base::RefCountedBytes(data);
    423 
    424   chrome::FaviconID id2 = db.AddFavicon(icon_url,
    425                                         chrome::TOUCH_ICON,
    426                                         favicon2,
    427                                         time,
    428                                         gfx::Size());
    429   EXPECT_NE(0, db.AddIconMapping(page_url, id2));
    430 
    431   icon_mappings.clear();
    432   EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
    433 
    434   EXPECT_EQ(page_url, icon_mappings.front().page_url);
    435   EXPECT_EQ(id2, icon_mappings.front().icon_id);
    436   EXPECT_EQ(chrome::TOUCH_ICON, icon_mappings.front().icon_type);
    437   EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
    438 
    439   // Add a touch precomposed icon
    440   scoped_refptr<base::RefCountedBytes> favicon3 =
    441       new base::RefCountedBytes(data2);
    442 
    443   chrome::FaviconID id3 = db.AddFavicon(icon_url,
    444                                         chrome::TOUCH_PRECOMPOSED_ICON,
    445                                         favicon3,
    446                                         time,
    447                                         gfx::Size());
    448   EXPECT_NE(0, db.AddIconMapping(page_url, id3));
    449 
    450   icon_mappings.clear();
    451   EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
    452 
    453   EXPECT_EQ(page_url, icon_mappings.front().page_url);
    454   EXPECT_EQ(id3, icon_mappings.front().icon_id);
    455   EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings.front().icon_type);
    456   EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
    457 }
    458 
    459 // Test result of GetIconMappingsForPageURL when an icon type is passed in.
    460 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLWithIconType) {
    461   ThumbnailDatabase db;
    462   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    463   db.BeginTransaction();
    464 
    465   GURL url("http://google.com");
    466   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    467   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    468   base::Time time = base::Time::Now();
    469 
    470   chrome::FaviconID id1 = db.AddFavicon(url,
    471                                         chrome::FAVICON,
    472                                         favicon,
    473                                         time,
    474                                         gfx::Size());
    475   EXPECT_NE(0, db.AddIconMapping(url, id1));
    476 
    477   chrome::FaviconID id2 = db.AddFavicon(url,
    478                                         chrome::TOUCH_ICON,
    479                                         favicon,
    480                                         time,
    481                                         gfx::Size());
    482   EXPECT_NE(0, db.AddIconMapping(url, id2));
    483 
    484   chrome::FaviconID id3 = db.AddFavicon(url,
    485                                         chrome::TOUCH_ICON,
    486                                         favicon,
    487                                         time,
    488                                         gfx::Size());
    489   EXPECT_NE(0, db.AddIconMapping(url, id3));
    490 
    491   // Only the mappings for favicons of type TOUCH_ICON should be returned as
    492   // TOUCH_ICON is a larger icon type than FAVICON.
    493   std::vector<IconMapping> icon_mappings;
    494   EXPECT_TRUE(db.GetIconMappingsForPageURL(
    495       url,
    496       chrome::FAVICON | chrome::TOUCH_ICON | chrome::TOUCH_PRECOMPOSED_ICON,
    497       &icon_mappings));
    498 
    499   EXPECT_EQ(2u, icon_mappings.size());
    500   if (id2 == icon_mappings[0].icon_id) {
    501     EXPECT_EQ(id3, icon_mappings[1].icon_id);
    502   } else {
    503     EXPECT_EQ(id3, icon_mappings[0].icon_id);
    504     EXPECT_EQ(id2, icon_mappings[1].icon_id);
    505   }
    506 
    507   icon_mappings.clear();
    508   EXPECT_TRUE(
    509       db.GetIconMappingsForPageURL(url, chrome::TOUCH_ICON, &icon_mappings));
    510   if (id2 == icon_mappings[0].icon_id) {
    511     EXPECT_EQ(id3, icon_mappings[1].icon_id);
    512   } else {
    513     EXPECT_EQ(id3, icon_mappings[0].icon_id);
    514     EXPECT_EQ(id2, icon_mappings[1].icon_id);
    515   }
    516 
    517   icon_mappings.clear();
    518   EXPECT_TRUE(
    519       db.GetIconMappingsForPageURL(url, chrome::FAVICON, &icon_mappings));
    520   EXPECT_EQ(1u, icon_mappings.size());
    521   EXPECT_EQ(id1, icon_mappings[0].icon_id);
    522 }
    523 
    524 TEST_F(ThumbnailDatabaseTest, HasMappingFor) {
    525   ThumbnailDatabase db;
    526   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    527   db.BeginTransaction();
    528 
    529   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    530   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    531 
    532   // Add a favicon which will have icon_mappings
    533   base::Time time = base::Time::Now();
    534   chrome::FaviconID id1 = db.AddFavicon(GURL("http://google.com"),
    535                                         chrome::FAVICON,
    536                                         favicon,
    537                                         time,
    538                                         gfx::Size());
    539   EXPECT_NE(id1, 0);
    540 
    541   // Add another type of favicon
    542   time = base::Time::Now();
    543   chrome::FaviconID id2 = db.AddFavicon(GURL("http://www.google.com/icon"),
    544                                         chrome::TOUCH_ICON,
    545                                         favicon,
    546                                         time,
    547                                         gfx::Size());
    548   EXPECT_NE(id2, 0);
    549 
    550   // Add 3rd favicon
    551   time = base::Time::Now();
    552   chrome::FaviconID id3 = db.AddFavicon(GURL("http://www.google.com/icon"),
    553                                         chrome::TOUCH_ICON,
    554                                         favicon,
    555                                         time,
    556                                         gfx::Size());
    557   EXPECT_NE(id3, 0);
    558 
    559   // Add 2 icon mapping
    560   GURL page_url("http://www.google.com");
    561   EXPECT_TRUE(db.AddIconMapping(page_url, id1));
    562   EXPECT_TRUE(db.AddIconMapping(page_url, id2));
    563 
    564   EXPECT_TRUE(db.HasMappingFor(id1));
    565   EXPECT_TRUE(db.HasMappingFor(id2));
    566   EXPECT_FALSE(db.HasMappingFor(id3));
    567 
    568   // Remove all mappings
    569   db.DeleteIconMappings(page_url);
    570   EXPECT_FALSE(db.HasMappingFor(id1));
    571   EXPECT_FALSE(db.HasMappingFor(id2));
    572   EXPECT_FALSE(db.HasMappingFor(id3));
    573 }
    574 
    575 TEST_F(ThumbnailDatabaseTest, CloneIconMappings) {
    576   ThumbnailDatabase db;
    577   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    578   db.BeginTransaction();
    579 
    580   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    581   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    582 
    583   // Add a favicon which will have icon_mappings
    584   chrome::FaviconID id1 = db.AddFavicon(
    585       GURL("http://google.com"), chrome::FAVICON);
    586   EXPECT_NE(0, id1);
    587   base::Time time = base::Time::Now();
    588   db.AddFaviconBitmap(id1, favicon, time, gfx::Size());
    589 
    590   // Add another type of favicon
    591   chrome::FaviconID id2 = db.AddFavicon(GURL("http://www.google.com/icon"),
    592                                         chrome::TOUCH_ICON);
    593   EXPECT_NE(0, id2);
    594   time = base::Time::Now();
    595   db.AddFaviconBitmap(id2, favicon, time, gfx::Size());
    596 
    597   // Add 3rd favicon
    598   chrome::FaviconID id3 = db.AddFavicon(GURL("http://www.google.com/icon"),
    599                                         chrome::TOUCH_ICON);
    600   EXPECT_NE(0, id3);
    601   time = base::Time::Now();
    602   db.AddFaviconBitmap(id3, favicon, time, gfx::Size());
    603 
    604   GURL page1_url("http://page1.com");
    605   EXPECT_TRUE(db.AddIconMapping(page1_url, id1));
    606   EXPECT_TRUE(db.AddIconMapping(page1_url, id2));
    607 
    608   GURL page2_url("http://page2.com");
    609   EXPECT_TRUE(db.AddIconMapping(page2_url, id3));
    610 
    611   // Test we do nothing with existing mappings.
    612   std::vector<IconMapping> icon_mapping;
    613   EXPECT_TRUE(db.GetIconMappingsForPageURL(page2_url, &icon_mapping));
    614   ASSERT_EQ(1U, icon_mapping.size());
    615 
    616   EXPECT_TRUE(db.CloneIconMappings(page1_url, page2_url));
    617 
    618   icon_mapping.clear();
    619   EXPECT_TRUE(db.GetIconMappingsForPageURL(page2_url, &icon_mapping));
    620   ASSERT_EQ(1U, icon_mapping.size());
    621   EXPECT_EQ(page2_url, icon_mapping[0].page_url);
    622   EXPECT_EQ(id3, icon_mapping[0].icon_id);
    623 
    624   // Test we clone if the new page has no mappings.
    625   GURL page3_url("http://page3.com");
    626   EXPECT_TRUE(db.CloneIconMappings(page1_url, page3_url));
    627 
    628   icon_mapping.clear();
    629   EXPECT_TRUE(db.GetIconMappingsForPageURL(page3_url, &icon_mapping));
    630 
    631   ASSERT_EQ(2U, icon_mapping.size());
    632   if (icon_mapping[0].icon_id == id2)
    633     std::swap(icon_mapping[0], icon_mapping[1]);
    634   EXPECT_EQ(page3_url, icon_mapping[0].page_url);
    635   EXPECT_EQ(id1, icon_mapping[0].icon_id);
    636   EXPECT_EQ(page3_url, icon_mapping[1].page_url);
    637   EXPECT_EQ(id2, icon_mapping[1].icon_id);
    638 }
    639 
    640 // Test loading version 3 database.
    641 TEST_F(ThumbnailDatabaseTest, Version3) {
    642   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v3.sql");
    643   ASSERT_TRUE(db.get() != NULL);
    644   VerifyTablesAndColumns(&db->db_);
    645 
    646   // Version 3 is deprecated, the data should all be gone.
    647   VerifyDatabaseEmpty(&db->db_);
    648 }
    649 
    650 // Test loading version 4 database.
    651 TEST_F(ThumbnailDatabaseTest, Version4) {
    652   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v4.sql");
    653   ASSERT_TRUE(db.get() != NULL);
    654   VerifyTablesAndColumns(&db->db_);
    655 
    656   // Version 4 is deprecated, the data should all be gone.
    657   VerifyDatabaseEmpty(&db->db_);
    658 }
    659 
    660 // Test loading version 5 database.
    661 TEST_F(ThumbnailDatabaseTest, Version5) {
    662   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v5.sql");
    663   ASSERT_TRUE(db.get() != NULL);
    664   VerifyTablesAndColumns(&db->db_);
    665 
    666   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl1, chrome::FAVICON,
    667                                kIconUrl1, gfx::Size(), sizeof(kBlob1), kBlob1));
    668   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl2, chrome::FAVICON,
    669                                kIconUrl2, gfx::Size(), sizeof(kBlob2), kBlob2));
    670   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl3, chrome::FAVICON,
    671                                kIconUrl1, gfx::Size(), sizeof(kBlob1), kBlob1));
    672   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl3, chrome::TOUCH_ICON,
    673                                kIconUrl3, gfx::Size(), sizeof(kBlob2), kBlob2));
    674 }
    675 
    676 // Test loading version 6 database.
    677 TEST_F(ThumbnailDatabaseTest, Version6) {
    678   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v6.sql");
    679   ASSERT_TRUE(db.get() != NULL);
    680   VerifyTablesAndColumns(&db->db_);
    681 
    682   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl1, chrome::FAVICON,
    683                                kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    684   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl2, chrome::FAVICON,
    685                                kIconUrl2, kLargeSize, sizeof(kBlob2), kBlob2));
    686   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl3, chrome::FAVICON,
    687                                kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    688   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl3, chrome::TOUCH_ICON,
    689                                kIconUrl3, kLargeSize, sizeof(kBlob2), kBlob2));
    690 }
    691 
    692 // Test loading version 7 database.
    693 TEST_F(ThumbnailDatabaseTest, Version7) {
    694   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v7.sql");
    695   ASSERT_TRUE(db.get() != NULL);
    696   VerifyTablesAndColumns(&db->db_);
    697 
    698   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl1, chrome::FAVICON,
    699                                kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    700   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl2, chrome::FAVICON,
    701                                kIconUrl2, kLargeSize, sizeof(kBlob2), kBlob2));
    702   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl3, chrome::FAVICON,
    703                                kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    704   EXPECT_TRUE(CheckPageHasIcon(db.get(), kPageUrl3, chrome::TOUCH_ICON,
    705                                kIconUrl3, kLargeSize, sizeof(kBlob2), kBlob2));
    706 }
    707 
    708 TEST_F(ThumbnailDatabaseTest, Recovery) {
    709   // This code tests the recovery module in concert with Chromium's
    710   // custom recover virtual table.  Under USE_SYSTEM_SQLITE, this is
    711   // not available.  This is detected dynamically because corrupt
    712   // databases still need to be handled, perhaps by Raze(), and the
    713   // recovery module is an obvious layer to abstract that to.
    714   // TODO(shess): Handle that case for real!
    715   if (!sql::Recovery::FullRecoverySupported())
    716     return;
    717 
    718   // Create an example database.
    719   {
    720     EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v7.sql"));
    721 
    722     sql::Connection raw_db;
    723     EXPECT_TRUE(raw_db.Open(file_name_));
    724     VerifyTablesAndColumns(&raw_db);
    725   }
    726 
    727   // Test that the contents make sense after clean open.
    728   {
    729     ThumbnailDatabase db;
    730     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    731 
    732     EXPECT_TRUE(
    733         CheckPageHasIcon(&db, kPageUrl1, chrome::FAVICON,
    734                          kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    735     EXPECT_TRUE(
    736         CheckPageHasIcon(&db, kPageUrl2, chrome::FAVICON,
    737                          kIconUrl2, kLargeSize, sizeof(kBlob2), kBlob2));
    738   }
    739 
    740   // Corrupt the |icon_mapping.page_url| index by deleting an element
    741   // from the backing table but not the index.
    742   {
    743     sql::Connection raw_db;
    744     EXPECT_TRUE(raw_db.Open(file_name_));
    745     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    746   }
    747   const char kIndexName[] = "icon_mapping_page_url_idx";
    748   const char kDeleteSql[] =
    749       "DELETE FROM icon_mapping WHERE page_url = 'http://yahoo.com/'";
    750   EXPECT_TRUE(
    751       sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql));
    752 
    753   // Database should be corrupt at the SQLite level.
    754   {
    755     sql::Connection raw_db;
    756     EXPECT_TRUE(raw_db.Open(file_name_));
    757     ASSERT_NE("ok", sql::test::IntegrityCheck(&raw_db));
    758   }
    759 
    760   // Open the database and access the corrupt index.
    761   {
    762     sql::ScopedErrorIgnorer ignore_errors;
    763     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    764     ThumbnailDatabase db;
    765     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    766 
    767     // Data for kPageUrl2 was deleted, but the index entry remains,
    768     // this will throw SQLITE_CORRUPT.  The corruption handler will
    769     // recover the database and poison the handle, so the outer call
    770     // fails.
    771     EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
    772 
    773     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    774   }
    775 
    776   // Check that the database is recovered at the SQLite level.
    777   {
    778     sql::Connection raw_db;
    779     EXPECT_TRUE(raw_db.Open(file_name_));
    780     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    781 
    782     // Check that the expected tables exist.
    783     VerifyTablesAndColumns(&raw_db);
    784   }
    785 
    786   // Database should also be recovered at higher levels.
    787   {
    788     ThumbnailDatabase db;
    789     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    790 
    791     // Now this fails because there is no mapping.
    792     EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
    793 
    794     // Other data was retained by recovery.
    795     EXPECT_TRUE(
    796         CheckPageHasIcon(&db, kPageUrl1, chrome::FAVICON,
    797                          kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    798   }
    799 
    800   // Corrupt the database again by adjusting the header.
    801   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
    802 
    803   // Database is unusable at the SQLite level.
    804   {
    805     sql::ScopedErrorIgnorer ignore_errors;
    806     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    807     sql::Connection raw_db;
    808     EXPECT_TRUE(raw_db.Open(file_name_));
    809     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
    810     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    811   }
    812 
    813   // Database should be recovered during open.
    814   {
    815     sql::ScopedErrorIgnorer ignore_errors;
    816     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    817     ThumbnailDatabase db;
    818     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    819 
    820     EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
    821     EXPECT_TRUE(
    822         CheckPageHasIcon(&db, kPageUrl1, chrome::FAVICON,
    823                          kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    824 
    825     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    826   }
    827 }
    828 
    829 TEST_F(ThumbnailDatabaseTest, Recovery6) {
    830   // TODO(shess): See comment at top of Recovery test.
    831   if (!sql::Recovery::FullRecoverySupported())
    832     return;
    833 
    834   // Create an example database without loading into ThumbnailDatabase
    835   // (which would upgrade it).
    836   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v6.sql"));
    837 
    838   // Corrupt the database again by adjusting the header.  This form of
    839   // corruption will cause immediate failures during Open(), before
    840   // the migration code runs, so the version-6 recovery will occur.
    841   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
    842 
    843   // Database is unusable at the SQLite level.
    844   {
    845     sql::ScopedErrorIgnorer ignore_errors;
    846     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    847     sql::Connection raw_db;
    848     EXPECT_TRUE(raw_db.Open(file_name_));
    849     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
    850     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    851   }
    852 
    853   // Database should be recovered during open.
    854   {
    855     sql::ScopedErrorIgnorer ignore_errors;
    856     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    857     ThumbnailDatabase db;
    858     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    859 
    860     // Test that some data is present, copied from
    861     // ThumbnailDatabaseTest.Version6 .
    862     EXPECT_TRUE(
    863         CheckPageHasIcon(&db, kPageUrl3, chrome::FAVICON,
    864                          kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
    865     EXPECT_TRUE(
    866         CheckPageHasIcon(&db, kPageUrl3, chrome::TOUCH_ICON,
    867                          kIconUrl3, kLargeSize, sizeof(kBlob2), kBlob2));
    868 
    869     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    870   }
    871 
    872   // Check that the database is recovered at a SQLite level, and that
    873   // the current schema is in place.
    874   {
    875     sql::Connection raw_db;
    876     EXPECT_TRUE(raw_db.Open(file_name_));
    877     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    878 
    879     // Check that the expected tables exist.
    880     VerifyTablesAndColumns(&raw_db);
    881   }
    882 }
    883 
    884 TEST_F(ThumbnailDatabaseTest, Recovery5) {
    885   // Create an example database without loading into ThumbnailDatabase
    886   // (which would upgrade it).
    887   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v5.sql"));
    888 
    889   // Corrupt the database again by adjusting the header.  This form of
    890   // corruption will cause immediate failures during Open(), before
    891   // the migration code runs, so the version-5 recovery will occur.
    892   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
    893 
    894   // Database is unusable at the SQLite level.
    895   {
    896     sql::ScopedErrorIgnorer ignore_errors;
    897     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    898     sql::Connection raw_db;
    899     EXPECT_TRUE(raw_db.Open(file_name_));
    900     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
    901     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    902   }
    903 
    904   // Database should be recovered during open.
    905   {
    906     sql::ScopedErrorIgnorer ignore_errors;
    907     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    908     ThumbnailDatabase db;
    909     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    910 
    911     // Test that some data is present, copied from
    912     // ThumbnailDatabaseTest.Version5 .
    913     EXPECT_TRUE(
    914         CheckPageHasIcon(&db, kPageUrl3, chrome::FAVICON,
    915                          kIconUrl1, gfx::Size(), sizeof(kBlob1), kBlob1));
    916     EXPECT_TRUE(
    917         CheckPageHasIcon(&db, kPageUrl3, chrome::TOUCH_ICON,
    918                          kIconUrl3, gfx::Size(), sizeof(kBlob2), kBlob2));
    919 
    920     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    921   }
    922 
    923   // Check that the database is recovered at a SQLite level, and that
    924   // the current schema is in place.
    925   {
    926     sql::Connection raw_db;
    927     EXPECT_TRUE(raw_db.Open(file_name_));
    928     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    929 
    930     // Check that the expected tables exist.
    931     VerifyTablesAndColumns(&raw_db);
    932   }
    933 }
    934 
    935 // Test that various broken schema found in the wild can be opened
    936 // successfully, and result in the correct schema.
    937 TEST_F(ThumbnailDatabaseTest, WildSchema) {
    938   base::FilePath sql_path;
    939   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &sql_path));
    940   sql_path = sql_path.AppendASCII("History").AppendASCII("thumbnail_wild");
    941 
    942   base::FileEnumerator fe(
    943       sql_path, false, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.sql"));
    944   for (base::FilePath name = fe.Next(); !name.empty(); name = fe.Next()) {
    945     SCOPED_TRACE(name.BaseName().AsUTF8Unsafe());
    946     // Generate a database path based on the golden's basename.
    947     base::FilePath db_base_name =
    948         name.BaseName().ReplaceExtension(FILE_PATH_LITERAL("db"));
    949     base::FilePath db_path = file_name_.DirName().Append(db_base_name);
    950     ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path, name));
    951 
    952     // All schema flaws should be cleaned up by Init().
    953     // TODO(shess): Differentiate between databases which need Raze()
    954     // and those which can be salvaged.
    955     ThumbnailDatabase db;
    956     ASSERT_EQ(sql::INIT_OK, db.Init(db_path));
    957 
    958     // Verify that the resulting schema is correct, whether it
    959     // involved razing the file or fixing things in place.
    960     VerifyTablesAndColumns(&db.db_);
    961   }
    962 }
    963 
    964 }  // namespace history
    965