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     favicon_base::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   favicon_base::FaviconID id =
    206       db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, time, gfx::Size());
    207   EXPECT_NE(0, id);
    208 
    209   EXPECT_NE(0, db.AddIconMapping(url, id));
    210   std::vector<IconMapping> icon_mappings;
    211   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mappings));
    212   EXPECT_EQ(1u, icon_mappings.size());
    213   EXPECT_EQ(url, icon_mappings.front().page_url);
    214   EXPECT_EQ(id, icon_mappings.front().icon_id);
    215 }
    216 
    217 TEST_F(ThumbnailDatabaseTest, UpdateIconMapping) {
    218   ThumbnailDatabase db;
    219   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    220   db.BeginTransaction();
    221 
    222   GURL url("http://google.com");
    223   favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::TOUCH_ICON);
    224 
    225   EXPECT_LT(0, db.AddIconMapping(url, id));
    226   std::vector<IconMapping> icon_mapping;
    227   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
    228   ASSERT_EQ(1u, icon_mapping.size());
    229   EXPECT_EQ(url, icon_mapping.front().page_url);
    230   EXPECT_EQ(id, icon_mapping.front().icon_id);
    231 
    232   GURL url1("http://www.google.com/");
    233   favicon_base::FaviconID new_id =
    234       db.AddFavicon(url1, favicon_base::TOUCH_ICON);
    235   EXPECT_TRUE(db.UpdateIconMapping(icon_mapping.front().mapping_id, new_id));
    236 
    237   icon_mapping.clear();
    238   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
    239   ASSERT_EQ(1u, icon_mapping.size());
    240   EXPECT_EQ(url, icon_mapping.front().page_url);
    241   EXPECT_EQ(new_id, icon_mapping.front().icon_id);
    242   EXPECT_NE(id, icon_mapping.front().icon_id);
    243 }
    244 
    245 TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
    246   ThumbnailDatabase db;
    247   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    248   db.BeginTransaction();
    249 
    250   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    251   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    252 
    253   GURL url("http://google.com");
    254   favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::TOUCH_ICON);
    255   base::Time time = base::Time::Now();
    256   db.AddFaviconBitmap(id, favicon, time, gfx::Size());
    257   EXPECT_LT(0, db.AddIconMapping(url, id));
    258 
    259   favicon_base::FaviconID id2 = db.AddFavicon(url, favicon_base::FAVICON);
    260   EXPECT_LT(0, db.AddIconMapping(url, id2));
    261   ASSERT_NE(id, id2);
    262 
    263   std::vector<IconMapping> icon_mapping;
    264   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
    265   ASSERT_EQ(2u, icon_mapping.size());
    266   EXPECT_EQ(icon_mapping.front().icon_type, favicon_base::TOUCH_ICON);
    267   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, favicon_base::FAVICON, NULL));
    268 
    269   db.DeleteIconMappings(url);
    270 
    271   EXPECT_FALSE(db.GetIconMappingsForPageURL(url, NULL));
    272   EXPECT_FALSE(db.GetIconMappingsForPageURL(url, favicon_base::FAVICON, NULL));
    273 }
    274 
    275 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) {
    276   ThumbnailDatabase db;
    277   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    278   db.BeginTransaction();
    279 
    280   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    281   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    282 
    283   GURL url("http://google.com");
    284 
    285   favicon_base::FaviconID id1 = db.AddFavicon(url, favicon_base::TOUCH_ICON);
    286   base::Time time = base::Time::Now();
    287   db.AddFaviconBitmap(id1, favicon, time, kSmallSize);
    288   db.AddFaviconBitmap(id1, favicon, time, kLargeSize);
    289   EXPECT_LT(0, db.AddIconMapping(url, id1));
    290 
    291   favicon_base::FaviconID id2 = db.AddFavicon(url, favicon_base::FAVICON);
    292   EXPECT_NE(id1, id2);
    293   db.AddFaviconBitmap(id2, favicon, time, kSmallSize);
    294   EXPECT_LT(0, db.AddIconMapping(url, id2));
    295 
    296   std::vector<IconMapping> icon_mappings;
    297   EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mappings));
    298   ASSERT_EQ(2u, icon_mappings.size());
    299   EXPECT_EQ(id1, icon_mappings[0].icon_id);
    300   EXPECT_EQ(id2, icon_mappings[1].icon_id);
    301 }
    302 
    303 TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrls) {
    304   ThumbnailDatabase db;
    305 
    306   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    307 
    308   db.BeginTransaction();
    309 
    310   // Build a database mapping kPageUrl1 -> kIconUrl1, kPageUrl2 ->
    311   // kIconUrl2, kPageUrl3 -> kIconUrl1, and kPageUrl5 -> kIconUrl5.
    312   // Then retain kPageUrl1, kPageUrl3, and kPageUrl5.  kPageUrl2
    313   // should go away, but the others should be retained correctly.
    314 
    315   // TODO(shess): This would probably make sense as a golden file.
    316 
    317   scoped_refptr<base::RefCountedStaticMemory> favicon1(
    318       new base::RefCountedStaticMemory(kBlob1, sizeof(kBlob1)));
    319   scoped_refptr<base::RefCountedStaticMemory> favicon2(
    320       new base::RefCountedStaticMemory(kBlob2, sizeof(kBlob2)));
    321 
    322   favicon_base::FaviconID kept_id1 =
    323       db.AddFavicon(kIconUrl1, favicon_base::FAVICON);
    324   db.AddFaviconBitmap(kept_id1, favicon1, base::Time::Now(), kLargeSize);
    325   db.AddIconMapping(kPageUrl1, kept_id1);
    326   db.AddIconMapping(kPageUrl3, kept_id1);
    327 
    328   favicon_base::FaviconID unkept_id =
    329       db.AddFavicon(kIconUrl2, favicon_base::FAVICON);
    330   db.AddFaviconBitmap(unkept_id, favicon1, base::Time::Now(), kLargeSize);
    331   db.AddIconMapping(kPageUrl2, unkept_id);
    332 
    333   favicon_base::FaviconID kept_id2 =
    334       db.AddFavicon(kIconUrl5, favicon_base::FAVICON);
    335   db.AddFaviconBitmap(kept_id2, favicon2, base::Time::Now(), kLargeSize);
    336   db.AddIconMapping(kPageUrl5, kept_id2);
    337 
    338   // RetainDataForPageUrls() uses schema manipulations for efficiency.
    339   // Grab a copy of the schema to make sure the final schema matches.
    340   const std::string original_schema = db.db_.GetSchema();
    341 
    342   std::vector<GURL> pages_to_keep;
    343   pages_to_keep.push_back(kPageUrl1);
    344   pages_to_keep.push_back(kPageUrl3);
    345   pages_to_keep.push_back(kPageUrl5);
    346   EXPECT_TRUE(db.RetainDataForPageUrls(pages_to_keep));
    347 
    348   // Mappings from the retained urls should be left.
    349   EXPECT_TRUE(CheckPageHasIcon(&db,
    350                                kPageUrl1,
    351                                favicon_base::FAVICON,
    352                                kIconUrl1,
    353                                kLargeSize,
    354                                sizeof(kBlob1),
    355                                kBlob1));
    356   EXPECT_TRUE(CheckPageHasIcon(&db,
    357                                kPageUrl3,
    358                                favicon_base::FAVICON,
    359                                kIconUrl1,
    360                                kLargeSize,
    361                                sizeof(kBlob1),
    362                                kBlob1));
    363   EXPECT_TRUE(CheckPageHasIcon(&db,
    364                                kPageUrl5,
    365                                favicon_base::FAVICON,
    366                                kIconUrl5,
    367                                kLargeSize,
    368                                sizeof(kBlob2),
    369                                kBlob2));
    370 
    371   // The one not retained should be missing.
    372   EXPECT_FALSE(db.GetFaviconIDForFaviconURL(kPageUrl2, false, NULL));
    373 
    374   // Schema should be the same.
    375   EXPECT_EQ(original_schema, db.db_.GetSchema());
    376 }
    377 
    378 // Tests that deleting a favicon deletes the favicon row and favicon bitmap
    379 // rows from the database.
    380 TEST_F(ThumbnailDatabaseTest, DeleteFavicon) {
    381   ThumbnailDatabase db;
    382   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    383   db.BeginTransaction();
    384 
    385   std::vector<unsigned char> data1(kBlob1, kBlob1 + sizeof(kBlob1));
    386   scoped_refptr<base::RefCountedBytes> favicon1(
    387       new base::RefCountedBytes(data1));
    388   std::vector<unsigned char> data2(kBlob2, kBlob2 + sizeof(kBlob2));
    389   scoped_refptr<base::RefCountedBytes> favicon2(
    390       new base::RefCountedBytes(data2));
    391 
    392   GURL url("http://google.com");
    393   favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::FAVICON);
    394   base::Time last_updated = base::Time::Now();
    395   db.AddFaviconBitmap(id, favicon1, last_updated, kSmallSize);
    396   db.AddFaviconBitmap(id, favicon2, last_updated, kLargeSize);
    397 
    398   EXPECT_TRUE(db.GetFaviconBitmaps(id, NULL));
    399 
    400   EXPECT_TRUE(db.DeleteFavicon(id));
    401   EXPECT_FALSE(db.GetFaviconBitmaps(id, NULL));
    402 }
    403 
    404 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
    405   ThumbnailDatabase db;
    406   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    407   db.BeginTransaction();
    408 
    409   // Add a favicon
    410   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    411   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    412 
    413   GURL page_url("http://google.com");
    414   GURL icon_url("http://google.com/favicon.ico");
    415   base::Time time = base::Time::Now();
    416 
    417   favicon_base::FaviconID id = db.AddFavicon(
    418       icon_url, favicon_base::FAVICON, favicon, time, gfx::Size());
    419   EXPECT_NE(0, db.AddIconMapping(page_url, id));
    420   std::vector<IconMapping> icon_mappings;
    421   EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
    422 
    423   EXPECT_EQ(page_url, icon_mappings.front().page_url);
    424   EXPECT_EQ(id, icon_mappings.front().icon_id);
    425   EXPECT_EQ(favicon_base::FAVICON, icon_mappings.front().icon_type);
    426   EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
    427 
    428   // Add a touch icon
    429   std::vector<unsigned char> data2(kBlob2, kBlob2 + sizeof(kBlob2));
    430   scoped_refptr<base::RefCountedBytes> favicon2 =
    431       new base::RefCountedBytes(data);
    432 
    433   favicon_base::FaviconID id2 = db.AddFavicon(
    434       icon_url, favicon_base::TOUCH_ICON, favicon2, time, gfx::Size());
    435   EXPECT_NE(0, db.AddIconMapping(page_url, id2));
    436 
    437   icon_mappings.clear();
    438   EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
    439 
    440   EXPECT_EQ(page_url, icon_mappings.front().page_url);
    441   EXPECT_EQ(id2, icon_mappings.front().icon_id);
    442   EXPECT_EQ(favicon_base::TOUCH_ICON, icon_mappings.front().icon_type);
    443   EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
    444 
    445   // Add a touch precomposed icon
    446   scoped_refptr<base::RefCountedBytes> favicon3 =
    447       new base::RefCountedBytes(data2);
    448 
    449   favicon_base::FaviconID id3 =
    450       db.AddFavicon(icon_url,
    451                     favicon_base::TOUCH_PRECOMPOSED_ICON,
    452                     favicon3,
    453                     time,
    454                     gfx::Size());
    455   EXPECT_NE(0, db.AddIconMapping(page_url, id3));
    456 
    457   icon_mappings.clear();
    458   EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
    459 
    460   EXPECT_EQ(page_url, icon_mappings.front().page_url);
    461   EXPECT_EQ(id3, icon_mappings.front().icon_id);
    462   EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON,
    463             icon_mappings.front().icon_type);
    464   EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
    465 }
    466 
    467 // Test result of GetIconMappingsForPageURL when an icon type is passed in.
    468 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLWithIconType) {
    469   ThumbnailDatabase db;
    470   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    471   db.BeginTransaction();
    472 
    473   GURL url("http://google.com");
    474   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    475   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    476   base::Time time = base::Time::Now();
    477 
    478   favicon_base::FaviconID id1 =
    479       db.AddFavicon(url, favicon_base::FAVICON, favicon, time, gfx::Size());
    480   EXPECT_NE(0, db.AddIconMapping(url, id1));
    481 
    482   favicon_base::FaviconID id2 =
    483       db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, time, gfx::Size());
    484   EXPECT_NE(0, db.AddIconMapping(url, id2));
    485 
    486   favicon_base::FaviconID id3 =
    487       db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, time, gfx::Size());
    488   EXPECT_NE(0, db.AddIconMapping(url, id3));
    489 
    490   // Only the mappings for favicons of type TOUCH_ICON should be returned as
    491   // TOUCH_ICON is a larger icon type than FAVICON.
    492   std::vector<IconMapping> icon_mappings;
    493   EXPECT_TRUE(db.GetIconMappingsForPageURL(
    494       url,
    495       favicon_base::FAVICON | favicon_base::TOUCH_ICON |
    496           favicon_base::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(db.GetIconMappingsForPageURL(
    509       url, favicon_base::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, favicon_base::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   favicon_base::FaviconID id1 = db.AddFavicon(GURL("http://google.com"),
    535                                               favicon_base::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   favicon_base::FaviconID id2 =
    544       db.AddFavicon(GURL("http://www.google.com/icon"),
    545                     favicon_base::TOUCH_ICON,
    546                     favicon,
    547                     time,
    548                     gfx::Size());
    549   EXPECT_NE(id2, 0);
    550 
    551   // Add 3rd favicon
    552   time = base::Time::Now();
    553   favicon_base::FaviconID id3 =
    554       db.AddFavicon(GURL("http://www.google.com/icon"),
    555                     favicon_base::TOUCH_ICON,
    556                     favicon,
    557                     time,
    558                     gfx::Size());
    559   EXPECT_NE(id3, 0);
    560 
    561   // Add 2 icon mapping
    562   GURL page_url("http://www.google.com");
    563   EXPECT_TRUE(db.AddIconMapping(page_url, id1));
    564   EXPECT_TRUE(db.AddIconMapping(page_url, id2));
    565 
    566   EXPECT_TRUE(db.HasMappingFor(id1));
    567   EXPECT_TRUE(db.HasMappingFor(id2));
    568   EXPECT_FALSE(db.HasMappingFor(id3));
    569 
    570   // Remove all mappings
    571   db.DeleteIconMappings(page_url);
    572   EXPECT_FALSE(db.HasMappingFor(id1));
    573   EXPECT_FALSE(db.HasMappingFor(id2));
    574   EXPECT_FALSE(db.HasMappingFor(id3));
    575 }
    576 
    577 TEST_F(ThumbnailDatabaseTest, CloneIconMappings) {
    578   ThumbnailDatabase db;
    579   ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    580   db.BeginTransaction();
    581 
    582   std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
    583   scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
    584 
    585   // Add a favicon which will have icon_mappings
    586   favicon_base::FaviconID id1 =
    587       db.AddFavicon(GURL("http://google.com"), favicon_base::FAVICON);
    588   EXPECT_NE(0, id1);
    589   base::Time time = base::Time::Now();
    590   db.AddFaviconBitmap(id1, favicon, time, gfx::Size());
    591 
    592   // Add another type of favicon
    593   favicon_base::FaviconID id2 = db.AddFavicon(
    594       GURL("http://www.google.com/icon"), favicon_base::TOUCH_ICON);
    595   EXPECT_NE(0, id2);
    596   time = base::Time::Now();
    597   db.AddFaviconBitmap(id2, favicon, time, gfx::Size());
    598 
    599   // Add 3rd favicon
    600   favicon_base::FaviconID id3 = db.AddFavicon(
    601       GURL("http://www.google.com/icon"), favicon_base::TOUCH_ICON);
    602   EXPECT_NE(0, id3);
    603   time = base::Time::Now();
    604   db.AddFaviconBitmap(id3, favicon, time, gfx::Size());
    605 
    606   GURL page1_url("http://page1.com");
    607   EXPECT_TRUE(db.AddIconMapping(page1_url, id1));
    608   EXPECT_TRUE(db.AddIconMapping(page1_url, id2));
    609 
    610   GURL page2_url("http://page2.com");
    611   EXPECT_TRUE(db.AddIconMapping(page2_url, id3));
    612 
    613   // Test we do nothing with existing mappings.
    614   std::vector<IconMapping> icon_mapping;
    615   EXPECT_TRUE(db.GetIconMappingsForPageURL(page2_url, &icon_mapping));
    616   ASSERT_EQ(1U, icon_mapping.size());
    617 
    618   EXPECT_TRUE(db.CloneIconMappings(page1_url, page2_url));
    619 
    620   icon_mapping.clear();
    621   EXPECT_TRUE(db.GetIconMappingsForPageURL(page2_url, &icon_mapping));
    622   ASSERT_EQ(1U, icon_mapping.size());
    623   EXPECT_EQ(page2_url, icon_mapping[0].page_url);
    624   EXPECT_EQ(id3, icon_mapping[0].icon_id);
    625 
    626   // Test we clone if the new page has no mappings.
    627   GURL page3_url("http://page3.com");
    628   EXPECT_TRUE(db.CloneIconMappings(page1_url, page3_url));
    629 
    630   icon_mapping.clear();
    631   EXPECT_TRUE(db.GetIconMappingsForPageURL(page3_url, &icon_mapping));
    632 
    633   ASSERT_EQ(2U, icon_mapping.size());
    634   if (icon_mapping[0].icon_id == id2)
    635     std::swap(icon_mapping[0], icon_mapping[1]);
    636   EXPECT_EQ(page3_url, icon_mapping[0].page_url);
    637   EXPECT_EQ(id1, icon_mapping[0].icon_id);
    638   EXPECT_EQ(page3_url, icon_mapping[1].page_url);
    639   EXPECT_EQ(id2, icon_mapping[1].icon_id);
    640 }
    641 
    642 // Test loading version 3 database.
    643 TEST_F(ThumbnailDatabaseTest, Version3) {
    644   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v3.sql");
    645   ASSERT_TRUE(db.get() != NULL);
    646   VerifyTablesAndColumns(&db->db_);
    647 
    648   // Version 3 is deprecated, the data should all be gone.
    649   VerifyDatabaseEmpty(&db->db_);
    650 }
    651 
    652 // Test loading version 4 database.
    653 TEST_F(ThumbnailDatabaseTest, Version4) {
    654   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v4.sql");
    655   ASSERT_TRUE(db.get() != NULL);
    656   VerifyTablesAndColumns(&db->db_);
    657 
    658   // Version 4 is deprecated, the data should all be gone.
    659   VerifyDatabaseEmpty(&db->db_);
    660 }
    661 
    662 // Test loading version 5 database.
    663 TEST_F(ThumbnailDatabaseTest, Version5) {
    664   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v5.sql");
    665   ASSERT_TRUE(db.get() != NULL);
    666   VerifyTablesAndColumns(&db->db_);
    667 
    668   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    669                                kPageUrl1,
    670                                favicon_base::FAVICON,
    671                                kIconUrl1,
    672                                gfx::Size(),
    673                                sizeof(kBlob1),
    674                                kBlob1));
    675   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    676                                kPageUrl2,
    677                                favicon_base::FAVICON,
    678                                kIconUrl2,
    679                                gfx::Size(),
    680                                sizeof(kBlob2),
    681                                kBlob2));
    682   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    683                                kPageUrl3,
    684                                favicon_base::FAVICON,
    685                                kIconUrl1,
    686                                gfx::Size(),
    687                                sizeof(kBlob1),
    688                                kBlob1));
    689   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    690                                kPageUrl3,
    691                                favicon_base::TOUCH_ICON,
    692                                kIconUrl3,
    693                                gfx::Size(),
    694                                sizeof(kBlob2),
    695                                kBlob2));
    696 }
    697 
    698 // Test loading version 6 database.
    699 TEST_F(ThumbnailDatabaseTest, Version6) {
    700   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v6.sql");
    701   ASSERT_TRUE(db.get() != NULL);
    702   VerifyTablesAndColumns(&db->db_);
    703 
    704   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    705                                kPageUrl1,
    706                                favicon_base::FAVICON,
    707                                kIconUrl1,
    708                                kLargeSize,
    709                                sizeof(kBlob1),
    710                                kBlob1));
    711   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    712                                kPageUrl2,
    713                                favicon_base::FAVICON,
    714                                kIconUrl2,
    715                                kLargeSize,
    716                                sizeof(kBlob2),
    717                                kBlob2));
    718   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    719                                kPageUrl3,
    720                                favicon_base::FAVICON,
    721                                kIconUrl1,
    722                                kLargeSize,
    723                                sizeof(kBlob1),
    724                                kBlob1));
    725   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    726                                kPageUrl3,
    727                                favicon_base::TOUCH_ICON,
    728                                kIconUrl3,
    729                                kLargeSize,
    730                                sizeof(kBlob2),
    731                                kBlob2));
    732 }
    733 
    734 // Test loading version 7 database.
    735 TEST_F(ThumbnailDatabaseTest, Version7) {
    736   scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v7.sql");
    737   ASSERT_TRUE(db.get() != NULL);
    738   VerifyTablesAndColumns(&db->db_);
    739 
    740   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    741                                kPageUrl1,
    742                                favicon_base::FAVICON,
    743                                kIconUrl1,
    744                                kLargeSize,
    745                                sizeof(kBlob1),
    746                                kBlob1));
    747   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    748                                kPageUrl2,
    749                                favicon_base::FAVICON,
    750                                kIconUrl2,
    751                                kLargeSize,
    752                                sizeof(kBlob2),
    753                                kBlob2));
    754   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    755                                kPageUrl3,
    756                                favicon_base::FAVICON,
    757                                kIconUrl1,
    758                                kLargeSize,
    759                                sizeof(kBlob1),
    760                                kBlob1));
    761   EXPECT_TRUE(CheckPageHasIcon(db.get(),
    762                                kPageUrl3,
    763                                favicon_base::TOUCH_ICON,
    764                                kIconUrl3,
    765                                kLargeSize,
    766                                sizeof(kBlob2),
    767                                kBlob2));
    768 }
    769 
    770 TEST_F(ThumbnailDatabaseTest, Recovery) {
    771   // This code tests the recovery module in concert with Chromium's
    772   // custom recover virtual table.  Under USE_SYSTEM_SQLITE, this is
    773   // not available.  This is detected dynamically because corrupt
    774   // databases still need to be handled, perhaps by Raze(), and the
    775   // recovery module is an obvious layer to abstract that to.
    776   // TODO(shess): Handle that case for real!
    777   if (!sql::Recovery::FullRecoverySupported())
    778     return;
    779 
    780   // Create an example database.
    781   {
    782     EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v7.sql"));
    783 
    784     sql::Connection raw_db;
    785     EXPECT_TRUE(raw_db.Open(file_name_));
    786     VerifyTablesAndColumns(&raw_db);
    787   }
    788 
    789   // Test that the contents make sense after clean open.
    790   {
    791     ThumbnailDatabase db;
    792     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    793 
    794     EXPECT_TRUE(CheckPageHasIcon(&db,
    795                                  kPageUrl1,
    796                                  favicon_base::FAVICON,
    797                                  kIconUrl1,
    798                                  kLargeSize,
    799                                  sizeof(kBlob1),
    800                                  kBlob1));
    801     EXPECT_TRUE(CheckPageHasIcon(&db,
    802                                  kPageUrl2,
    803                                  favicon_base::FAVICON,
    804                                  kIconUrl2,
    805                                  kLargeSize,
    806                                  sizeof(kBlob2),
    807                                  kBlob2));
    808   }
    809 
    810   // Corrupt the |icon_mapping.page_url| index by deleting an element
    811   // from the backing table but not the index.
    812   {
    813     sql::Connection raw_db;
    814     EXPECT_TRUE(raw_db.Open(file_name_));
    815     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    816   }
    817   const char kIndexName[] = "icon_mapping_page_url_idx";
    818   const char kDeleteSql[] =
    819       "DELETE FROM icon_mapping WHERE page_url = 'http://yahoo.com/'";
    820   EXPECT_TRUE(
    821       sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql));
    822 
    823   // Database should be corrupt at the SQLite level.
    824   {
    825     sql::Connection raw_db;
    826     EXPECT_TRUE(raw_db.Open(file_name_));
    827     ASSERT_NE("ok", sql::test::IntegrityCheck(&raw_db));
    828   }
    829 
    830   // Open the database and access the corrupt index.
    831   {
    832     sql::ScopedErrorIgnorer ignore_errors;
    833     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    834     ThumbnailDatabase db;
    835     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    836 
    837     // Data for kPageUrl2 was deleted, but the index entry remains,
    838     // this will throw SQLITE_CORRUPT.  The corruption handler will
    839     // recover the database and poison the handle, so the outer call
    840     // fails.
    841     EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
    842 
    843     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    844   }
    845 
    846   // Check that the database is recovered at the SQLite level.
    847   {
    848     sql::Connection raw_db;
    849     EXPECT_TRUE(raw_db.Open(file_name_));
    850     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    851 
    852     // Check that the expected tables exist.
    853     VerifyTablesAndColumns(&raw_db);
    854   }
    855 
    856   // Database should also be recovered at higher levels.
    857   {
    858     ThumbnailDatabase db;
    859     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    860 
    861     // Now this fails because there is no mapping.
    862     EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
    863 
    864     // Other data was retained by recovery.
    865     EXPECT_TRUE(CheckPageHasIcon(&db,
    866                                  kPageUrl1,
    867                                  favicon_base::FAVICON,
    868                                  kIconUrl1,
    869                                  kLargeSize,
    870                                  sizeof(kBlob1),
    871                                  kBlob1));
    872   }
    873 
    874   // Corrupt the database again by adjusting the header.
    875   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
    876 
    877   // Database is unusable at the SQLite level.
    878   {
    879     sql::ScopedErrorIgnorer ignore_errors;
    880     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    881     sql::Connection raw_db;
    882     EXPECT_TRUE(raw_db.Open(file_name_));
    883     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
    884     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    885   }
    886 
    887   // Database should be recovered during open.
    888   {
    889     sql::ScopedErrorIgnorer ignore_errors;
    890     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    891     ThumbnailDatabase db;
    892     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    893 
    894     EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
    895     EXPECT_TRUE(CheckPageHasIcon(&db,
    896                                  kPageUrl1,
    897                                  favicon_base::FAVICON,
    898                                  kIconUrl1,
    899                                  kLargeSize,
    900                                  sizeof(kBlob1),
    901                                  kBlob1));
    902 
    903     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    904   }
    905 }
    906 
    907 TEST_F(ThumbnailDatabaseTest, Recovery6) {
    908   // TODO(shess): See comment at top of Recovery test.
    909   if (!sql::Recovery::FullRecoverySupported())
    910     return;
    911 
    912   // Create an example database without loading into ThumbnailDatabase
    913   // (which would upgrade it).
    914   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v6.sql"));
    915 
    916   // Corrupt the database by adjusting the header.  This form of corruption will
    917   // cause immediate failures during Open(), before the migration code runs, so
    918   // the recovery code will run.
    919   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
    920 
    921   // Database is unusable at the SQLite level.
    922   {
    923     sql::ScopedErrorIgnorer ignore_errors;
    924     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    925     sql::Connection raw_db;
    926     EXPECT_TRUE(raw_db.Open(file_name_));
    927     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
    928     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    929   }
    930 
    931   // Database open should succeed.
    932   {
    933     sql::ScopedErrorIgnorer ignore_errors;
    934     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    935     ThumbnailDatabase db;
    936     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    937     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    938   }
    939 
    940   // The database should be usable at the SQLite level, with a current schema
    941   // and no data.
    942   {
    943     sql::Connection raw_db;
    944     EXPECT_TRUE(raw_db.Open(file_name_));
    945     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    946 
    947     // Check that the expected tables exist.
    948     VerifyTablesAndColumns(&raw_db);
    949 
    950     // Version 6 recovery is deprecated, the data should all be gone.
    951     VerifyDatabaseEmpty(&raw_db);
    952   }
    953 }
    954 
    955 TEST_F(ThumbnailDatabaseTest, Recovery5) {
    956   // TODO(shess): See comment at top of Recovery test.
    957   if (!sql::Recovery::FullRecoverySupported())
    958     return;
    959 
    960   // Create an example database without loading into ThumbnailDatabase
    961   // (which would upgrade it).
    962   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v5.sql"));
    963 
    964   // Corrupt the database by adjusting the header.  This form of corruption will
    965   // cause immediate failures during Open(), before the migration code runs, so
    966   // the recovery code will run.
    967   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
    968 
    969   // Database is unusable at the SQLite level.
    970   {
    971     sql::ScopedErrorIgnorer ignore_errors;
    972     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    973     sql::Connection raw_db;
    974     EXPECT_TRUE(raw_db.Open(file_name_));
    975     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
    976     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    977   }
    978 
    979   // Database open should succeed.
    980   {
    981     sql::ScopedErrorIgnorer ignore_errors;
    982     ignore_errors.IgnoreError(SQLITE_CORRUPT);
    983     ThumbnailDatabase db;
    984     ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
    985     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
    986   }
    987 
    988   // The database should be usable at the SQLite level, with a current schema
    989   // and no data.
    990   {
    991     sql::Connection raw_db;
    992     EXPECT_TRUE(raw_db.Open(file_name_));
    993     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
    994 
    995     // Check that the expected tables exist.
    996     VerifyTablesAndColumns(&raw_db);
    997 
    998     // Version 5 recovery is deprecated, the data should all be gone.
    999     VerifyDatabaseEmpty(&raw_db);
   1000   }
   1001 }
   1002 
   1003 // Test that various broken schema found in the wild can be opened
   1004 // successfully, and result in the correct schema.
   1005 TEST_F(ThumbnailDatabaseTest, WildSchema) {
   1006   base::FilePath sql_path;
   1007   ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &sql_path));
   1008   sql_path = sql_path.AppendASCII("History").AppendASCII("thumbnail_wild");
   1009 
   1010   base::FileEnumerator fe(
   1011       sql_path, false, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.sql"));
   1012   for (base::FilePath name = fe.Next(); !name.empty(); name = fe.Next()) {
   1013     SCOPED_TRACE(name.BaseName().AsUTF8Unsafe());
   1014     // Generate a database path based on the golden's basename.
   1015     base::FilePath db_base_name =
   1016         name.BaseName().ReplaceExtension(FILE_PATH_LITERAL("db"));
   1017     base::FilePath db_path = file_name_.DirName().Append(db_base_name);
   1018     ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path, name));
   1019 
   1020     // All schema flaws should be cleaned up by Init().
   1021     // TODO(shess): Differentiate between databases which need Raze()
   1022     // and those which can be salvaged.
   1023     ThumbnailDatabase db;
   1024     ASSERT_EQ(sql::INIT_OK, db.Init(db_path));
   1025 
   1026     // Verify that the resulting schema is correct, whether it
   1027     // involved razing the file or fixing things in place.
   1028     VerifyTablesAndColumns(&db.db_);
   1029   }
   1030 }
   1031 
   1032 }  // namespace history
   1033