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