Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: chrome/browser/history/top_sites_database_unittest.cc

Issue 870063002: Componentize TopSites, TopSitesBackend, TopSitesDatabase (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@815983002
Patch Set: Fix typo Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 void SetUp() override {
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
OLDNEW
« no previous file with comments | « chrome/browser/history/top_sites_database.cc ('k') | chrome/browser/history/top_sites_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698