Chromium Code Reviews| Index: chrome/browser/history/history_unittest.cc |
| diff --git a/chrome/browser/history/history_unittest.cc b/chrome/browser/history/history_unittest.cc |
| index 88dd6d4dd2f9dbb760635036ca84f36ad5ea94c8..3ded67fca8937d2b4f1f319f814e38ced1c72ade 100644 |
| --- a/chrome/browser/history/history_unittest.cc |
| +++ b/chrome/browser/history/history_unittest.cc |
| @@ -37,6 +37,7 @@ |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/string_util.h" |
| +#include "base/stringprintf.h" |
| #include "base/time.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/history/download_row.h" |
| @@ -44,6 +45,7 @@ |
| #include "chrome/browser/history/history_database.h" |
| #include "chrome/browser/history/history_notifications.h" |
| #include "chrome/browser/history/history_service.h" |
| +#include "chrome/browser/history/history_unittest_base.h" |
| #include "chrome/browser/history/in_memory_database.h" |
| #include "chrome/browser/history/in_memory_history_backend.h" |
| #include "chrome/browser/history/page_usage_data.h" |
| @@ -99,7 +101,7 @@ class BackendDelegate : public HistoryBackend::Delegate { |
| // This must be outside the anonymous namespace for the friend statement in |
| // HistoryBackend to work. |
| -class HistoryBackendDBTest : public testing::Test { |
| +class HistoryBackendDBTest : public HistoryUnitTestBase { |
| public: |
| HistoryBackendDBTest() : db_(NULL) { |
| } |
| @@ -121,6 +123,16 @@ class HistoryBackendDBTest : public testing::Test { |
| "HistoryBackend::Init"; |
| } |
| + void CreateDBVersion(int version) { |
|
sky
2013/01/31 01:28:49
Wrap all calls to this in ASSERT_NO_FATAL_FAILURE.
Randy Smith (Not in Mondays)
2013/01/31 20:11:28
Done, but could you clue me as to what the goal is
|
| + FilePath data_path; |
| + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path)); |
| + data_path = data_path.AppendASCII("History"); |
| + data_path = data_path.Append(StringPrintf("history.%d.sql", version)); |
| + ASSERT_NO_FATAL_FAILURE( |
| + ExecuteSQLScript(data_path, history_dir_.Append( |
| + chrome::kHistoryFilename))); |
| + } |
| + |
| // testing::Test |
| virtual void SetUp() { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| @@ -145,15 +157,21 @@ class HistoryBackendDBTest : public testing::Test { |
| } |
| int64 AddDownload(DownloadItem::DownloadState state, const Time& time) { |
| + std::vector<GURL> url_chain; |
| + url_chain.push_back(GURL("foo-url")); |
| + |
| DownloadRow download( |
| FilePath(FILE_PATH_LITERAL("foo-path")), |
| - GURL("foo-url"), |
| + FilePath(FILE_PATH_LITERAL("foo-path")), |
| + url_chain, |
| GURL(""), |
| time, |
| time, |
| 0, |
| 512, |
| state, |
| + content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, |
| + content::DOWNLOAD_INTERRUPT_REASON_NONE, |
| 0, |
| 0); |
| return db_->CreateDownload(download); |
| @@ -213,19 +231,13 @@ TEST_F(HistoryBackendDBTest, ClearBrowsingData_Downloads) { |
| } |
| TEST_F(HistoryBackendDBTest, MigrateDownloadsState) { |
| - // Create the db and close it so that we can reopen it directly. |
| - CreateBackendAndDatabase(); |
| - DeleteBackend(); |
| + // Create the db we want. |
| + CreateDBVersion(22); |
| { |
| - // Re-open the db for manual manipulation. |
| + // Open the db for manual manipulation. |
| sql::Connection db; |
| ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); |
| - { |
| - // Manually force the version to 22. |
| - sql::Statement version22(db.GetUniqueStatement( |
| - "UPDATE meta SET value=22 WHERE key='version'")); |
| - ASSERT_TRUE(version22.Run()); |
| - } |
| + |
| // Manually insert corrupted rows; there's infrastructure in place now to |
| // make this impossible, at least according to the test above. |
| for (int state = 0; state < 5; ++state) { |
| @@ -247,8 +259,8 @@ TEST_F(HistoryBackendDBTest, MigrateDownloadsState) { |
| } |
| // Re-open the db using the HistoryDatabase, which should migrate from version |
| - // 22 to 23, fixing just the row whose state was 3. Then close the db so that |
| - // we can re-open it directly. |
| + // 22 to the current version, fixing just the row whose state was 3. |
| + // Then close the db so that we can re-open it directly. |
| CreateBackendAndDatabase(); |
| DeleteBackend(); |
| { |
| @@ -283,6 +295,258 @@ TEST_F(HistoryBackendDBTest, MigrateDownloadsState) { |
| } |
| } |
| +TEST_F(HistoryBackendDBTest, MigrateDownloadsReasonPathsAndDangerType) { |
| + Time now(base::Time::Now()); |
| + |
| + // Create the db we want. The schema didn't change from 22->23, so just |
| + // re-use the v22 file. |
| + CreateDBVersion(22); |
| + { |
| + // Re-open the db for manual manipulation. |
| + sql::Connection db; |
| + ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); |
| + |
| + // Manually insert some rows. |
| + sql::Statement s(db.GetUniqueStatement( |
| + "INSERT INTO downloads (id, full_path, url, start_time, " |
| + "received_bytes, total_bytes, state, end_time, opened) VALUES " |
| + "(?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
| + |
| + int64 db_handle = 0; |
|
sky
2013/01/31 01:28:49
THis code isn't particularly readable. Can you cre
Randy Smith (Not in Mondays)
2013/01/31 20:11:28
I can, and if it's important to you I will. But I
|
| + // Null path. |
| + s.BindInt64(0, ++db_handle); |
| + s.BindString(1, ""); |
| + s.BindString(2, "http://whatever.com/index.html"); |
| + s.BindInt64(3, now.ToTimeT()); |
| + s.BindInt64(4, 100); |
| + s.BindInt64(5, 100); |
| + s.BindInt(6, 1); |
| + s.BindInt64(7, now.ToTimeT()); |
| + s.BindInt(8, 1); |
| + ASSERT_TRUE(s.Run()); |
| + s.Reset(true); |
| + |
| + // Non-null path. |
| + s.BindInt64(0, ++db_handle); |
| + s.BindString(1, "/path/to/some/file"); |
| + s.BindString(2, "http://whatever.com/index1.html"); |
| + s.BindInt64(3, now.ToTimeT()); |
| + s.BindInt64(4, 100); |
| + s.BindInt64(5, 100); |
| + s.BindInt(6, 1); |
| + s.BindInt64(7, now.ToTimeT()); |
| + s.BindInt(8, 1); |
| + ASSERT_TRUE(s.Run()); |
| + } |
| + |
| + // Re-open the db using the HistoryDatabase, which should migrate from version |
| + // 23 to 24, creating the new tables and creating the new path, reason, |
| + // and danger columns. |
| + CreateBackendAndDatabase(); |
| + DeleteBackend(); |
| + { |
| + // Re-open the db for manual manipulation. |
| + sql::Connection db; |
| + ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); |
| + { |
| + // The version should have been updated. |
| + int cur_version = HistoryDatabase::GetCurrentVersion(); |
| + ASSERT_LT(23, cur_version); |
| + sql::Statement s(db.GetUniqueStatement( |
| + "SELECT value FROM meta WHERE key = 'version'")); |
| + EXPECT_TRUE(s.Step()); |
| + EXPECT_EQ(cur_version, s.ColumnInt(0)); |
| + } |
| + { |
| + base::Time nowish(base::Time::FromTimeT(now.ToTimeT())); |
| + |
| + // Confirm downloads table is valid. |
| + sql::Statement statement(db.GetUniqueStatement( |
| + "SELECT id, interrupt_reason, current_path, target_path, " |
| + " danger_type, start_time, end_time " |
| + "FROM downloads ORDER BY id")); |
| + EXPECT_TRUE(statement.Step()); |
| + EXPECT_EQ(1, statement.ColumnInt64(0)); |
| + EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, |
| + statement.ColumnInt(1)); |
| + EXPECT_EQ("", statement.ColumnString(2)); |
| + EXPECT_EQ("", statement.ColumnString(3)); |
| + // Implicit dependence on value of kDangerTypeNotDangerous from |
| + // download_database.cc. |
| + EXPECT_EQ(0, statement.ColumnInt(4)); |
| + EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5)); |
| + EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6)); |
| + |
| + EXPECT_TRUE(statement.Step()); |
| + EXPECT_EQ(2, statement.ColumnInt64(0)); |
| + EXPECT_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, |
| + statement.ColumnInt(1)); |
| + EXPECT_EQ("/path/to/some/file", statement.ColumnString(2)); |
| + EXPECT_EQ("/path/to/some/file", statement.ColumnString(3)); |
| + EXPECT_EQ(0, statement.ColumnInt(4)); |
| + EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(5)); |
| + EXPECT_EQ(nowish.ToInternalValue(), statement.ColumnInt64(6)); |
| + |
| + EXPECT_FALSE(statement.Step()); |
| + } |
| + { |
| + // Confirm downloads_url_chains table is valid. |
| + sql::Statement statement(db.GetUniqueStatement( |
| + "SELECT id, chain_index, url FROM downloads_url_chains " |
| + " ORDER BY id, chain_index")); |
| + EXPECT_TRUE(statement.Step()); |
| + EXPECT_EQ(1, statement.ColumnInt64(0)); |
| + EXPECT_EQ(0, statement.ColumnInt(1)); |
| + EXPECT_EQ("http://whatever.com/index.html", statement.ColumnString(2)); |
| + |
| + EXPECT_TRUE(statement.Step()); |
| + EXPECT_EQ(2, statement.ColumnInt64(0)); |
| + EXPECT_EQ(0, statement.ColumnInt(1)); |
| + EXPECT_EQ("http://whatever.com/index1.html", statement.ColumnString(2)); |
| + |
| + EXPECT_FALSE(statement.Step()); |
| + } |
| + } |
| +} |
| + |
| +TEST_F(HistoryBackendDBTest, ConfirmDownloadRowCreateAndDelete) { |
| + // Create the DB. |
| + CreateBackendAndDatabase(); |
| + |
| + base::Time now(base::Time::Now()); |
| + |
| + // Add some downloads. |
| + AddDownload(DownloadItem::COMPLETE, now); |
| + int64 did2 = AddDownload(DownloadItem::COMPLETE, now + |
| + base::TimeDelta::FromDays(2)); |
| + int64 did3 = AddDownload(DownloadItem::COMPLETE, now - |
| + base::TimeDelta::FromDays(2)); |
| + |
| + // Confirm that resulted in the correct number of rows in the DB. |
| + DeleteBackend(); |
| + { |
| + sql::Connection db; |
| + ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); |
| + sql::Statement statement(db.GetUniqueStatement( |
| + "Select Count(*) from downloads")); |
| + EXPECT_TRUE(statement.Step()); |
| + EXPECT_EQ(3, statement.ColumnInt(0)); |
| + |
| + sql::Statement statement1(db.GetUniqueStatement( |
| + "Select Count(*) from downloads_url_chains")); |
| + EXPECT_TRUE(statement1.Step()); |
| + EXPECT_EQ(3, statement1.ColumnInt(0)); |
| + } |
| + |
| + // Delete some rows and make sure the results are still correct. |
| + CreateBackendAndDatabase(); |
| + db_->RemoveDownload(did2); |
| + db_->RemoveDownload(did3); |
| + DeleteBackend(); |
| + { |
| + sql::Connection db; |
| + ASSERT_TRUE(db.Open(history_dir_.Append(chrome::kHistoryFilename))); |
| + sql::Statement statement(db.GetUniqueStatement( |
| + "Select Count(*) from downloads")); |
| + EXPECT_TRUE(statement.Step()); |
| + EXPECT_EQ(1, statement.ColumnInt(0)); |
| + |
| + sql::Statement statement1(db.GetUniqueStatement( |
| + "Select Count(*) from downloads_url_chains")); |
| + EXPECT_TRUE(statement1.Step()); |
| + EXPECT_EQ(1, statement1.ColumnInt(0)); |
| + } |
| +} |
| + |
| +struct InterruptReasonAssociation { |
| + std::string name; |
| + int value; |
| +}; |
| + |
| +// Test is dependent on interrupt reasons being listed in header file |
| +// in order. |
| +const InterruptReasonAssociation current_reasons[] = { |
| +#define INTERRUPT_REASON(a, b) { #a, b }, |
| +#include "content/public/browser/download_interrupt_reason_values.h" |
| +#undef INTERRUPT_REASON |
| +}; |
| + |
| +// This represents a list of all reasons we've previously used; |
| +// Do Not Remove Any Entries From This List. |
| +const InterruptReasonAssociation historical_reasons[] = { |
| + {"FILE_FAILED", 1}, |
| + {"FILE_ACCESS_DENIED", 2}, |
| + {"FILE_NO_SPACE", 3}, |
| + {"FILE_NAME_TOO_LONG", 5}, |
| + {"FILE_TOO_LARGE", 6}, |
| + {"FILE_VIRUS_INFECTED", 7}, |
| + {"FILE_TRANSIENT_ERROR", 10}, |
| + {"FILE_BLOCKED", 11}, |
| + {"FILE_SECURITY_CHECK_FAILED", 12}, |
| + {"FILE_TOO_SHORT", 13}, |
| + {"NETWORK_FAILED", 20}, |
| + {"NETWORK_TIMEOUT", 21}, |
| + {"NETWORK_DISCONNECTED", 22}, |
| + {"NETWORK_SERVER_DOWN", 23}, |
| + {"SERVER_FAILED", 30}, |
| + {"SERVER_NO_RANGE", 31}, |
| + {"SERVER_PRECONDITION", 32}, |
| + {"SERVER_BAD_CONTENT", 33}, |
| + {"USER_CANCELED", 40}, |
| + {"USER_SHUTDOWN", 41}, |
| + {"CRASH", 50}, |
| +}; |
| + |
| +// Make sure no one has changed a DownloadInterruptReason we've previously |
| +// persisted. |
| +TEST_F(HistoryBackendDBTest, |
| + ConfirmDownloadInterruptReasonBackwardsCompatible) { |
| + // Are there any cases in which a historical number has been repurposed |
| + // for an error other than it's original? |
| + for (size_t i = 0; i < arraysize(current_reasons); i++) { |
| + const InterruptReasonAssociation& cur_reason(current_reasons[i]); |
| + bool found = false; |
| + |
| + for (size_t j = 0; j < arraysize(historical_reasons); ++j) { |
| + const InterruptReasonAssociation& hist_reason(historical_reasons[j]); |
| + |
| + if (hist_reason.value == cur_reason.value) { |
| + EXPECT_EQ(cur_reason.name, hist_reason.name) |
| + << "Same integer value used for old error \"" |
| + << hist_reason.name |
| + << "\" as for new error \"" |
| + << cur_reason.name |
| + << "\"." << std::endl |
| + << "**This will cause database conflicts with persisted values**" |
| + << std::endl |
| + << "Please assign a new, non-conflicting value for the new error."; |
| + } |
| + |
| + if (hist_reason.name == cur_reason.name) { |
| + EXPECT_EQ(cur_reason.value, hist_reason.value) |
| + << "Same name (\"" << hist_reason.name |
| + << "\") maps to a different value historically (" |
| + << hist_reason.value << ") and currently (" |
| + << cur_reason.value << ")" << std::endl |
| + << "This may cause database conflicts with persisted values" |
| + << std::endl |
| + << "If this error is the same as the old one, you should" |
| + << std::endl |
| + << "use the old value, and if it is different, you should" |
| + << std::endl |
| + << "use a new name."; |
| + |
| + found = true; |
| + } |
| + } |
| + |
| + EXPECT_TRUE(found) |
| + << "Error \"" << cur_reason.name << "\" not found in historical list." |
| + << std::endl |
| + << "Please add it."; |
| + } |
| +} |
| + |
| // The tracker uses RenderProcessHost pointers for scoping but never |
| // dereferences them. We use ints because it's easier. This function converts |
| // between the two. |