Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / top_sites_database_unittest.cc
1 // Copyright (c) 2011 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 <map>
6
7 #include "base/files/file_path.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/path_service.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/history/top_sites_database.h"
12 #include "chrome/common/chrome_paths.h"
13 #include "chrome/tools/profiles/thumbnail-inl.h"
14 #include "components/history/core/browser/history_types.h"
15 #include "sql/connection.h"
16 #include "sql/recovery.h"
17 #include "sql/test/scoped_error_ignorer.h"
18 #include "sql/test/test_helpers.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "third_party/sqlite/sqlite3.h"
21 #include "url/gurl.h"
22
23 namespace {
24
25 // URL with url_rank 0 in golden files.
26 const GURL kUrl0 = GURL("http://www.google.com/");
27
28 // URL with url_rank 1 in golden files.
29 const GURL kUrl1 = GURL("http://www.google.com/chrome/intl/en/welcome.html");
30
31 // URL with url_rank 2 in golden files.
32 const GURL kUrl2 = GURL("https://chrome.google.com/webstore?hl=en");
33
34 // Create the test database at |db_path| from the golden file at
35 // |ascii_path| in the "History/" subdir of the test data dir.
36 WARN_UNUSED_RESULT bool CreateDatabaseFromSQL(const base::FilePath &db_path,
37                                                 const char* ascii_path) {
38   base::FilePath sql_path;
39   if (!PathService::Get(chrome::DIR_TEST_DATA, &sql_path))
40     return false;
41   sql_path = sql_path.AppendASCII("History").AppendASCII(ascii_path);
42   return sql::test::CreateDatabaseFromSQL(db_path, sql_path);
43 }
44
45 // Verify that the up-to-date database has the expected tables and
46 // columns.  Functional tests only check whether the things which
47 // should be there are, but do not check if extraneous items are
48 // present.  Any extraneous items have the potential to interact
49 // negatively with future schema changes.
50 void VerifyTablesAndColumns(sql::Connection* db) {
51   // [meta] and [thumbnails].
52   EXPECT_EQ(2u, sql::test::CountSQLTables(db));
53
54   // Implicit index on [meta], index on [thumbnails].
55   EXPECT_EQ(2u, sql::test::CountSQLIndices(db));
56
57   // [key] and [value].
58   EXPECT_EQ(2u, sql::test::CountTableColumns(db, "meta"));
59
60   // [url], [url_rank], [title], [thumbnail], [redirects],
61   // [boring_score], [good_clipping], [at_top], [last_updated], and
62   // [load_completed], [last_forced]
63   EXPECT_EQ(11u, sql::test::CountTableColumns(db, "thumbnails"));
64 }
65
66 void VerifyDatabaseEmpty(sql::Connection* db) {
67   size_t rows = 0;
68   EXPECT_TRUE(sql::test::CountTableRows(db, "thumbnails", &rows));
69   EXPECT_EQ(0u, rows);
70 }
71
72 }  // namespace
73
74 namespace history {
75
76 class TopSitesDatabaseTest : public testing::Test {
77  protected:
78   virtual void SetUp() {
79     // Get a temporary directory for the test DB files.
80     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
81     file_name_ = temp_dir_.path().AppendASCII("TestTopSites.db");
82   }
83
84   base::ScopedTempDir temp_dir_;
85   base::FilePath file_name_;
86 };
87
88 // Version 1 is deprecated, the resulting schema should be current,
89 // with no data.
90 TEST_F(TopSitesDatabaseTest, Version1) {
91   ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v1.sql"));
92
93   TopSitesDatabase db;
94   ASSERT_TRUE(db.Init(file_name_));
95   VerifyTablesAndColumns(db.db_.get());
96   VerifyDatabaseEmpty(db.db_.get());
97 }
98
99 TEST_F(TopSitesDatabaseTest, Version2) {
100   ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v2.sql"));
101
102   TopSitesDatabase db;
103   ASSERT_TRUE(db.Init(file_name_));
104
105   VerifyTablesAndColumns(db.db_.get());
106
107   // Basic operational check.
108   MostVisitedURLList urls;
109   std::map<GURL, Images> thumbnails;
110   db.GetPageThumbnails(&urls, &thumbnails);
111   ASSERT_EQ(3u, urls.size());
112   ASSERT_EQ(3u, thumbnails.size());
113   EXPECT_EQ(kUrl0, urls[0].url);  // [0] because of url_rank.
114   // kGoogleThumbnail includes nul terminator.
115   ASSERT_EQ(sizeof(kGoogleThumbnail) - 1,
116             thumbnails[urls[0].url].thumbnail->size());
117   EXPECT_TRUE(!memcmp(thumbnails[urls[0].url].thumbnail->front(),
118                       kGoogleThumbnail, sizeof(kGoogleThumbnail) - 1));
119
120   ASSERT_TRUE(db.RemoveURL(urls[1]));
121   db.GetPageThumbnails(&urls, &thumbnails);
122   ASSERT_EQ(2u, urls.size());
123   ASSERT_EQ(2u, thumbnails.size());
124 }
125
126 TEST_F(TopSitesDatabaseTest, Version3) {
127   ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v3.sql"));
128
129   TopSitesDatabase db;
130   ASSERT_TRUE(db.Init(file_name_));
131
132   VerifyTablesAndColumns(db.db_.get());
133
134   // Basic operational check.
135   MostVisitedURLList urls;
136   std::map<GURL, Images> thumbnails;
137   db.GetPageThumbnails(&urls, &thumbnails);
138   ASSERT_EQ(3u, urls.size());
139   ASSERT_EQ(3u, thumbnails.size());
140   EXPECT_EQ(kUrl0, urls[0].url);  // [0] because of url_rank.
141   // kGoogleThumbnail includes nul terminator.
142   ASSERT_EQ(sizeof(kGoogleThumbnail) - 1,
143             thumbnails[urls[0].url].thumbnail->size());
144   EXPECT_TRUE(!memcmp(thumbnails[urls[0].url].thumbnail->front(),
145                       kGoogleThumbnail, sizeof(kGoogleThumbnail) - 1));
146
147   ASSERT_TRUE(db.RemoveURL(urls[1]));
148   db.GetPageThumbnails(&urls, &thumbnails);
149   ASSERT_EQ(2u, urls.size());
150   ASSERT_EQ(2u, thumbnails.size());
151 }
152
153 // Version 1 is deprecated, the resulting schema should be current,
154 // with no data.
155 TEST_F(TopSitesDatabaseTest, Recovery1) {
156   // Recovery module only supports some platforms at this time.
157   if (!sql::Recovery::FullRecoverySupported())
158     return;
159
160   // Create an example database.
161   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v1.sql"));
162
163   // Corrupt the database by adjusting the header size.
164   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
165
166   // Database is unusable at the SQLite level.
167   {
168     sql::ScopedErrorIgnorer ignore_errors;
169     ignore_errors.IgnoreError(SQLITE_CORRUPT);
170     sql::Connection raw_db;
171     EXPECT_TRUE(raw_db.Open(file_name_));
172     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
173     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
174   }
175
176   // Corruption should be detected and recovered during Init().
177   {
178     sql::ScopedErrorIgnorer ignore_errors;
179     ignore_errors.IgnoreError(SQLITE_CORRUPT);
180
181     TopSitesDatabase db;
182     ASSERT_TRUE(db.Init(file_name_));
183     VerifyTablesAndColumns(db.db_.get());
184     VerifyDatabaseEmpty(db.db_.get());
185
186     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
187   }
188 }
189
190 TEST_F(TopSitesDatabaseTest, Recovery2) {
191   // Recovery module only supports some platforms at this time.
192   if (!sql::Recovery::FullRecoverySupported())
193     return;
194
195   // Create an example database.
196   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v2.sql"));
197
198   // Corrupt the database by adjusting the header.
199   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
200
201   // Database is unusable at the SQLite level.
202   {
203     sql::ScopedErrorIgnorer ignore_errors;
204     ignore_errors.IgnoreError(SQLITE_CORRUPT);
205     sql::Connection raw_db;
206     EXPECT_TRUE(raw_db.Open(file_name_));
207     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
208     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
209   }
210
211   // Corruption should be detected and recovered during Init().  After recovery,
212   // the Version2 checks should work.
213   {
214     sql::ScopedErrorIgnorer ignore_errors;
215     ignore_errors.IgnoreError(SQLITE_CORRUPT);
216
217     TopSitesDatabase db;
218     ASSERT_TRUE(db.Init(file_name_));
219
220     VerifyTablesAndColumns(db.db_.get());
221
222     // Basic operational check.
223     MostVisitedURLList urls;
224     std::map<GURL, Images> thumbnails;
225     db.GetPageThumbnails(&urls, &thumbnails);
226     ASSERT_EQ(3u, urls.size());
227     ASSERT_EQ(3u, thumbnails.size());
228     EXPECT_EQ(kUrl0, urls[0].url);  // [0] because of url_rank.
229     // kGoogleThumbnail includes nul terminator.
230     ASSERT_EQ(sizeof(kGoogleThumbnail) - 1,
231               thumbnails[urls[0].url].thumbnail->size());
232     EXPECT_TRUE(!memcmp(thumbnails[urls[0].url].thumbnail->front(),
233                         kGoogleThumbnail, sizeof(kGoogleThumbnail) - 1));
234
235     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
236   }
237 }
238
239 TEST_F(TopSitesDatabaseTest, Recovery3) {
240   // Recovery module only supports some platforms at this time.
241   if (!sql::Recovery::FullRecoverySupported())
242     return;
243
244   // Create an example database.
245   EXPECT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v3.sql"));
246
247   // Corrupt the database by adjusting the header.
248   EXPECT_TRUE(sql::test::CorruptSizeInHeader(file_name_));
249
250   // Database is unusable at the SQLite level.
251   {
252     sql::ScopedErrorIgnorer ignore_errors;
253     ignore_errors.IgnoreError(SQLITE_CORRUPT);
254     sql::Connection raw_db;
255     EXPECT_TRUE(raw_db.Open(file_name_));
256     EXPECT_FALSE(raw_db.IsSQLValid("PRAGMA integrity_check"));
257     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
258   }
259
260   // Corruption should be detected and recovered during Init().
261   {
262     sql::ScopedErrorIgnorer ignore_errors;
263     ignore_errors.IgnoreError(SQLITE_CORRUPT);
264
265     TopSitesDatabase db;
266     ASSERT_TRUE(db.Init(file_name_));
267
268     MostVisitedURLList urls;
269     std::map<GURL, Images> thumbnails;
270     db.GetPageThumbnails(&urls, &thumbnails);
271     ASSERT_EQ(3u, urls.size());
272     ASSERT_EQ(3u, thumbnails.size());
273     EXPECT_EQ(kUrl0, urls[0].url);  // [0] because of url_rank.
274     // kGoogleThumbnail includes nul terminator.
275     ASSERT_EQ(sizeof(kGoogleThumbnail) - 1,
276               thumbnails[urls[0].url].thumbnail->size());
277     EXPECT_TRUE(!memcmp(thumbnails[urls[0].url].thumbnail->front(),
278                         kGoogleThumbnail, sizeof(kGoogleThumbnail) - 1));
279
280     ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
281   }
282
283   // Double-check database integrity.
284   {
285     sql::Connection raw_db;
286     EXPECT_TRUE(raw_db.Open(file_name_));
287     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
288   }
289
290   // Corrupt the thumnails.url auto-index by deleting an element from the table
291   // but leaving it in the index.
292   const char kIndexName[] = "sqlite_autoindex_thumbnails_1";
293   // TODO(shess): Refactor CorruptTableOrIndex() to make parameterized
294   // statements easy.
295   const char kDeleteSql[] =
296       "DELETE FROM thumbnails WHERE url = "
297       "'http://www.google.com/chrome/intl/en/welcome.html'";
298   EXPECT_TRUE(
299       sql::test::CorruptTableOrIndex(file_name_, kIndexName, kDeleteSql));
300
301   // SQLite can operate on the database, but notices the corruption in integrity
302   // check.
303   {
304     sql::Connection raw_db;
305     EXPECT_TRUE(raw_db.Open(file_name_));
306     ASSERT_NE("ok", sql::test::IntegrityCheck(&raw_db));
307   }
308
309   // Open the database and access the corrupt index.
310   {
311     TopSitesDatabase db;
312     ASSERT_TRUE(db.Init(file_name_));
313
314     {
315       sql::ScopedErrorIgnorer ignore_errors;
316       ignore_errors.IgnoreError(SQLITE_CORRUPT);
317
318       // Data for kUrl1 was deleted, but the index entry remains, this will
319       // throw SQLITE_CORRUPT.  The corruption handler will recover the database
320       // and poison the handle, so the outer call fails.
321       EXPECT_EQ(TopSitesDatabase::kRankOfNonExistingURL,
322                 db.GetURLRank(MostVisitedURL(kUrl1, base::string16())));
323
324       ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
325     }
326   }
327
328   // Check that the database is recovered at the SQLite level.
329   {
330     sql::Connection raw_db;
331     EXPECT_TRUE(raw_db.Open(file_name_));
332     ASSERT_EQ("ok", sql::test::IntegrityCheck(&raw_db));
333   }
334
335   // After recovery, the database accesses won't throw errors.  The top-ranked
336   // item is removed, but the ranking was revised in post-processing.
337   {
338     TopSitesDatabase db;
339     ASSERT_TRUE(db.Init(file_name_));
340     VerifyTablesAndColumns(db.db_.get());
341
342     EXPECT_EQ(TopSitesDatabase::kRankOfNonExistingURL,
343               db.GetURLRank(MostVisitedURL(kUrl1, base::string16())));
344
345     MostVisitedURLList urls;
346     std::map<GURL, Images> thumbnails;
347     db.GetPageThumbnails(&urls, &thumbnails);
348     ASSERT_EQ(2u, urls.size());
349     ASSERT_EQ(2u, thumbnails.size());
350     EXPECT_EQ(kUrl0, urls[0].url);  // [0] because of url_rank.
351     EXPECT_EQ(kUrl2, urls[1].url);  // [1] because of url_rank.
352   }
353 }
354
355 TEST_F(TopSitesDatabaseTest, AddRemoveEditThumbnails) {
356   ASSERT_TRUE(CreateDatabaseFromSQL(file_name_, "TopSites.v3.sql"));
357
358   TopSitesDatabase db;
359   ASSERT_TRUE(db.Init(file_name_));
360
361   // Add a new URL, not forced, rank = 1.
362   GURL mapsUrl = GURL("http://maps.google.com/");
363   MostVisitedURL url1(mapsUrl, base::ASCIIToUTF16("Google Maps"));
364   db.SetPageThumbnail(url1, 1, Images());
365
366   MostVisitedURLList urls;
367   std::map<GURL, Images> thumbnails;
368   db.GetPageThumbnails(&urls, &thumbnails);
369   ASSERT_EQ(4u, urls.size());
370   ASSERT_EQ(4u, thumbnails.size());
371   EXPECT_EQ(kUrl0, urls[0].url);
372   EXPECT_EQ(mapsUrl, urls[1].url);
373
374   // Add a new URL, forced.
375   GURL driveUrl = GURL("http://drive.google.com/");
376   MostVisitedURL url2(driveUrl, base::ASCIIToUTF16("Google Drive"));
377   url2.last_forced_time = base::Time::FromJsTime(789714000000);  // 10/1/1995
378   db.SetPageThumbnail(url2, TopSitesDatabase::kRankOfForcedURL, Images());
379
380   db.GetPageThumbnails(&urls, &thumbnails);
381   ASSERT_EQ(5u, urls.size());
382   ASSERT_EQ(5u, thumbnails.size());
383   EXPECT_EQ(driveUrl, urls[0].url);  // Forced URLs always appear first.
384   EXPECT_EQ(kUrl0, urls[1].url);
385   EXPECT_EQ(mapsUrl, urls[2].url);
386
387   // Add a new URL, forced (earlier).
388   GURL plusUrl = GURL("http://plus.google.com/");
389   MostVisitedURL url3(plusUrl, base::ASCIIToUTF16("Google Plus"));
390   url3.last_forced_time = base::Time::FromJsTime(787035600000);  // 10/12/1994
391   db.SetPageThumbnail(url3, TopSitesDatabase::kRankOfForcedURL, Images());
392
393   db.GetPageThumbnails(&urls, &thumbnails);
394   ASSERT_EQ(6u, urls.size());
395   ASSERT_EQ(6u, thumbnails.size());
396   EXPECT_EQ(plusUrl, urls[0].url);  // New forced URL should appear first.
397   EXPECT_EQ(driveUrl, urls[1].url);
398   EXPECT_EQ(kUrl0, urls[2].url);
399   EXPECT_EQ(mapsUrl, urls[3].url);
400
401   // Change the last_forced_time of a forced URL.
402   url3.last_forced_time = base::Time::FromJsTime(792392400000);  // 10/2/1995
403   db.SetPageThumbnail(url3, TopSitesDatabase::kRankOfForcedURL, Images());
404
405   db.GetPageThumbnails(&urls, &thumbnails);
406   ASSERT_EQ(6u, urls.size());
407   ASSERT_EQ(6u, thumbnails.size());
408   EXPECT_EQ(driveUrl, urls[0].url);
409   EXPECT_EQ(plusUrl, urls[1].url);  // Forced URL should have moved second.
410   EXPECT_EQ(kUrl0, urls[2].url);
411   EXPECT_EQ(mapsUrl, urls[3].url);
412
413   // Change a non-forced URL to forced using UpdatePageRank.
414   url1.last_forced_time = base::Time::FromJsTime(792219600000);  // 8/2/1995
415   db.UpdatePageRank(url1, TopSitesDatabase::kRankOfForcedURL);
416
417   db.GetPageThumbnails(&urls, &thumbnails);
418   ASSERT_EQ(6u, urls.size());
419   ASSERT_EQ(6u, thumbnails.size());
420   EXPECT_EQ(driveUrl, urls[0].url);
421   EXPECT_EQ(mapsUrl, urls[1].url);  // Maps moves to second forced URL.
422   EXPECT_EQ(plusUrl, urls[2].url);
423   EXPECT_EQ(kUrl0, urls[3].url);
424
425   // Change a forced URL to non-forced using SetPageThumbnail.
426   url3.last_forced_time = base::Time();
427   db.SetPageThumbnail(url3, 1, Images());
428
429   db.GetPageThumbnails(&urls, &thumbnails);
430   ASSERT_EQ(6u, urls.size());
431   ASSERT_EQ(6u, thumbnails.size());
432   EXPECT_EQ(driveUrl, urls[0].url);
433   EXPECT_EQ(mapsUrl, urls[1].url);
434   EXPECT_EQ(kUrl0, urls[2].url);
435   EXPECT_EQ(plusUrl, urls[3].url);  // Plus moves to second non-forced URL.
436
437   // Change a non-forced URL to earlier non-forced using UpdatePageRank.
438   db.UpdatePageRank(url3, 0);
439
440   db.GetPageThumbnails(&urls, &thumbnails);
441   ASSERT_EQ(6u, urls.size());
442   ASSERT_EQ(6u, thumbnails.size());
443   EXPECT_EQ(driveUrl, urls[0].url);
444   EXPECT_EQ(mapsUrl, urls[1].url);
445   EXPECT_EQ(plusUrl, urls[2].url);  // Plus moves to first non-forced URL.
446   EXPECT_EQ(kUrl0, urls[3].url);
447
448   // Change a non-forced URL to later non-forced using SetPageThumbnail.
449   db.SetPageThumbnail(url3, 2, Images());
450
451   db.GetPageThumbnails(&urls, &thumbnails);
452   ASSERT_EQ(6u, urls.size());
453   ASSERT_EQ(6u, thumbnails.size());
454   EXPECT_EQ(driveUrl, urls[0].url);
455   EXPECT_EQ(mapsUrl, urls[1].url);
456   EXPECT_EQ(kUrl0, urls[2].url);
457   EXPECT_EQ(plusUrl, urls[4].url);  // Plus moves to third non-forced URL.
458
459   // Remove a non-forced URL.
460   db.RemoveURL(url3);
461
462   db.GetPageThumbnails(&urls, &thumbnails);
463   ASSERT_EQ(5u, urls.size());
464   ASSERT_EQ(5u, thumbnails.size());
465   EXPECT_EQ(driveUrl, urls[0].url);
466   EXPECT_EQ(mapsUrl, urls[1].url);
467   EXPECT_EQ(kUrl0, urls[2].url);
468
469   // Remove a forced URL.
470   db.RemoveURL(url2);
471
472   db.GetPageThumbnails(&urls, &thumbnails);
473   ASSERT_EQ(4u, urls.size());
474   ASSERT_EQ(4u, thumbnails.size());
475   EXPECT_EQ(mapsUrl, urls[0].url);
476   EXPECT_EQ(kUrl0, urls[1].url);
477 }
478
479 }  // namespace history