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