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.
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"
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";
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/");
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");
48 const gfx::Size kSmallSize = gfx::Size(16, 16);
49 const gfx::Size kLargeSize = gfx::Size(32, 32);
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))
58 sql_path = sql_path.AppendASCII("History").AppendASCII(ascii_path);
59 return sql::test::CreateDatabaseFromSQL(db_path, sql_path);
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));
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));
76 EXPECT_EQ(2u, sql::test::CountTableColumns(db, "meta"));
78 // [id], [url], and [icon_type].
79 EXPECT_EQ(3u, sql::test::CountTableColumns(db, "favicons"));
81 // [id], [icon_id], [last_updated], [image_data], [width], and [height].
82 EXPECT_EQ(6u, sql::test::CountTableColumns(db, "favicon_bitmaps"));
84 // [id], [page_url], and [icon_id].
85 EXPECT_EQ(3u, sql::test::CountTableColumns(db, "icon_mapping"));
88 void VerifyDatabaseEmpty(sql::Connection* db) {
90 EXPECT_TRUE(sql::test::CountTableRows(db, "favicons", &rows));
92 EXPECT_TRUE(sql::test::CountTableRows(db, "favicon_bitmaps", &rows));
94 EXPECT_TRUE(sql::test::CountTableRows(db, "icon_mapping", &rows));
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()";
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)
119 if (iter == icon_mappings.end()) {
120 ADD_FAILURE() << "failed to find |expected_icon_type|";
124 if (expected_icon_url != iter->icon_url) {
125 EXPECT_EQ(expected_icon_url, iter->icon_url);
129 std::vector<FaviconBitmap> favicon_bitmaps;
130 if (!db->GetFaviconBitmaps(iter->icon_id, &favicon_bitmaps)) {
131 ADD_FAILURE() << "failed GetFaviconBitmaps()";
135 if (1 != favicon_bitmaps.size()) {
136 EXPECT_EQ(1u, favicon_bitmaps.size());
140 if (expected_icon_size != favicon_bitmaps[0].pixel_size) {
141 EXPECT_EQ(expected_icon_size, favicon_bitmaps[0].pixel_size);
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());
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|";
161 class ThumbnailDatabaseTest : public testing::Test {
163 ThumbnailDatabaseTest() {
165 ~ThumbnailDatabaseTest() override {}
167 // Initialize a thumbnail database instance from the SQL file at
168 // |golden_path| in the "History/" subdirectory of test data.
169 scoped_ptr<ThumbnailDatabase> LoadFromGolden(const char* golden_path) {
170 if (!CreateDatabaseFromSQL(file_name_, golden_path)) {
171 ADD_FAILURE() << "Failed loading " << golden_path;
172 return scoped_ptr<ThumbnailDatabase>();
175 scoped_ptr<ThumbnailDatabase> db(new ThumbnailDatabase(NULL));
176 EXPECT_EQ(sql::INIT_OK, db->Init(file_name_));
177 db->BeginTransaction();
183 void SetUp() override {
184 // Get a temporary directory for the test DB files.
185 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
187 file_name_ = temp_dir_.path().AppendASCII("TestFavicons.db");
190 base::ScopedTempDir temp_dir_;
191 base::FilePath file_name_;
194 TEST_F(ThumbnailDatabaseTest, AddIconMapping) {
195 ThumbnailDatabase db(NULL);
196 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
197 db.BeginTransaction();
199 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
200 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
202 GURL url("http://google.com");
203 base::Time time = base::Time::Now();
204 favicon_base::FaviconID id =
205 db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, time, gfx::Size());
208 EXPECT_NE(0, db.AddIconMapping(url, id));
209 std::vector<IconMapping> icon_mappings;
210 EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mappings));
211 EXPECT_EQ(1u, icon_mappings.size());
212 EXPECT_EQ(url, icon_mappings.front().page_url);
213 EXPECT_EQ(id, icon_mappings.front().icon_id);
216 TEST_F(ThumbnailDatabaseTest, UpdateIconMapping) {
217 ThumbnailDatabase db(NULL);
218 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
219 db.BeginTransaction();
221 GURL url("http://google.com");
222 favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::TOUCH_ICON);
224 EXPECT_LT(0, db.AddIconMapping(url, id));
225 std::vector<IconMapping> icon_mapping;
226 EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
227 ASSERT_EQ(1u, icon_mapping.size());
228 EXPECT_EQ(url, icon_mapping.front().page_url);
229 EXPECT_EQ(id, icon_mapping.front().icon_id);
231 GURL url1("http://www.google.com/");
232 favicon_base::FaviconID new_id =
233 db.AddFavicon(url1, favicon_base::TOUCH_ICON);
234 EXPECT_TRUE(db.UpdateIconMapping(icon_mapping.front().mapping_id, new_id));
236 icon_mapping.clear();
237 EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
238 ASSERT_EQ(1u, icon_mapping.size());
239 EXPECT_EQ(url, icon_mapping.front().page_url);
240 EXPECT_EQ(new_id, icon_mapping.front().icon_id);
241 EXPECT_NE(id, icon_mapping.front().icon_id);
244 TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
245 ThumbnailDatabase db(NULL);
246 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
247 db.BeginTransaction();
249 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
250 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
252 GURL url("http://google.com");
253 favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::TOUCH_ICON);
254 base::Time time = base::Time::Now();
255 db.AddFaviconBitmap(id, favicon, time, gfx::Size());
256 EXPECT_LT(0, db.AddIconMapping(url, id));
258 favicon_base::FaviconID id2 = db.AddFavicon(url, favicon_base::FAVICON);
259 EXPECT_LT(0, db.AddIconMapping(url, id2));
262 std::vector<IconMapping> icon_mapping;
263 EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
264 ASSERT_EQ(2u, icon_mapping.size());
265 EXPECT_EQ(icon_mapping.front().icon_type, favicon_base::TOUCH_ICON);
266 EXPECT_TRUE(db.GetIconMappingsForPageURL(url, favicon_base::FAVICON, NULL));
268 db.DeleteIconMappings(url);
270 EXPECT_FALSE(db.GetIconMappingsForPageURL(url, NULL));
271 EXPECT_FALSE(db.GetIconMappingsForPageURL(url, favicon_base::FAVICON, NULL));
274 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) {
275 ThumbnailDatabase db(NULL);
276 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
277 db.BeginTransaction();
279 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
280 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
282 GURL url("http://google.com");
284 favicon_base::FaviconID id1 = db.AddFavicon(url, favicon_base::TOUCH_ICON);
285 base::Time time = base::Time::Now();
286 db.AddFaviconBitmap(id1, favicon, time, kSmallSize);
287 db.AddFaviconBitmap(id1, favicon, time, kLargeSize);
288 EXPECT_LT(0, db.AddIconMapping(url, id1));
290 favicon_base::FaviconID id2 = db.AddFavicon(url, favicon_base::FAVICON);
292 db.AddFaviconBitmap(id2, favicon, time, kSmallSize);
293 EXPECT_LT(0, db.AddIconMapping(url, id2));
295 std::vector<IconMapping> icon_mappings;
296 EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mappings));
297 ASSERT_EQ(2u, icon_mappings.size());
298 EXPECT_EQ(id1, icon_mappings[0].icon_id);
299 EXPECT_EQ(id2, icon_mappings[1].icon_id);
302 TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrls) {
303 ThumbnailDatabase db(NULL);
305 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
307 db.BeginTransaction();
309 // Build a database mapping kPageUrl1 -> kIconUrl1, kPageUrl2 ->
310 // kIconUrl2, kPageUrl3 -> kIconUrl1, and kPageUrl5 -> kIconUrl5.
311 // Then retain kPageUrl1, kPageUrl3, and kPageUrl5. kPageUrl2
312 // should go away, but the others should be retained correctly.
314 // TODO(shess): This would probably make sense as a golden file.
316 scoped_refptr<base::RefCountedStaticMemory> favicon1(
317 new base::RefCountedStaticMemory(kBlob1, sizeof(kBlob1)));
318 scoped_refptr<base::RefCountedStaticMemory> favicon2(
319 new base::RefCountedStaticMemory(kBlob2, sizeof(kBlob2)));
321 favicon_base::FaviconID kept_id1 =
322 db.AddFavicon(kIconUrl1, favicon_base::FAVICON);
323 db.AddFaviconBitmap(kept_id1, favicon1, base::Time::Now(), kLargeSize);
324 db.AddIconMapping(kPageUrl1, kept_id1);
325 db.AddIconMapping(kPageUrl3, kept_id1);
327 favicon_base::FaviconID unkept_id =
328 db.AddFavicon(kIconUrl2, favicon_base::FAVICON);
329 db.AddFaviconBitmap(unkept_id, favicon1, base::Time::Now(), kLargeSize);
330 db.AddIconMapping(kPageUrl2, unkept_id);
332 favicon_base::FaviconID kept_id2 =
333 db.AddFavicon(kIconUrl5, favicon_base::FAVICON);
334 db.AddFaviconBitmap(kept_id2, favicon2, base::Time::Now(), kLargeSize);
335 db.AddIconMapping(kPageUrl5, kept_id2);
337 // RetainDataForPageUrls() uses schema manipulations for efficiency.
338 // Grab a copy of the schema to make sure the final schema matches.
339 const std::string original_schema = db.db_.GetSchema();
341 std::vector<GURL> pages_to_keep;
342 pages_to_keep.push_back(kPageUrl1);
343 pages_to_keep.push_back(kPageUrl3);
344 pages_to_keep.push_back(kPageUrl5);
345 EXPECT_TRUE(db.RetainDataForPageUrls(pages_to_keep));
347 // Mappings from the retained urls should be left.
348 EXPECT_TRUE(CheckPageHasIcon(&db,
350 favicon_base::FAVICON,
355 EXPECT_TRUE(CheckPageHasIcon(&db,
357 favicon_base::FAVICON,
362 EXPECT_TRUE(CheckPageHasIcon(&db,
364 favicon_base::FAVICON,
370 // The one not retained should be missing.
371 EXPECT_FALSE(db.GetFaviconIDForFaviconURL(kPageUrl2, false, NULL));
373 // Schema should be the same.
374 EXPECT_EQ(original_schema, db.db_.GetSchema());
377 // Tests that deleting a favicon deletes the favicon row and favicon bitmap
378 // rows from the database.
379 TEST_F(ThumbnailDatabaseTest, DeleteFavicon) {
380 ThumbnailDatabase db(NULL);
381 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
382 db.BeginTransaction();
384 std::vector<unsigned char> data1(kBlob1, kBlob1 + sizeof(kBlob1));
385 scoped_refptr<base::RefCountedBytes> favicon1(
386 new base::RefCountedBytes(data1));
387 std::vector<unsigned char> data2(kBlob2, kBlob2 + sizeof(kBlob2));
388 scoped_refptr<base::RefCountedBytes> favicon2(
389 new base::RefCountedBytes(data2));
391 GURL url("http://google.com");
392 favicon_base::FaviconID id = db.AddFavicon(url, favicon_base::FAVICON);
393 base::Time last_updated = base::Time::Now();
394 db.AddFaviconBitmap(id, favicon1, last_updated, kSmallSize);
395 db.AddFaviconBitmap(id, favicon2, last_updated, kLargeSize);
397 EXPECT_TRUE(db.GetFaviconBitmaps(id, NULL));
399 EXPECT_TRUE(db.DeleteFavicon(id));
400 EXPECT_FALSE(db.GetFaviconBitmaps(id, NULL));
403 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
404 ThumbnailDatabase db(NULL);
405 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
406 db.BeginTransaction();
409 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
410 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
412 GURL page_url("http://google.com");
413 GURL icon_url("http://google.com/favicon.ico");
414 base::Time time = base::Time::Now();
416 favicon_base::FaviconID id = db.AddFavicon(
417 icon_url, favicon_base::FAVICON, favicon, time, gfx::Size());
418 EXPECT_NE(0, db.AddIconMapping(page_url, id));
419 std::vector<IconMapping> icon_mappings;
420 EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
422 EXPECT_EQ(page_url, icon_mappings.front().page_url);
423 EXPECT_EQ(id, icon_mappings.front().icon_id);
424 EXPECT_EQ(favicon_base::FAVICON, icon_mappings.front().icon_type);
425 EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
428 std::vector<unsigned char> data2(kBlob2, kBlob2 + sizeof(kBlob2));
429 scoped_refptr<base::RefCountedBytes> favicon2 =
430 new base::RefCountedBytes(data);
432 favicon_base::FaviconID id2 = db.AddFavicon(
433 icon_url, favicon_base::TOUCH_ICON, favicon2, time, gfx::Size());
434 EXPECT_NE(0, db.AddIconMapping(page_url, id2));
436 icon_mappings.clear();
437 EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
439 EXPECT_EQ(page_url, icon_mappings.front().page_url);
440 EXPECT_EQ(id2, icon_mappings.front().icon_id);
441 EXPECT_EQ(favicon_base::TOUCH_ICON, icon_mappings.front().icon_type);
442 EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
444 // Add a touch precomposed icon
445 scoped_refptr<base::RefCountedBytes> favicon3 =
446 new base::RefCountedBytes(data2);
448 favicon_base::FaviconID id3 =
449 db.AddFavicon(icon_url,
450 favicon_base::TOUCH_PRECOMPOSED_ICON,
454 EXPECT_NE(0, db.AddIconMapping(page_url, id3));
456 icon_mappings.clear();
457 EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
459 EXPECT_EQ(page_url, icon_mappings.front().page_url);
460 EXPECT_EQ(id3, icon_mappings.front().icon_id);
461 EXPECT_EQ(favicon_base::TOUCH_PRECOMPOSED_ICON,
462 icon_mappings.front().icon_type);
463 EXPECT_EQ(icon_url, icon_mappings.front().icon_url);
466 // Test result of GetIconMappingsForPageURL when an icon type is passed in.
467 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLWithIconType) {
468 ThumbnailDatabase db(NULL);
469 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
470 db.BeginTransaction();
472 GURL url("http://google.com");
473 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
474 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
475 base::Time time = base::Time::Now();
477 favicon_base::FaviconID id1 =
478 db.AddFavicon(url, favicon_base::FAVICON, favicon, time, gfx::Size());
479 EXPECT_NE(0, db.AddIconMapping(url, id1));
481 favicon_base::FaviconID id2 =
482 db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, time, gfx::Size());
483 EXPECT_NE(0, db.AddIconMapping(url, id2));
485 favicon_base::FaviconID id3 =
486 db.AddFavicon(url, favicon_base::TOUCH_ICON, favicon, time, gfx::Size());
487 EXPECT_NE(0, db.AddIconMapping(url, id3));
489 // Only the mappings for favicons of type TOUCH_ICON should be returned as
490 // TOUCH_ICON is a larger icon type than FAVICON.
491 std::vector<IconMapping> icon_mappings;
492 EXPECT_TRUE(db.GetIconMappingsForPageURL(
494 favicon_base::FAVICON | favicon_base::TOUCH_ICON |
495 favicon_base::TOUCH_PRECOMPOSED_ICON,
498 EXPECT_EQ(2u, icon_mappings.size());
499 if (id2 == icon_mappings[0].icon_id) {
500 EXPECT_EQ(id3, icon_mappings[1].icon_id);
502 EXPECT_EQ(id3, icon_mappings[0].icon_id);
503 EXPECT_EQ(id2, icon_mappings[1].icon_id);
506 icon_mappings.clear();
507 EXPECT_TRUE(db.GetIconMappingsForPageURL(
508 url, favicon_base::TOUCH_ICON, &icon_mappings));
509 if (id2 == icon_mappings[0].icon_id) {
510 EXPECT_EQ(id3, icon_mappings[1].icon_id);
512 EXPECT_EQ(id3, icon_mappings[0].icon_id);
513 EXPECT_EQ(id2, icon_mappings[1].icon_id);
516 icon_mappings.clear();
518 db.GetIconMappingsForPageURL(url, favicon_base::FAVICON, &icon_mappings));
519 EXPECT_EQ(1u, icon_mappings.size());
520 EXPECT_EQ(id1, icon_mappings[0].icon_id);
523 TEST_F(ThumbnailDatabaseTest, HasMappingFor) {
524 ThumbnailDatabase db(NULL);
525 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
526 db.BeginTransaction();
528 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
529 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
531 // Add a favicon which will have icon_mappings
532 base::Time time = base::Time::Now();
533 favicon_base::FaviconID id1 = db.AddFavicon(GURL("http://google.com"),
534 favicon_base::FAVICON,
540 // Add another type of favicon
541 time = base::Time::Now();
542 favicon_base::FaviconID id2 =
543 db.AddFavicon(GURL("http://www.google.com/icon"),
544 favicon_base::TOUCH_ICON,
551 time = base::Time::Now();
552 favicon_base::FaviconID id3 =
553 db.AddFavicon(GURL("http://www.google.com/icon"),
554 favicon_base::TOUCH_ICON,
560 // Add 2 icon mapping
561 GURL page_url("http://www.google.com");
562 EXPECT_TRUE(db.AddIconMapping(page_url, id1));
563 EXPECT_TRUE(db.AddIconMapping(page_url, id2));
565 EXPECT_TRUE(db.HasMappingFor(id1));
566 EXPECT_TRUE(db.HasMappingFor(id2));
567 EXPECT_FALSE(db.HasMappingFor(id3));
569 // Remove all mappings
570 db.DeleteIconMappings(page_url);
571 EXPECT_FALSE(db.HasMappingFor(id1));
572 EXPECT_FALSE(db.HasMappingFor(id2));
573 EXPECT_FALSE(db.HasMappingFor(id3));
576 TEST_F(ThumbnailDatabaseTest, CloneIconMappings) {
577 ThumbnailDatabase db(NULL);
578 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
579 db.BeginTransaction();
581 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
582 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
584 // Add a favicon which will have icon_mappings
585 favicon_base::FaviconID id1 =
586 db.AddFavicon(GURL("http://google.com"), favicon_base::FAVICON);
588 base::Time time = base::Time::Now();
589 db.AddFaviconBitmap(id1, favicon, time, gfx::Size());
591 // Add another type of favicon
592 favicon_base::FaviconID id2 = db.AddFavicon(
593 GURL("http://www.google.com/icon"), favicon_base::TOUCH_ICON);
595 time = base::Time::Now();
596 db.AddFaviconBitmap(id2, favicon, time, gfx::Size());
599 favicon_base::FaviconID id3 = db.AddFavicon(
600 GURL("http://www.google.com/icon"), favicon_base::TOUCH_ICON);
602 time = base::Time::Now();
603 db.AddFaviconBitmap(id3, favicon, time, gfx::Size());
605 GURL page1_url("http://page1.com");
606 EXPECT_TRUE(db.AddIconMapping(page1_url, id1));
607 EXPECT_TRUE(db.AddIconMapping(page1_url, id2));
609 GURL page2_url("http://page2.com");
610 EXPECT_TRUE(db.AddIconMapping(page2_url, id3));
612 // Test we do nothing with existing mappings.
613 std::vector<IconMapping> icon_mapping;
614 EXPECT_TRUE(db.GetIconMappingsForPageURL(page2_url, &icon_mapping));
615 ASSERT_EQ(1U, icon_mapping.size());
617 EXPECT_TRUE(db.CloneIconMappings(page1_url, page2_url));
619 icon_mapping.clear();
620 EXPECT_TRUE(db.GetIconMappingsForPageURL(page2_url, &icon_mapping));
621 ASSERT_EQ(1U, icon_mapping.size());
622 EXPECT_EQ(page2_url, icon_mapping[0].page_url);
623 EXPECT_EQ(id3, icon_mapping[0].icon_id);
625 // Test we clone if the new page has no mappings.
626 GURL page3_url("http://page3.com");
627 EXPECT_TRUE(db.CloneIconMappings(page1_url, page3_url));
629 icon_mapping.clear();
630 EXPECT_TRUE(db.GetIconMappingsForPageURL(page3_url, &icon_mapping));
632 ASSERT_EQ(2U, icon_mapping.size());
633 if (icon_mapping[0].icon_id == id2)
634 std::swap(icon_mapping[0], icon_mapping[1]);
635 EXPECT_EQ(page3_url, icon_mapping[0].page_url);
636 EXPECT_EQ(id1, icon_mapping[0].icon_id);
637 EXPECT_EQ(page3_url, icon_mapping[1].page_url);
638 EXPECT_EQ(id2, icon_mapping[1].icon_id);
641 // Test loading version 3 database.
642 TEST_F(ThumbnailDatabaseTest, Version3) {
643 scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v3.sql");
644 ASSERT_TRUE(db.get() != NULL);
645 VerifyTablesAndColumns(&db->db_);
647 // Version 3 is deprecated, the data should all be gone.
648 VerifyDatabaseEmpty(&db->db_);
651 // Test loading version 4 database.
652 TEST_F(ThumbnailDatabaseTest, Version4) {
653 scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v4.sql");
654 ASSERT_TRUE(db.get() != NULL);
655 VerifyTablesAndColumns(&db->db_);
657 // Version 4 is deprecated, the data should all be gone.
658 VerifyDatabaseEmpty(&db->db_);
661 // Test loading version 5 database.
662 TEST_F(ThumbnailDatabaseTest, Version5) {
663 scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v5.sql");
664 ASSERT_TRUE(db.get() != NULL);
665 VerifyTablesAndColumns(&db->db_);
667 EXPECT_TRUE(CheckPageHasIcon(db.get(),
669 favicon_base::FAVICON,
674 EXPECT_TRUE(CheckPageHasIcon(db.get(),
676 favicon_base::FAVICON,
681 EXPECT_TRUE(CheckPageHasIcon(db.get(),
683 favicon_base::FAVICON,
688 EXPECT_TRUE(CheckPageHasIcon(db.get(),
690 favicon_base::TOUCH_ICON,
697 // Test loading version 6 database.
698 TEST_F(ThumbnailDatabaseTest, Version6) {
699 scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v6.sql");
700 ASSERT_TRUE(db.get() != NULL);
701 VerifyTablesAndColumns(&db->db_);
703 EXPECT_TRUE(CheckPageHasIcon(db.get(),
705 favicon_base::FAVICON,
710 EXPECT_TRUE(CheckPageHasIcon(db.get(),
712 favicon_base::FAVICON,
717 EXPECT_TRUE(CheckPageHasIcon(db.get(),
719 favicon_base::FAVICON,
724 EXPECT_TRUE(CheckPageHasIcon(db.get(),
726 favicon_base::TOUCH_ICON,
733 // Test loading version 7 database.
734 TEST_F(ThumbnailDatabaseTest, Version7) {
735 scoped_ptr<ThumbnailDatabase> db = LoadFromGolden("Favicons.v7.sql");
736 ASSERT_TRUE(db.get() != NULL);
737 VerifyTablesAndColumns(&db->db_);
739 EXPECT_TRUE(CheckPageHasIcon(db.get(),
741 favicon_base::FAVICON,
746 EXPECT_TRUE(CheckPageHasIcon(db.get(),
748 favicon_base::FAVICON,
753 EXPECT_TRUE(CheckPageHasIcon(db.get(),
755 favicon_base::FAVICON,
760 EXPECT_TRUE(CheckPageHasIcon(db.get(),
762 favicon_base::TOUCH_ICON,
769 TEST_F(ThumbnailDatabaseTest, Recovery) {
770 // This code tests the recovery module in concert with Chromium's
771 // custom recover virtual table. Under USE_SYSTEM_SQLITE, this is
772 // not available. This is detected dynamically because corrupt
773 // databases still need to be handled, perhaps by Raze(), and the
774 // recovery module is an obvious layer to abstract that to.
775 // TODO(shess): Handle that case for real!
776 if (!sql::Recovery::FullRecoverySupported())
779 // Create an example database.
781 EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v7.sql"));
783 sql::Connection raw_db;
784 EXPECT_TRUE(raw_db.Open(file_name_));
785 VerifyTablesAndColumns(&raw_db);
788 // Test that the contents make sense after clean open.
790 ThumbnailDatabase db(NULL);
791 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
793 EXPECT_TRUE(CheckPageHasIcon(&db,
795 favicon_base::FAVICON,
800 EXPECT_TRUE(CheckPageHasIcon(&db,
802 favicon_base::FAVICON,
809 // Corrupt the |icon_mapping.page_url| index by deleting an element
810 // from the backing table but not the index.
812 sql::Connection raw_db;
813 EXPECT_TRUE(raw_db.Open(file_name_));
814 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
816 const char kIndexName[] = "icon_mapping_page_url_idx";
817 const char kDeleteSql[] =
818 "DELETE FROM icon_mapping WHERE page_url = 'http://yahoo.com/'";
820 sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql));
822 // Database should be corrupt at the SQLite level.
824 sql::Connection raw_db;
825 EXPECT_TRUE(raw_db.Open(file_name_));
826 ASSERT_NE("ok", sql::test::IntegrityCheck(&raw_db));
829 // Open the database and access the corrupt index.
831 sql::ScopedErrorIgnorer ignore_errors;
832 ignore_errors.IgnoreError(SQLITE_CORRUPT);
833 ThumbnailDatabase db(NULL);
834 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
836 // Data for kPageUrl2 was deleted, but the index entry remains,
837 // this will throw SQLITE_CORRUPT. The corruption handler will
838 // recover the database and poison the handle, so the outer call
840 EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
842 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
845 // Check that the database is recovered at the SQLite level.
847 sql::Connection raw_db;
848 EXPECT_TRUE(raw_db.Open(file_name_));
849 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
851 // Check that the expected tables exist.
852 VerifyTablesAndColumns(&raw_db);
855 // Database should also be recovered at higher levels.
857 ThumbnailDatabase db(NULL);
858 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
860 // Now this fails because there is no mapping.
861 EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
863 // Other data was retained by recovery.
864 EXPECT_TRUE(CheckPageHasIcon(&db,
866 favicon_base::FAVICON,
873 // Corrupt the database again by adjusting the header.
874 EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
876 // Database is unusable at the SQLite level.
878 sql::ScopedErrorIgnorer ignore_errors;
879 ignore_errors.IgnoreError(SQLITE_CORRUPT);
880 sql::Connection raw_db;
881 EXPECT_TRUE(raw_db.Open(file_name_));
882 EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
883 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
886 // Database should be recovered during open.
888 sql::ScopedErrorIgnorer ignore_errors;
889 ignore_errors.IgnoreError(SQLITE_CORRUPT);
890 ThumbnailDatabase db(NULL);
891 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
893 EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
894 EXPECT_TRUE(CheckPageHasIcon(&db,
896 favicon_base::FAVICON,
902 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
906 TEST_F(ThumbnailDatabaseTest, Recovery6) {
907 // TODO(shess): See comment at top of Recovery test.
908 if (!sql::Recovery::FullRecoverySupported())
911 // Create an example database without loading into ThumbnailDatabase
912 // (which would upgrade it).
913 EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v6.sql"));
915 // Corrupt the database by adjusting the header. This form of corruption will
916 // cause immediate failures during Open(), before the migration code runs, so
917 // the recovery code will run.
918 EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
920 // Database is unusable at the SQLite level.
922 sql::ScopedErrorIgnorer ignore_errors;
923 ignore_errors.IgnoreError(SQLITE_CORRUPT);
924 sql::Connection raw_db;
925 EXPECT_TRUE(raw_db.Open(file_name_));
926 EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
927 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
930 // Database open should succeed.
932 sql::ScopedErrorIgnorer ignore_errors;
933 ignore_errors.IgnoreError(SQLITE_CORRUPT);
934 ThumbnailDatabase db(NULL);
935 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
936 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
939 // The database should be usable at the SQLite level, with a current schema
942 sql::Connection raw_db;
943 EXPECT_TRUE(raw_db.Open(file_name_));
944 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
946 // Check that the expected tables exist.
947 VerifyTablesAndColumns(&raw_db);
949 // Version 6 recovery is deprecated, the data should all be gone.
950 VerifyDatabaseEmpty(&raw_db);
954 TEST_F(ThumbnailDatabaseTest, Recovery5) {
955 // TODO(shess): See comment at top of Recovery test.
956 if (!sql::Recovery::FullRecoverySupported())
959 // Create an example database without loading into ThumbnailDatabase
960 // (which would upgrade it).
961 EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v5.sql"));
963 // Corrupt the database by adjusting the header. This form of corruption will
964 // cause immediate failures during Open(), before the migration code runs, so
965 // the recovery code will run.
966 EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
968 // Database is unusable at the SQLite level.
970 sql::ScopedErrorIgnorer ignore_errors;
971 ignore_errors.IgnoreError(SQLITE_CORRUPT);
972 sql::Connection raw_db;
973 EXPECT_TRUE(raw_db.Open(file_name_));
974 EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
975 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
978 // Database open should succeed.
980 sql::ScopedErrorIgnorer ignore_errors;
981 ignore_errors.IgnoreError(SQLITE_CORRUPT);
982 ThumbnailDatabase db(NULL);
983 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
984 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
987 // The database should be usable at the SQLite level, with a current schema
990 sql::Connection raw_db;
991 EXPECT_TRUE(raw_db.Open(file_name_));
992 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
994 // Check that the expected tables exist.
995 VerifyTablesAndColumns(&raw_db);
997 // Version 5 recovery is deprecated, the data should all be gone.
998 VerifyDatabaseEmpty(&raw_db);
1002 // Test that various broken schema found in the wild can be opened
1003 // successfully, and result in the correct schema.
1004 TEST_F(ThumbnailDatabaseTest, WildSchema) {
1005 base::FilePath sql_path;
1006 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &sql_path));
1007 sql_path = sql_path.AppendASCII("History").AppendASCII("thumbnail_wild");
1009 base::FileEnumerator fe(
1010 sql_path, false, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.sql"));
1011 for (base::FilePath name = fe.Next(); !name.empty(); name = fe.Next()) {
1012 SCOPED_TRACE(name.BaseName().AsUTF8Unsafe());
1013 // Generate a database path based on the golden's basename.
1014 base::FilePath db_base_name =
1015 name.BaseName().ReplaceExtension(FILE_PATH_LITERAL("db"));
1016 base::FilePath db_path = file_name_.DirName().Append(db_base_name);
1017 ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path, name));
1019 // All schema flaws should be cleaned up by Init().
1020 // TODO(shess): Differentiate between databases which need Raze()
1021 // and those which can be salvaged.
1022 ThumbnailDatabase db(NULL);
1023 ASSERT_EQ(sql::INIT_OK, db.Init(db_path));
1025 // Verify that the resulting schema is correct, whether it
1026 // involved razing the file or fixing things in place.
1027 VerifyTablesAndColumns(&db.db_);
1031 } // namespace history