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