Index: chrome/browser/history/thumbnail_database_unittest.cc |
diff --git a/chrome/browser/history/thumbnail_database_unittest.cc b/chrome/browser/history/thumbnail_database_unittest.cc |
index cfebd30416aa45bc3bf6f5308c7bd1303e99a548..fcab1eb7fa84259f24e4dfb98d2792b032b319d8 100644 |
--- a/chrome/browser/history/thumbnail_database_unittest.cc |
+++ b/chrome/browser/history/thumbnail_database_unittest.cc |
@@ -78,8 +78,9 @@ void VerifyTablesAndColumns(sql::Connection* db) { |
// [id], [url], and [icon_type]. |
EXPECT_EQ(3u, sql::test::CountTableColumns(db, "favicons")); |
- // [id], [icon_id], [last_updated], [image_data], [width], and [height]. |
- EXPECT_EQ(6u, sql::test::CountTableColumns(db, "favicon_bitmaps")); |
+ // [id], [icon_id], [last_updated], [image_data], [width], [height] and |
+ // [last_requested]. |
+ EXPECT_EQ(7u, sql::test::CountTableColumns(db, "favicon_bitmaps")); |
// [id], [page_url], and [icon_id]. |
EXPECT_EQ(3u, sql::test::CountTableColumns(db, "icon_mapping")); |
@@ -213,6 +214,39 @@ TEST_F(ThumbnailDatabaseTest, AddIconMapping) { |
EXPECT_EQ(id, icon_mappings.front().icon_id); |
} |
+TEST_F(ThumbnailDatabaseTest, LastRequestedTime) { |
+ ThumbnailDatabase db(NULL); |
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_)); |
+ db.BeginTransaction(); |
+ |
+ std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1)); |
+ scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data)); |
+ |
+ GURL url("http://google.com"); |
+ base::Time now = base::Time::Now(); |
+ favicon_base::FaviconID id = |
+ db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, now, gfx::Size()); |
+ ASSERT_NE(0, id); |
+ |
+ // Fetching the last requested time of a non-existent bitmap should fail. |
+ base::Time last_requested = base::Time::UnixEpoch(); |
+ EXPECT_FALSE(db.GetFaviconBitmap(id + 1, NULL, &last_requested, NULL, NULL)); |
+ EXPECT_EQ(last_requested, base::Time::UnixEpoch()); // Remains unchanged. |
+ |
+ // Fetching the last requested time of a bitmap that has no last request |
+ // should return a null timestamp. |
+ last_requested = base::Time::UnixEpoch(); |
+ EXPECT_TRUE(db.GetFaviconBitmap(id, NULL, &last_requested, NULL, NULL)); |
+ EXPECT_TRUE(last_requested.is_null()); |
+ |
+ // Setting the last requested time of an existing bitmap should succeed, and |
+ // the set time should be returned by the corresponding "Get". |
+ last_requested = base::Time::UnixEpoch(); |
+ EXPECT_TRUE(db.SetFaviconBitmapLastRequestedTime(id, now)); |
+ EXPECT_TRUE(db.GetFaviconBitmap(id, NULL, &last_requested, NULL, NULL)); |
+ EXPECT_EQ(last_requested, now); |
+ } |
+ |
TEST_F(ThumbnailDatabaseTest, UpdateIconMapping) { |
ThumbnailDatabase db(NULL); |
ASSERT_EQ(sql::INIT_OK, db.Init(file_name_)); |
@@ -747,6 +781,42 @@ TEST_F(ThumbnailDatabaseTest, Version7) { |
kBlob2)); |
} |
+// Test loading version 8 database. |
+TEST_F(ThumbnailDatabaseTest, Version8) { |
+ scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v8.sql"); |
+ ASSERT_TRUE(db.get() != NULL); |
+ VerifyTablesAndColumns(&db->db_); |
+ |
+ EXPECT_TRUE(CheckPageHasIcon(db.get(), |
+ kPageUrl1, |
+ favicon_base::FAVICON, |
+ kIconUrl1, |
+ kLargeSize, |
+ sizeof(kBlob1), |
+ kBlob1)); |
+ EXPECT_TRUE(CheckPageHasIcon(db.get(), |
+ kPageUrl2, |
+ favicon_base::FAVICON, |
+ kIconUrl2, |
+ kLargeSize, |
+ sizeof(kBlob2), |
+ kBlob2)); |
+ EXPECT_TRUE(CheckPageHasIcon(db.get(), |
+ kPageUrl3, |
+ favicon_base::FAVICON, |
+ kIconUrl1, |
+ kLargeSize, |
+ sizeof(kBlob1), |
+ kBlob1)); |
+ EXPECT_TRUE(CheckPageHasIcon(db.get(), |
+ kPageUrl3, |
+ favicon_base::TOUCH_ICON, |
+ kIconUrl3, |
+ kLargeSize, |
+ sizeof(kBlob2), |
+ kBlob2)); |
+} |
+ |
TEST_F(ThumbnailDatabaseTest, Recovery) { |
// This code tests the recovery module in concert with Chromium's |
// custom recover virtual table. Under USE_SYSTEM_SQLITE, this is |
@@ -759,7 +829,7 @@ TEST_F(ThumbnailDatabaseTest, Recovery) { |
// Create an example database. |
{ |
- EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v7.sql")); |
+ EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v8.sql")); |
sql::Connection raw_db; |
EXPECT_TRUE(raw_db.Open(file_name_)); |
@@ -884,6 +954,118 @@ TEST_F(ThumbnailDatabaseTest, Recovery) { |
} |
} |
+TEST_F(ThumbnailDatabaseTest, Recovery7) { |
+ // This code tests the recovery module in concert with Chromium's |
+ // custom recover virtual table. Under USE_SYSTEM_SQLITE, this is |
+ // not available. This is detected dynamically because corrupt |
+ // databases still need to be handled, perhaps by Raze(), and the |
+ // recovery module is an obvious layer to abstract that to. |
+ // TODO(shess): Handle that case for real! |
+ if (!sql::Recovery::FullRecoverySupported()) |
+ return; |
+ |
+ // Create an example database without loading into ThumbnailDatabase |
+ // (which would upgrade it). |
+ EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v7.sql")); |
+ |
+ // Corrupt the |icon_mapping.page_url| index by deleting an element |
+ // from the backing table but not the index. |
+ { |
+ sql::Connection raw_db; |
+ EXPECT_TRUE(raw_db.Open(file_name_)); |
+ ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db)); |
+ } |
+ const char kIndexName[] = "icon_mapping_page_url_idx"; |
+ const char kDeleteSql[] = |
+ "DELETE FROM icon_mapping WHERE page_url = 'http://yahoo.com/'"; |
+ EXPECT_TRUE( |
+ sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql)); |
+ |
+ // Database should be corrupt at the SQLite level. |
+ { |
+ sql::Connection raw_db; |
+ EXPECT_TRUE(raw_db.Open(file_name_)); |
+ ASSERT_NE("ok", sql::test::IntegrityCheck(&raw_db)); |
+ } |
+ |
+ // Open the database and access the corrupt index. Note that this upgrades |
+ // the database. |
+ { |
+ sql::ScopedErrorIgnorer ignore_errors; |
+ ignore_errors.IgnoreError(SQLITE_CORRUPT); |
+ ThumbnailDatabase db(NULL); |
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_)); |
+ |
+ // Data for kPageUrl2 was deleted, but the index entry remains, |
+ // this will throw SQLITE_CORRUPT. The corruption handler will |
+ // recover the database and poison the handle, so the outer call |
+ // fails. |
+ EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL)); |
+ |
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors()); |
+ } |
+ |
+ // Check that the database is recovered at the SQLite level. |
+ { |
+ sql::Connection raw_db; |
+ EXPECT_TRUE(raw_db.Open(file_name_)); |
+ ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db)); |
+ |
+ // Check that the expected tables exist. |
+ VerifyTablesAndColumns(&raw_db); |
+ } |
+ |
+ // Database should also be recovered at higher levels. |
+ { |
+ ThumbnailDatabase db(NULL); |
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_)); |
+ |
+ // Now this fails because there is no mapping. |
+ EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL)); |
+ |
+ // Other data was retained by recovery. |
+ EXPECT_TRUE(CheckPageHasIcon(&db, |
+ kPageUrl1, |
+ favicon_base::FAVICON, |
+ kIconUrl1, |
+ kLargeSize, |
+ sizeof(kBlob1), |
+ kBlob1)); |
+ } |
+ |
+ // Corrupt the database again by adjusting the header. |
+ EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_)); |
+ |
+ // Database is unusable at the SQLite level. |
+ { |
+ sql::ScopedErrorIgnorer ignore_errors; |
+ ignore_errors.IgnoreError(SQLITE_CORRUPT); |
+ sql::Connection raw_db; |
+ EXPECT_TRUE(raw_db.Open(file_name_)); |
+ EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check")); |
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors()); |
+ } |
+ |
+ // Database should be recovered during open. |
+ { |
+ sql::ScopedErrorIgnorer ignore_errors; |
+ ignore_errors.IgnoreError(SQLITE_CORRUPT); |
+ ThumbnailDatabase db(NULL); |
+ ASSERT_EQ(sql::INIT_OK, db.Init(file_name_)); |
+ |
+ EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL)); |
+ EXPECT_TRUE(CheckPageHasIcon(&db, |
+ kPageUrl1, |
+ favicon_base::FAVICON, |
+ kIconUrl1, |
+ kLargeSize, |
+ sizeof(kBlob1), |
+ kBlob1)); |
+ |
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors()); |
+ } |
+} |
+ |
TEST_F(ThumbnailDatabaseTest, Recovery6) { |
// TODO(shess): See comment at top of Recovery test. |
if (!sql::Recovery::FullRecoverySupported()) |