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 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()";
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 virtual ~ThumbnailDatabaseTest() {
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>();
176 scoped_ptr<ThumbnailDatabase> db(new ThumbnailDatabase());
177 EXPECT_EQ(sql::INIT_OK, db->Init(file_name_));
178 db->BeginTransaction();
184 virtual void SetUp() {
185 // Get a temporary directory for the test DB files.
186 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
188 file_name_ = temp_dir_.path().AppendASCII("TestFavicons.db");
191 base::ScopedTempDir temp_dir_;
192 base::FilePath file_name_;
195 TEST_F(ThumbnailDatabaseTest, AddIconMapping) {
196 ThumbnailDatabase db;
197 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
198 db.BeginTransaction();
200 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
201 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
203 GURL url("http://google.com");
204 base::Time time = base::Time::Now();
205 chrome::FaviconID id = db.AddFavicon(url,
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);
220 TEST_F(ThumbnailDatabaseTest, UpdateIconMapping) {
221 ThumbnailDatabase db;
222 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
223 db.BeginTransaction();
225 GURL url("http://google.com");
226 chrome::FaviconID id =
227 db.AddFavicon(url, chrome::TOUCH_ICON);
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);
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));
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);
249 TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
250 ThumbnailDatabase db;
251 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
252 db.BeginTransaction();
254 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
255 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
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));
264 chrome::FaviconID id2 =
265 db.AddFavicon(url, chrome::FAVICON);
266 EXPECT_LT(0, db.AddIconMapping(url, id2));
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));
275 db.DeleteIconMappings(url);
277 EXPECT_FALSE(db.GetIconMappingsForPageURL(url, NULL));
278 EXPECT_FALSE(db.GetIconMappingsForPageURL(url, chrome::FAVICON, NULL));
281 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) {
282 ThumbnailDatabase db;
283 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
284 db.BeginTransaction();
286 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
287 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
289 GURL url("http://google.com");
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));
297 chrome::FaviconID id2 = db.AddFavicon(url, chrome::FAVICON);
299 db.AddFaviconBitmap(id2, favicon, time, kSmallSize);
300 EXPECT_LT(0, db.AddIconMapping(url, id2));
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);
309 TEST_F(ThumbnailDatabaseTest, RetainDataForPageUrls) {
310 ThumbnailDatabase db;
312 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
314 db.BeginTransaction();
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.
321 // TODO(shess): This would probably make sense as a golden file.
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)));
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);
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);
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);
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();
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));
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));
359 // The one not retained should be missing.
360 EXPECT_FALSE(db.GetFaviconIDForFaviconURL(kPageUrl2, false, NULL));
362 // Schema should be the same.
363 EXPECT_EQ(original_schema, db.db_.GetSchema());
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();
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));
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);
386 EXPECT_TRUE(db.GetFaviconBitmaps(id, NULL));
388 EXPECT_TRUE(db.DeleteFavicon(id));
389 EXPECT_FALSE(db.GetFaviconBitmaps(id, NULL));
392 TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
393 ThumbnailDatabase db;
394 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
395 db.BeginTransaction();
398 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
399 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
401 GURL page_url("http://google.com");
402 GURL icon_url("http://google.com/favicon.ico");
403 base::Time time = base::Time::Now();
405 chrome::FaviconID id = db.AddFavicon(icon_url,
410 EXPECT_NE(0, db.AddIconMapping(page_url, id));
411 std::vector<IconMapping> icon_mappings;
412 EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
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);
420 std::vector<unsigned char> data2(kBlob2, kBlob2 + sizeof(kBlob2));
421 scoped_refptr<base::RefCountedBytes> favicon2 =
422 new base::RefCountedBytes(data);
424 chrome::FaviconID id2 = db.AddFavicon(icon_url,
429 EXPECT_NE(0, db.AddIconMapping(page_url, id2));
431 icon_mappings.clear();
432 EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
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);
439 // Add a touch precomposed icon
440 scoped_refptr<base::RefCountedBytes> favicon3 =
441 new base::RefCountedBytes(data2);
443 chrome::FaviconID id3 = db.AddFavicon(icon_url,
444 chrome::TOUCH_PRECOMPOSED_ICON,
448 EXPECT_NE(0, db.AddIconMapping(page_url, id3));
450 icon_mappings.clear();
451 EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url, &icon_mappings));
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);
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();
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();
470 chrome::FaviconID id1 = db.AddFavicon(url,
475 EXPECT_NE(0, db.AddIconMapping(url, id1));
477 chrome::FaviconID id2 = db.AddFavicon(url,
482 EXPECT_NE(0, db.AddIconMapping(url, id2));
484 chrome::FaviconID id3 = db.AddFavicon(url,
489 EXPECT_NE(0, db.AddIconMapping(url, id3));
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(
496 chrome::FAVICON | chrome::TOUCH_ICON | chrome::TOUCH_PRECOMPOSED_ICON,
499 EXPECT_EQ(2u, icon_mappings.size());
500 if (id2 == icon_mappings[0].icon_id) {
501 EXPECT_EQ(id3, icon_mappings[1].icon_id);
503 EXPECT_EQ(id3, icon_mappings[0].icon_id);
504 EXPECT_EQ(id2, icon_mappings[1].icon_id);
507 icon_mappings.clear();
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);
513 EXPECT_EQ(id3, icon_mappings[0].icon_id);
514 EXPECT_EQ(id2, icon_mappings[1].icon_id);
517 icon_mappings.clear();
519 db.GetIconMappingsForPageURL(url, chrome::FAVICON, &icon_mappings));
520 EXPECT_EQ(1u, icon_mappings.size());
521 EXPECT_EQ(id1, icon_mappings[0].icon_id);
524 TEST_F(ThumbnailDatabaseTest, HasMappingFor) {
525 ThumbnailDatabase db;
526 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
527 db.BeginTransaction();
529 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
530 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
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"),
541 // Add another type of favicon
542 time = base::Time::Now();
543 chrome::FaviconID id2 = db.AddFavicon(GURL("http://www.google.com/icon"),
551 time = base::Time::Now();
552 chrome::FaviconID id3 = db.AddFavicon(GURL("http://www.google.com/icon"),
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));
564 EXPECT_TRUE(db.HasMappingFor(id1));
565 EXPECT_TRUE(db.HasMappingFor(id2));
566 EXPECT_FALSE(db.HasMappingFor(id3));
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));
575 TEST_F(ThumbnailDatabaseTest, CloneIconMappings) {
576 ThumbnailDatabase db;
577 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
578 db.BeginTransaction();
580 std::vector<unsigned char> data(kBlob1, kBlob1 + sizeof(kBlob1));
581 scoped_refptr<base::RefCountedBytes> favicon(new base::RefCountedBytes(data));
583 // Add a favicon which will have icon_mappings
584 chrome::FaviconID id1 = db.AddFavicon(
585 GURL("http://google.com"), chrome::FAVICON);
587 base::Time time = base::Time::Now();
588 db.AddFaviconBitmap(id1, favicon, time, gfx::Size());
590 // Add another type of favicon
591 chrome::FaviconID id2 = db.AddFavicon(GURL("http://www.google.com/icon"),
594 time = base::Time::Now();
595 db.AddFaviconBitmap(id2, favicon, time, gfx::Size());
598 chrome::FaviconID id3 = db.AddFavicon(GURL("http://www.google.com/icon"),
601 time = base::Time::Now();
602 db.AddFaviconBitmap(id3, favicon, time, gfx::Size());
604 GURL page1_url("http://page1.com");
605 EXPECT_TRUE(db.AddIconMapping(page1_url, id1));
606 EXPECT_TRUE(db.AddIconMapping(page1_url, id2));
608 GURL page2_url("http://page2.com");
609 EXPECT_TRUE(db.AddIconMapping(page2_url, id3));
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());
616 EXPECT_TRUE(db.CloneIconMappings(page1_url, page2_url));
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);
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));
628 icon_mapping.clear();
629 EXPECT_TRUE(db.GetIconMappingsForPageURL(page3_url, &icon_mapping));
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);
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_);
646 // Version 3 is deprecated, the data should all be gone.
647 VerifyDatabaseEmpty(&db->db_);
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_);
656 // Version 4 is deprecated, the data should all be gone.
657 VerifyDatabaseEmpty(&db->db_);
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_);
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));
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_);
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));
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_);
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));
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())
718 // Create an example database.
720 EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v7.sql"));
722 sql::Connection raw_db;
723 EXPECT_TRUE(raw_db.Open(file_name_));
724 VerifyTablesAndColumns(&raw_db);
727 // Test that the contents make sense after clean open.
729 ThumbnailDatabase db;
730 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
733 CheckPageHasIcon(&db, kPageUrl1, chrome::FAVICON,
734 kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
736 CheckPageHasIcon(&db, kPageUrl2, chrome::FAVICON,
737 kIconUrl2, kLargeSize, sizeof(kBlob2), kBlob2));
740 // Corrupt the |icon_mapping.page_url| index by deleting an element
741 // from the backing table but not the index.
743 sql::Connection raw_db;
744 EXPECT_TRUE(raw_db.Open(file_name_));
745 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
747 const char kIndexName[] = "icon_mapping_page_url_idx";
748 const char kDeleteSql[] =
749 "DELETE FROM icon_mapping WHERE page_url = 'http://yahoo.com/'";
751 sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql));
753 // Database should be corrupt at the SQLite level.
755 sql::Connection raw_db;
756 EXPECT_TRUE(raw_db.Open(file_name_));
757 ASSERT_NE("ok", sql::test::IntegrityCheck(&raw_db));
760 // Open the database and access the corrupt index.
762 sql::ScopedErrorIgnorer ignore_errors;
763 ignore_errors.IgnoreError(SQLITE_CORRUPT);
764 ThumbnailDatabase db;
765 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
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
771 EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
773 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
776 // Check that the database is recovered at the SQLite level.
778 sql::Connection raw_db;
779 EXPECT_TRUE(raw_db.Open(file_name_));
780 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
782 // Check that the expected tables exist.
783 VerifyTablesAndColumns(&raw_db);
786 // Database should also be recovered at higher levels.
788 ThumbnailDatabase db;
789 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
791 // Now this fails because there is no mapping.
792 EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
794 // Other data was retained by recovery.
796 CheckPageHasIcon(&db, kPageUrl1, chrome::FAVICON,
797 kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
800 // Corrupt the database again by adjusting the header.
801 EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
803 // Database is unusable at the SQLite level.
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());
813 // Database should be recovered during open.
815 sql::ScopedErrorIgnorer ignore_errors;
816 ignore_errors.IgnoreError(SQLITE_CORRUPT);
817 ThumbnailDatabase db;
818 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
820 EXPECT_FALSE(db.GetIconMappingsForPageURL(kPageUrl2, NULL));
822 CheckPageHasIcon(&db, kPageUrl1, chrome::FAVICON,
823 kIconUrl1, kLargeSize, sizeof(kBlob1), kBlob1));
825 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
829 TEST_F(ThumbnailDatabaseTest, Recovery6) {
830 // TODO(shess): See comment at top of Recovery test.
831 if (!sql::Recovery::FullRecoverySupported())
834 // Create an example database without loading into ThumbnailDatabase
835 // (which would upgrade it).
836 EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v6.sql"));
838 // Corrupt the database by adjusting the header. This form of corruption will
839 // cause immediate failures during Open(), before the migration code runs, so
840 // the recovery code will run.
841 EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
843 // Database is unusable at the SQLite level.
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());
853 // Database open should succeed.
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 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
862 // The database should be usable at the SQLite level, with a current schema
865 sql::Connection raw_db;
866 EXPECT_TRUE(raw_db.Open(file_name_));
867 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
869 // Check that the expected tables exist.
870 VerifyTablesAndColumns(&raw_db);
872 // Version 6 recovery is deprecated, the data should all be gone.
873 VerifyDatabaseEmpty(&raw_db);
877 TEST_F(ThumbnailDatabaseTest, Recovery5) {
878 // TODO(shess): See comment at top of Recovery test.
879 if (!sql::Recovery::FullRecoverySupported())
882 // Create an example database without loading into ThumbnailDatabase
883 // (which would upgrade it).
884 EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "Favicons.v5.sql"));
886 // Corrupt the database by adjusting the header. This form of corruption will
887 // cause immediate failures during Open(), before the migration code runs, so
888 // the recovery code will run.
889 EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
891 // Database is unusable at the SQLite level.
893 sql::ScopedErrorIgnorer ignore_errors;
894 ignore_errors.IgnoreError(SQLITE_CORRUPT);
895 sql::Connection raw_db;
896 EXPECT_TRUE(raw_db.Open(file_name_));
897 EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
898 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
901 // Database open should succeed.
903 sql::ScopedErrorIgnorer ignore_errors;
904 ignore_errors.IgnoreError(SQLITE_CORRUPT);
905 ThumbnailDatabase db;
906 ASSERT_EQ(sql::INIT_OK, db.Init(file_name_));
907 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
910 // The database should be usable at the SQLite level, with a current schema
913 sql::Connection raw_db;
914 EXPECT_TRUE(raw_db.Open(file_name_));
915 ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
917 // Check that the expected tables exist.
918 VerifyTablesAndColumns(&raw_db);
920 // Version 5 recovery is deprecated, the data should all be gone.
921 VerifyDatabaseEmpty(&raw_db);
925 // Test that various broken schema found in the wild can be opened
926 // successfully, and result in the correct schema.
927 TEST_F(ThumbnailDatabaseTest, WildSchema) {
928 base::FilePath sql_path;
929 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &sql_path));
930 sql_path = sql_path.AppendASCII("History").AppendASCII("thumbnail_wild");
932 base::FileEnumerator fe(
933 sql_path, false, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.sql"));
934 for (base::FilePath name = fe.Next(); !name.empty(); name = fe.Next()) {
935 SCOPED_TRACE(name.BaseName().AsUTF8Unsafe());
936 // Generate a database path based on the golden's basename.
937 base::FilePath db_base_name =
938 name.BaseName().ReplaceExtension(FILE_PATH_LITERAL("db"));
939 base::FilePath db_path = file_name_.DirName().Append(db_base_name);
940 ASSERT_TRUE(sql::test::CreateDatabaseFromSQL(db_path, name));
942 // All schema flaws should be cleaned up by Init().
943 // TODO(shess): Differentiate between databases which need Raze()
944 // and those which can be salvaged.
945 ThumbnailDatabase db;
946 ASSERT_EQ(sql::INIT_OK, db.Init(db_path));
948 // Verify that the resulting schema is correct, whether it
949 // involved razing the file or fixing things in place.
950 VerifyTablesAndColumns(&db.db_);
954 } // namespace history