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