Commit 00370898 authored by Shubham Aggarwal's avatar Shubham Aggarwal Committed by Commit Bot

Add test coverage for WAL mode in database_unittest

This change makes it so that all tests in database_unittest.cc now run
both with WAL mode on and off. Better test coverage will help us
transition to using WAL mode by default in the future.

Bug: 78507
Change-Id: I116a8151c1ab4919a78657e46df98b26eddb7f11
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2490664
Commit-Queue: Shubham Aggarwal <shuagga@microsoft.com>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#821790}
parent 1bfcd597
...@@ -114,9 +114,31 @@ class ScopedUmaskSetter { ...@@ -114,9 +114,31 @@ class ScopedUmaskSetter {
} // namespace } // namespace
using SQLDatabaseTest = sql::SQLTestBase; // We use the parameter to run all tests with WAL mode on and off.
class SQLDatabaseTest : public SQLTestBase,
public testing::WithParamInterface<bool> {
public:
SQLDatabaseTest() : SQLTestBase(GetDBOptions()) {}
explicit SQLDatabaseTest(DatabaseOptions options) : SQLTestBase(options) {}
TEST_F(SQLDatabaseTest, Execute) { sql::DatabaseOptions GetDBOptions() {
sql::DatabaseOptions options;
options.wal_mode = IsWALEnabled();
// TODO(crbug.com/1120969): Remove after switching to exclusive mode on by
// default.
options.exclusive_locking = false;
#if defined(OS_FUCHSIA) // Exclusive mode needs to be enabled to enter WAL mode
// on Fuchsia
if (IsWALEnabled()) {
options.exclusive_locking = true;
}
#endif // defined(OS_FUCHSIA)
return options;
}
bool IsWALEnabled() { return GetParam(); }
};
TEST_P(SQLDatabaseTest, Execute) {
// Valid statement should return true. // Valid statement should return true.
ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
EXPECT_EQ(SQLITE_OK, db().GetErrorCode()); EXPECT_EQ(SQLITE_OK, db().GetErrorCode());
...@@ -127,7 +149,7 @@ TEST_F(SQLDatabaseTest, Execute) { ...@@ -127,7 +149,7 @@ TEST_F(SQLDatabaseTest, Execute) {
EXPECT_EQ(SQLITE_ERROR, db().GetErrorCode()); EXPECT_EQ(SQLITE_ERROR, db().GetErrorCode());
} }
TEST_F(SQLDatabaseTest, ExecuteWithErrorCode) { TEST_P(SQLDatabaseTest, ExecuteWithErrorCode) {
ASSERT_EQ(SQLITE_OK, ASSERT_EQ(SQLITE_OK,
db().ExecuteAndReturnErrorCode("CREATE TABLE foo (a, b)")); db().ExecuteAndReturnErrorCode("CREATE TABLE foo (a, b)"));
ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode("CREATE TABLE TABLE")); ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode("CREATE TABLE TABLE"));
...@@ -135,7 +157,7 @@ TEST_F(SQLDatabaseTest, ExecuteWithErrorCode) { ...@@ -135,7 +157,7 @@ TEST_F(SQLDatabaseTest, ExecuteWithErrorCode) {
"INSERT INTO foo(a, b) VALUES (1, 2, 3, 4)")); "INSERT INTO foo(a, b) VALUES (1, 2, 3, 4)"));
} }
TEST_F(SQLDatabaseTest, CachedStatement) { TEST_P(SQLDatabaseTest, CachedStatement) {
sql::StatementID id1 = SQL_FROM_HERE; sql::StatementID id1 = SQL_FROM_HERE;
sql::StatementID id2 = SQL_FROM_HERE; sql::StatementID id2 = SQL_FROM_HERE;
static const char kId1Sql[] = "SELECT a FROM foo"; static const char kId1Sql[] = "SELECT a FROM foo";
...@@ -195,13 +217,13 @@ TEST_F(SQLDatabaseTest, CachedStatement) { ...@@ -195,13 +217,13 @@ TEST_F(SQLDatabaseTest, CachedStatement) {
<< "Using a different SQL with the same statement ID should DCHECK"; << "Using a different SQL with the same statement ID should DCHECK";
} }
TEST_F(SQLDatabaseTest, IsSQLValidTest) { TEST_P(SQLDatabaseTest, IsSQLValidTest) {
ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
ASSERT_TRUE(db().IsSQLValid("SELECT a FROM foo")); ASSERT_TRUE(db().IsSQLValid("SELECT a FROM foo"));
ASSERT_FALSE(db().IsSQLValid("SELECT no_exist FROM foo")); ASSERT_FALSE(db().IsSQLValid("SELECT no_exist FROM foo"));
} }
TEST_F(SQLDatabaseTest, DoesTableExist) { TEST_P(SQLDatabaseTest, DoesTableExist) {
EXPECT_FALSE(db().DoesTableExist("foo")); EXPECT_FALSE(db().DoesTableExist("foo"));
EXPECT_FALSE(db().DoesTableExist("foo_index")); EXPECT_FALSE(db().DoesTableExist("foo_index"));
...@@ -215,7 +237,7 @@ TEST_F(SQLDatabaseTest, DoesTableExist) { ...@@ -215,7 +237,7 @@ TEST_F(SQLDatabaseTest, DoesTableExist) {
EXPECT_FALSE(db().DoesTableExist("FOO")); EXPECT_FALSE(db().DoesTableExist("FOO"));
} }
TEST_F(SQLDatabaseTest, DoesIndexExist) { TEST_P(SQLDatabaseTest, DoesIndexExist) {
ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
EXPECT_FALSE(db().DoesIndexExist("foo")); EXPECT_FALSE(db().DoesIndexExist("foo"));
EXPECT_FALSE(db().DoesIndexExist("foo_ubdex")); EXPECT_FALSE(db().DoesIndexExist("foo_ubdex"));
...@@ -230,7 +252,7 @@ TEST_F(SQLDatabaseTest, DoesIndexExist) { ...@@ -230,7 +252,7 @@ TEST_F(SQLDatabaseTest, DoesIndexExist) {
EXPECT_FALSE(db().DoesIndexExist("FOO_INDEX")); EXPECT_FALSE(db().DoesIndexExist("FOO_INDEX"));
} }
TEST_F(SQLDatabaseTest, DoesViewExist) { TEST_P(SQLDatabaseTest, DoesViewExist) {
EXPECT_FALSE(db().DoesViewExist("voo")); EXPECT_FALSE(db().DoesViewExist("voo"));
ASSERT_TRUE(db().Execute("CREATE VIEW voo (a) AS SELECT 1")); ASSERT_TRUE(db().Execute("CREATE VIEW voo (a) AS SELECT 1"));
EXPECT_FALSE(db().DoesIndexExist("voo")); EXPECT_FALSE(db().DoesIndexExist("voo"));
...@@ -242,7 +264,7 @@ TEST_F(SQLDatabaseTest, DoesViewExist) { ...@@ -242,7 +264,7 @@ TEST_F(SQLDatabaseTest, DoesViewExist) {
EXPECT_FALSE(db().DoesViewExist("VOO")); EXPECT_FALSE(db().DoesViewExist("VOO"));
} }
TEST_F(SQLDatabaseTest, DoesColumnExist) { TEST_P(SQLDatabaseTest, DoesColumnExist) {
ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
EXPECT_FALSE(db().DoesColumnExist("foo", "bar")); EXPECT_FALSE(db().DoesColumnExist("foo", "bar"));
...@@ -257,7 +279,7 @@ TEST_F(SQLDatabaseTest, DoesColumnExist) { ...@@ -257,7 +279,7 @@ TEST_F(SQLDatabaseTest, DoesColumnExist) {
EXPECT_TRUE(db().DoesColumnExist("foo", "A")); EXPECT_TRUE(db().DoesColumnExist("foo", "A"));
} }
TEST_F(SQLDatabaseTest, GetLastInsertRowId) { TEST_P(SQLDatabaseTest, GetLastInsertRowId) {
ASSERT_TRUE(db().Execute("CREATE TABLE foo (id INTEGER PRIMARY KEY, value)")); ASSERT_TRUE(db().Execute("CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"));
ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)")); ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)"));
...@@ -273,7 +295,7 @@ TEST_F(SQLDatabaseTest, GetLastInsertRowId) { ...@@ -273,7 +295,7 @@ TEST_F(SQLDatabaseTest, GetLastInsertRowId) {
EXPECT_EQ(12, s.ColumnInt(0)); EXPECT_EQ(12, s.ColumnInt(0));
} }
TEST_F(SQLDatabaseTest, Rollback) { TEST_P(SQLDatabaseTest, Rollback) {
ASSERT_TRUE(db().BeginTransaction()); ASSERT_TRUE(db().BeginTransaction());
ASSERT_TRUE(db().BeginTransaction()); ASSERT_TRUE(db().BeginTransaction());
EXPECT_EQ(2, db().transaction_nesting()); EXPECT_EQ(2, db().transaction_nesting());
...@@ -284,7 +306,7 @@ TEST_F(SQLDatabaseTest, Rollback) { ...@@ -284,7 +306,7 @@ TEST_F(SQLDatabaseTest, Rollback) {
// Test the scoped error expecter by attempting to insert a duplicate // Test the scoped error expecter by attempting to insert a duplicate
// value into an index. // value into an index.
TEST_F(SQLDatabaseTest, ScopedErrorExpecter) { TEST_P(SQLDatabaseTest, ScopedErrorExpecter) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)")); ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
...@@ -299,7 +321,7 @@ TEST_F(SQLDatabaseTest, ScopedErrorExpecter) { ...@@ -299,7 +321,7 @@ TEST_F(SQLDatabaseTest, ScopedErrorExpecter) {
// Test that clients of GetUntrackedStatement() can test corruption-handling // Test that clients of GetUntrackedStatement() can test corruption-handling
// with ScopedErrorExpecter. // with ScopedErrorExpecter.
TEST_F(SQLDatabaseTest, ScopedIgnoreUntracked) { TEST_P(SQLDatabaseTest, ScopedIgnoreUntracked) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_FALSE(db().DoesTableExist("bar")); ASSERT_FALSE(db().DoesTableExist("bar"));
...@@ -321,7 +343,7 @@ TEST_F(SQLDatabaseTest, ScopedIgnoreUntracked) { ...@@ -321,7 +343,7 @@ TEST_F(SQLDatabaseTest, ScopedIgnoreUntracked) {
} }
} }
TEST_F(SQLDatabaseTest, ErrorCallback) { TEST_P(SQLDatabaseTest, ErrorCallback) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)")); ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
...@@ -378,7 +400,7 @@ TEST_F(SQLDatabaseTest, ErrorCallback) { ...@@ -378,7 +400,7 @@ TEST_F(SQLDatabaseTest, ErrorCallback) {
// Test that sql::Database::Raze() results in a database without the // Test that sql::Database::Raze() results in a database without the
// tables from the original database. // tables from the original database.
TEST_F(SQLDatabaseTest, Raze) { TEST_P(SQLDatabaseTest, Raze) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)")); ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)"));
...@@ -476,7 +498,7 @@ void TestPageSize(const base::FilePath& db_prefix, ...@@ -476,7 +498,7 @@ void TestPageSize(const base::FilePath& db_prefix,
// Verify that sql::Recovery maintains the page size, and the virtual table // Verify that sql::Recovery maintains the page size, and the virtual table
// works with page sizes other than SQLite's default. Also verify the case // works with page sizes other than SQLite's default. Also verify the case
// where the default page size has changed. // where the default page size has changed.
TEST_F(SQLDatabaseTest, RazePageSize) { TEST_P(SQLDatabaseTest, RazePageSize) {
const std::string default_page_size = const std::string default_page_size =
ExecuteWithResult(&db(), "PRAGMA page_size"); ExecuteWithResult(&db(), "PRAGMA page_size");
...@@ -501,11 +523,11 @@ TEST_F(SQLDatabaseTest, RazePageSize) { ...@@ -501,11 +523,11 @@ TEST_F(SQLDatabaseTest, RazePageSize) {
} }
// Test that Raze() results are seen in other connections. // Test that Raze() results are seen in other connections.
TEST_F(SQLDatabaseTest, RazeMultiple) { TEST_P(SQLDatabaseTest, RazeMultiple) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
sql::Database other_db; sql::Database other_db(GetDBOptions());
ASSERT_TRUE(other_db.Open(db_path())); ASSERT_TRUE(other_db.Open(db_path()));
// Check that the second connection sees the table. // Check that the second connection sees the table.
...@@ -517,14 +539,14 @@ TEST_F(SQLDatabaseTest, RazeMultiple) { ...@@ -517,14 +539,14 @@ TEST_F(SQLDatabaseTest, RazeMultiple) {
ASSERT_EQ(0, SqliteMasterCount(&other_db)); ASSERT_EQ(0, SqliteMasterCount(&other_db));
} }
TEST_F(SQLDatabaseTest, RazeLocked) { TEST_P(SQLDatabaseTest, RazeLocked) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
// Open a transaction and write some data in a second connection. // Open a transaction and write some data in a second connection.
// This will acquire a PENDING or EXCLUSIVE transaction, which will // This will acquire a PENDING or EXCLUSIVE transaction, which will
// cause the raze to fail. // cause the raze to fail.
sql::Database other_db; sql::Database other_db(GetDBOptions());
ASSERT_TRUE(other_db.Open(db_path())); ASSERT_TRUE(other_db.Open(db_path()));
ASSERT_TRUE(other_db.BeginTransaction()); ASSERT_TRUE(other_db.BeginTransaction());
const char* kInsertSql = "INSERT INTO foo VALUES (1, 'data')"; const char* kInsertSql = "INSERT INTO foo VALUES (1, 'data')";
...@@ -544,7 +566,7 @@ TEST_F(SQLDatabaseTest, RazeLocked) { ...@@ -544,7 +566,7 @@ TEST_F(SQLDatabaseTest, RazeLocked) {
// blocks raze. // blocks raze.
// This doesn't happen in WAL mode because reads are no longer blocked by // This doesn't happen in WAL mode because reads are no longer blocked by
// write operations when using a WAL. // write operations when using a WAL.
if (!base::FeatureList::IsEnabled(sql::features::kEnableWALModeByDefault)) { if (!IsWALEnabled()) {
const char* kQuery = "SELECT COUNT(*) FROM foo"; const char* kQuery = "SELECT COUNT(*) FROM foo";
sql::Statement s(other_db.GetUniqueStatement(kQuery)); sql::Statement s(other_db.GetUniqueStatement(kQuery));
ASSERT_TRUE(s.Step()); ASSERT_TRUE(s.Step());
...@@ -558,7 +580,7 @@ TEST_F(SQLDatabaseTest, RazeLocked) { ...@@ -558,7 +580,7 @@ TEST_F(SQLDatabaseTest, RazeLocked) {
// Verify that Raze() can handle an empty file. SQLite should treat // Verify that Raze() can handle an empty file. SQLite should treat
// this as an empty database. // this as an empty database.
TEST_F(SQLDatabaseTest, RazeEmptyDB) { TEST_P(SQLDatabaseTest, RazeEmptyDB) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
db().Close(); db().Close();
...@@ -573,7 +595,7 @@ TEST_F(SQLDatabaseTest, RazeEmptyDB) { ...@@ -573,7 +595,7 @@ TEST_F(SQLDatabaseTest, RazeEmptyDB) {
// Verify that Raze() can handle a file of junk. // Verify that Raze() can handle a file of junk.
// Need exclusive mode off here as there are some subtleties (by design) around // Need exclusive mode off here as there are some subtleties (by design) around
// how the cache is used with it on which causes the test to fail. // how the cache is used with it on which causes the test to fail.
TEST_F(SQLDatabaseTest, RazeNOTADB) { TEST_P(SQLDatabaseTest, RazeNOTADB) {
db().Close(); db().Close();
sql::Database::Delete(db_path()); sql::Database::Delete(db_path());
ASSERT_FALSE(GetPathExists(db_path())); ASSERT_FALSE(GetPathExists(db_path()));
...@@ -599,7 +621,7 @@ TEST_F(SQLDatabaseTest, RazeNOTADB) { ...@@ -599,7 +621,7 @@ TEST_F(SQLDatabaseTest, RazeNOTADB) {
} }
// Verify that Raze() can handle a database overwritten with garbage. // Verify that Raze() can handle a database overwritten with garbage.
TEST_F(SQLDatabaseTest, RazeNOTADB2) { TEST_P(SQLDatabaseTest, RazeNOTADB2) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_EQ(1, SqliteMasterCount(&db())); ASSERT_EQ(1, SqliteMasterCount(&db()));
...@@ -628,7 +650,7 @@ TEST_F(SQLDatabaseTest, RazeNOTADB2) { ...@@ -628,7 +650,7 @@ TEST_F(SQLDatabaseTest, RazeNOTADB2) {
// essential for cases where the Open() can fail entirely, so the // essential for cases where the Open() can fail entirely, so the
// Raze() cannot happen later. Additionally test that when the // Raze() cannot happen later. Additionally test that when the
// callback does this during Open(), the open is retried and succeeds. // callback does this during Open(), the open is retried and succeeds.
TEST_F(SQLDatabaseTest, RazeCallbackReopen) { TEST_P(SQLDatabaseTest, RazeCallbackReopen) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_EQ(1, SqliteMasterCount(&db())); ASSERT_EQ(1, SqliteMasterCount(&db()));
...@@ -661,7 +683,7 @@ TEST_F(SQLDatabaseTest, RazeCallbackReopen) { ...@@ -661,7 +683,7 @@ TEST_F(SQLDatabaseTest, RazeCallbackReopen) {
} }
// Basic test of RazeAndClose() operation. // Basic test of RazeAndClose() operation.
TEST_F(SQLDatabaseTest, RazeAndClose) { TEST_P(SQLDatabaseTest, RazeAndClose) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)";
...@@ -689,7 +711,7 @@ TEST_F(SQLDatabaseTest, RazeAndClose) { ...@@ -689,7 +711,7 @@ TEST_F(SQLDatabaseTest, RazeAndClose) {
// Test that various operations fail without crashing after // Test that various operations fail without crashing after
// RazeAndClose(). // RazeAndClose().
TEST_F(SQLDatabaseTest, RazeAndCloseDiagnostics) { TEST_P(SQLDatabaseTest, RazeAndCloseDiagnostics) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)";
const char* kSimpleSql = "SELECT 1"; const char* kSimpleSql = "SELECT 1";
...@@ -763,7 +785,7 @@ TEST_F(SQLDatabaseTest, RazeAndCloseDiagnostics) { ...@@ -763,7 +785,7 @@ TEST_F(SQLDatabaseTest, RazeAndCloseDiagnostics) {
// Test that Raze() turns off memory mapping so that the file is truncated. // Test that Raze() turns off memory mapping so that the file is truncated.
// [This would not cover the case of multiple connections where one of the other // [This would not cover the case of multiple connections where one of the other
// connections is memory-mapped. That is infrequent in Chromium.] // connections is memory-mapped. That is infrequent in Chromium.]
TEST_F(SQLDatabaseTest, RazeTruncate) { TEST_P(SQLDatabaseTest, RazeTruncate) {
// The empty database has 0 or 1 pages. Raze() should leave it with exactly 1 // The empty database has 0 or 1 pages. Raze() should leave it with exactly 1
// page. Not checking directly because auto_vacuum on Android adds a freelist // page. Not checking directly because auto_vacuum on Android adds a freelist
// page. // page.
...@@ -800,7 +822,7 @@ TEST_F(SQLDatabaseTest, RazeTruncate) { ...@@ -800,7 +822,7 @@ TEST_F(SQLDatabaseTest, RazeTruncate) {
} }
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
TEST_F(SQLDatabaseTest, SetTempDirForSQL) { TEST_P(SQLDatabaseTest, SetTempDirForSQL) {
sql::MetaTable meta_table; sql::MetaTable meta_table;
// Below call needs a temporary directory in sqlite3 // Below call needs a temporary directory in sqlite3
// On Android, it can pass only when the temporary directory is set. // On Android, it can pass only when the temporary directory is set.
...@@ -811,27 +833,7 @@ TEST_F(SQLDatabaseTest, SetTempDirForSQL) { ...@@ -811,27 +833,7 @@ TEST_F(SQLDatabaseTest, SetTempDirForSQL) {
} }
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID)
// Contained param indicates whether WAL mode is switched on or not. TEST_P(SQLDatabaseTest, Delete) {
class JournalModeTest : public SQLDatabaseTest,
public testing::WithParamInterface<bool> {
public:
JournalModeTest() : SQLDatabaseTest(GetDBOptions()) {}
sql::DatabaseOptions GetDBOptions() {
sql::DatabaseOptions options;
options.wal_mode = IsWALEnabled();
#if defined(OS_FUCHSIA) // Exclusive mode needs to be enabled to enter WAL mode
// on Fuchsia
if (IsWALEnabled()) {
options.exclusive_locking = true;
}
#endif // defined(OS_FUCHSIA)
return options;
}
bool IsWALEnabled() { return GetParam(); }
};
TEST_P(JournalModeTest, Delete) {
EXPECT_TRUE(db().Execute("CREATE TABLE x (x)")); EXPECT_TRUE(db().Execute("CREATE TABLE x (x)"));
db().Close(); db().Close();
...@@ -853,15 +855,13 @@ TEST_P(JournalModeTest, Delete) { ...@@ -853,15 +855,13 @@ TEST_P(JournalModeTest, Delete) {
// WAL mode is currently not supported on Fuchsia // WAL mode is currently not supported on Fuchsia
#if !defined(OS_FUCHSIA) #if !defined(OS_FUCHSIA)
INSTANTIATE_TEST_SUITE_P(SQLDatabaseTest, JournalModeTest, testing::Bool()); INSTANTIATE_TEST_SUITE_P(JournalMode, SQLDatabaseTest, testing::Bool());
#else #else
INSTANTIATE_TEST_SUITE_P(SQLDatabaseTest, INSTANTIATE_TEST_SUITE_P(JournalMode, SQLDatabaseTest, testing::Values(false));
JournalModeTest,
testing::Values(false));
#endif #endif
#if defined(OS_POSIX) // This test operates on POSIX file permissions. #if defined(OS_POSIX) // This test operates on POSIX file permissions.
TEST_P(JournalModeTest, PosixFilePermissions) { TEST_P(SQLDatabaseTest, PosixFilePermissions) {
db().Close(); db().Close();
sql::Database::Delete(db_path()); sql::Database::Delete(db_path());
ASSERT_FALSE(GetPathExists(db_path())); ASSERT_FALSE(GetPathExists(db_path()));
...@@ -910,7 +910,7 @@ TEST_P(JournalModeTest, PosixFilePermissions) { ...@@ -910,7 +910,7 @@ TEST_P(JournalModeTest, PosixFilePermissions) {
#endif // defined(OS_POSIX) #endif // defined(OS_POSIX)
// Test that errors start happening once Poison() is called. // Test that errors start happening once Poison() is called.
TEST_F(SQLDatabaseTest, Poison) { TEST_P(SQLDatabaseTest, Poison) {
EXPECT_TRUE(db().Execute("CREATE TABLE x (x)")); EXPECT_TRUE(db().Execute("CREATE TABLE x (x)"));
// Before the Poison() call, things generally work. // Before the Poison() call, things generally work.
...@@ -957,7 +957,7 @@ TEST_F(SQLDatabaseTest, Poison) { ...@@ -957,7 +957,7 @@ TEST_F(SQLDatabaseTest, Poison) {
EXPECT_FALSE(db().CommitTransaction()); EXPECT_FALSE(db().CommitTransaction());
} }
TEST_F(SQLDatabaseTest, AttachDatabase) { TEST_P(SQLDatabaseTest, AttachDatabase) {
EXPECT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); EXPECT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
// Create a database to attach to. // Create a database to attach to.
...@@ -990,7 +990,7 @@ TEST_F(SQLDatabaseTest, AttachDatabase) { ...@@ -990,7 +990,7 @@ TEST_F(SQLDatabaseTest, AttachDatabase) {
EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar")); EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar"));
} }
TEST_F(SQLDatabaseTest, AttachDatabaseWithOpenTransaction) { TEST_P(SQLDatabaseTest, AttachDatabaseWithOpenTransaction) {
EXPECT_TRUE(db().Execute("CREATE TABLE foo (a, b)")); EXPECT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
// Create a database to attach to. // Create a database to attach to.
...@@ -1036,7 +1036,7 @@ TEST_F(SQLDatabaseTest, AttachDatabaseWithOpenTransaction) { ...@@ -1036,7 +1036,7 @@ TEST_F(SQLDatabaseTest, AttachDatabaseWithOpenTransaction) {
EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar")); EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar"));
} }
TEST_F(SQLDatabaseTest, Basic_QuickIntegrityCheck) { TEST_P(SQLDatabaseTest, Basic_QuickIntegrityCheck) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kCreateSql));
EXPECT_TRUE(db().QuickIntegrityCheck()); EXPECT_TRUE(db().QuickIntegrityCheck());
...@@ -1053,7 +1053,7 @@ TEST_F(SQLDatabaseTest, Basic_QuickIntegrityCheck) { ...@@ -1053,7 +1053,7 @@ TEST_F(SQLDatabaseTest, Basic_QuickIntegrityCheck) {
} }
} }
TEST_F(SQLDatabaseTest, Basic_FullIntegrityCheck) { TEST_P(SQLDatabaseTest, Basic_FullIntegrityCheck) {
const std::string kOk("ok"); const std::string kOk("ok");
std::vector<std::string> messages; std::vector<std::string> messages;
...@@ -1080,7 +1080,7 @@ TEST_F(SQLDatabaseTest, Basic_FullIntegrityCheck) { ...@@ -1080,7 +1080,7 @@ TEST_F(SQLDatabaseTest, Basic_FullIntegrityCheck) {
// file that would pass the quick check and fail the full check. // file that would pass the quick check and fail the full check.
} }
TEST_F(SQLDatabaseTest, OnMemoryDump) { TEST_P(SQLDatabaseTest, OnMemoryDump) {
base::trace_event::MemoryDumpArgs args = { base::trace_event::MemoryDumpArgs args = {
base::trace_event::MemoryDumpLevelOfDetail::DETAILED}; base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
base::trace_event::ProcessMemoryDump pmd(args); base::trace_event::ProcessMemoryDump pmd(args);
...@@ -1090,7 +1090,7 @@ TEST_F(SQLDatabaseTest, OnMemoryDump) { ...@@ -1090,7 +1090,7 @@ TEST_F(SQLDatabaseTest, OnMemoryDump) {
// Test that the functions to collect diagnostic data run to completion, without // Test that the functions to collect diagnostic data run to completion, without
// worrying too much about what they generate (since that will change). // worrying too much about what they generate (since that will change).
TEST_F(SQLDatabaseTest, CollectDiagnosticInfo) { TEST_P(SQLDatabaseTest, CollectDiagnosticInfo) {
const std::string corruption_info = db().CollectCorruptionInfo(); const std::string corruption_info = db().CollectCorruptionInfo();
EXPECT_NE(std::string::npos, corruption_info.find("SQLITE_CORRUPT")); EXPECT_NE(std::string::npos, corruption_info.find("SQLITE_CORRUPT"));
EXPECT_NE(std::string::npos, corruption_info.find("integrity_check")); EXPECT_NE(std::string::npos, corruption_info.find("integrity_check"));
...@@ -1123,7 +1123,7 @@ TEST_F(SQLDatabaseTest, CollectDiagnosticInfo) { ...@@ -1123,7 +1123,7 @@ TEST_F(SQLDatabaseTest, CollectDiagnosticInfo) {
// Test that a fresh database has mmap enabled by default, if mmap'ed I/O is // Test that a fresh database has mmap enabled by default, if mmap'ed I/O is
// enabled by SQLite. // enabled by SQLite.
TEST_F(SQLDatabaseTest, MmapInitiallyEnabled) { TEST_P(SQLDatabaseTest, MmapInitiallyEnabled) {
{ {
sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size")); sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size"));
ASSERT_TRUE(s.Step()) ASSERT_TRUE(s.Step())
...@@ -1151,7 +1151,7 @@ TEST_F(SQLDatabaseTest, MmapInitiallyEnabled) { ...@@ -1151,7 +1151,7 @@ TEST_F(SQLDatabaseTest, MmapInitiallyEnabled) {
// Test whether a fresh database gets mmap enabled when using alternate status // Test whether a fresh database gets mmap enabled when using alternate status
// storage. // storage.
TEST_F(SQLDatabaseTest, MmapInitiallyEnabledAltStatus) { TEST_P(SQLDatabaseTest, MmapInitiallyEnabledAltStatus) {
// Re-open fresh database with alt-status flag set. // Re-open fresh database with alt-status flag set.
db().Close(); db().Close();
sql::Database::Delete(db_path()); sql::Database::Delete(db_path());
...@@ -1183,7 +1183,7 @@ TEST_F(SQLDatabaseTest, MmapInitiallyEnabledAltStatus) { ...@@ -1183,7 +1183,7 @@ TEST_F(SQLDatabaseTest, MmapInitiallyEnabledAltStatus) {
EXPECT_EQ("0", ExecuteWithResult(&db(), "PRAGMA mmap_size")); EXPECT_EQ("0", ExecuteWithResult(&db(), "PRAGMA mmap_size"));
} }
TEST_F(SQLDatabaseTest, GetAppropriateMmapSize) { TEST_P(SQLDatabaseTest, GetAppropriateMmapSize) {
const size_t kMmapAlot = 25 * 1024 * 1024; const size_t kMmapAlot = 25 * 1024 * 1024;
int64_t mmap_status = MetaTable::kMmapFailure; int64_t mmap_status = MetaTable::kMmapFailure;
...@@ -1226,7 +1226,7 @@ TEST_F(SQLDatabaseTest, GetAppropriateMmapSize) { ...@@ -1226,7 +1226,7 @@ TEST_F(SQLDatabaseTest, GetAppropriateMmapSize) {
ASSERT_EQ(MetaTable::kMmapSuccess, mmap_status); ASSERT_EQ(MetaTable::kMmapSuccess, mmap_status);
} }
TEST_F(SQLDatabaseTest, GetAppropriateMmapSizeAltStatus) { TEST_P(SQLDatabaseTest, GetAppropriateMmapSizeAltStatus) {
const size_t kMmapAlot = 25 * 1024 * 1024; const size_t kMmapAlot = 25 * 1024 * 1024;
// At this point, Database still expects a future [meta] table. // At this point, Database still expects a future [meta] table.
...@@ -1262,7 +1262,7 @@ TEST_F(SQLDatabaseTest, GetAppropriateMmapSizeAltStatus) { ...@@ -1262,7 +1262,7 @@ TEST_F(SQLDatabaseTest, GetAppropriateMmapSizeAltStatus) {
ExecuteWithResult(&db(), "SELECT * FROM MmapStatus")); ExecuteWithResult(&db(), "SELECT * FROM MmapStatus"));
} }
TEST_F(SQLDatabaseTest, GetMemoryUsage) { TEST_P(SQLDatabaseTest, GetMemoryUsage) {
// Databases with mmap enabled may not follow the assumptions below. // Databases with mmap enabled may not follow the assumptions below.
db().Close(); db().Close();
db().set_mmap_disabled(); db().set_mmap_disabled();
...@@ -1287,24 +1287,29 @@ TEST_F(SQLDatabaseTest, GetMemoryUsage) { ...@@ -1287,24 +1287,29 @@ TEST_F(SQLDatabaseTest, GetMemoryUsage) {
class SQLDatabaseTestExclusiveMode : public SQLDatabaseTest { class SQLDatabaseTestExclusiveMode : public SQLDatabaseTest {
public: public:
SQLDatabaseTestExclusiveMode() SQLDatabaseTestExclusiveMode() : SQLDatabaseTest(GetDBOptions()) {}
: SQLDatabaseTest({.exclusive_locking = true}) {}
DatabaseOptions GetDBOptions() {
DatabaseOptions options = SQLDatabaseTest::GetDBOptions();
options.exclusive_locking = true;
return options;
}
}; };
TEST_F(SQLDatabaseTestExclusiveMode, LockingModeExclusive) { TEST_P(SQLDatabaseTestExclusiveMode, LockingModeExclusive) {
EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA locking_mode"), "exclusive"); EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA locking_mode"), "exclusive");
} }
TEST_F(SQLDatabaseTest, LockingModeNormal) { TEST_P(SQLDatabaseTest, LockingModeNormal) {
EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA locking_mode"), "normal"); EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA locking_mode"), "normal");
} }
TEST_P(JournalModeTest, OpenedInCorrectMode) { TEST_P(SQLDatabaseTest, OpenedInCorrectMode) {
std::string expected_mode = IsWALEnabled() ? "wal" : "truncate"; std::string expected_mode = IsWALEnabled() ? "wal" : "truncate";
EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA journal_mode"), expected_mode); EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA journal_mode"), expected_mode);
} }
TEST_P(JournalModeTest, CheckpointDatabase) { TEST_P(SQLDatabaseTest, CheckpointDatabase) {
if (!IsWALEnabled()) if (!IsWALEnabled())
return; return;
...@@ -1338,14 +1343,16 @@ TEST_P(JournalModeTest, CheckpointDatabase) { ...@@ -1338,14 +1343,16 @@ TEST_P(JournalModeTest, CheckpointDatabase) {
EXPECT_EQ(ExecuteWithResult(&db(), "SELECT value FROM foo where id=2"), "2"); EXPECT_EQ(ExecuteWithResult(&db(), "SELECT value FROM foo where id=2"), "2");
} }
TEST_F(SQLDatabaseTest, CorruptSizeInHeaderTest) { TEST_P(SQLDatabaseTest, CorruptSizeInHeaderTest) {
ASSERT_TRUE(db().Execute("CREATE TABLE foo (x)")); ASSERT_TRUE(db().Execute("CREATE TABLE foo (x)"));
ASSERT_TRUE(db().Execute("CREATE TABLE bar (x)")); ASSERT_TRUE(db().Execute("CREATE TABLE bar (x)"));
db().Close();
ASSERT_TRUE(CorruptSizeInHeaderOfDB()); ASSERT_TRUE(CorruptSizeInHeaderOfDB());
{ {
sql::test::ScopedErrorExpecter expecter; sql::test::ScopedErrorExpecter expecter;
expecter.ExpectError(SQLITE_CORRUPT); expecter.ExpectError(SQLITE_CORRUPT);
ASSERT_TRUE(db().Open(db_path()));
EXPECT_FALSE(db().Execute("INSERT INTO foo values (1)")); EXPECT_FALSE(db().Execute("INSERT INTO foo values (1)"));
EXPECT_FALSE(db().DoesTableExist("foo")); EXPECT_FALSE(db().DoesTableExist("foo"));
EXPECT_FALSE(db().DoesTableExist("bar")); EXPECT_FALSE(db().DoesTableExist("bar"));
...@@ -1357,7 +1364,7 @@ TEST_F(SQLDatabaseTest, CorruptSizeInHeaderTest) { ...@@ -1357,7 +1364,7 @@ TEST_F(SQLDatabaseTest, CorruptSizeInHeaderTest) {
// To prevent invalid SQL from accidentally shipping to production, prepared // To prevent invalid SQL from accidentally shipping to production, prepared
// statements which fail to compile with SQLITE_ERROR call DLOG(DCHECK). This // statements which fail to compile with SQLITE_ERROR call DLOG(DCHECK). This
// case cannot be suppressed with an error callback. // case cannot be suppressed with an error callback.
TEST_F(SQLDatabaseTest, CompileError) { TEST_P(SQLDatabaseTest, CompileError) {
// DEATH tests not supported on Android, iOS, or Fuchsia. // DEATH tests not supported on Android, iOS, or Fuchsia.
#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_FUCHSIA) #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_FUCHSIA)
if (DLOG_IS_ON(FATAL)) { if (DLOG_IS_ON(FATAL)) {
......
...@@ -9,6 +9,7 @@ namespace sql { ...@@ -9,6 +9,7 @@ namespace sql {
namespace test { namespace test {
struct ColumnInfo; struct ColumnInfo;
bool CorruptSizeInHeader(const base::FilePath&);
} // namespace test } // namespace test
// Restricts access to APIs internal to the //sql package. // Restricts access to APIs internal to the //sql package.
...@@ -23,6 +24,7 @@ class InternalApiToken { ...@@ -23,6 +24,7 @@ class InternalApiToken {
friend class DatabaseTestPeer; friend class DatabaseTestPeer;
friend class Recovery; friend class Recovery;
friend struct test::ColumnInfo; friend struct test::ColumnInfo;
friend bool test::CorruptSizeInHeader(const base::FilePath&);
}; };
} // namespace sql } // namespace sql
......
...@@ -10,12 +10,12 @@ ...@@ -10,12 +10,12 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "base/check.h"
#include "base/check_op.h" #include "base/check_op.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "sql/database.h" #include "sql/database.h"
#include "sql/sql_features.h"
#include "sql/statement.h" #include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/sqlite/sqlite3.h" #include "third_party/sqlite/sqlite3.h"
...@@ -71,21 +71,49 @@ void WriteBigEndian(unsigned val, unsigned char* buf, size_t bytes) { ...@@ -71,21 +71,49 @@ void WriteBigEndian(unsigned val, unsigned char* buf, size_t bytes) {
} }
} }
bool IsWalDatabase(const base::FilePath& db_path) {
// The SQLite header is documented at:
// https://www.sqlite.org/fileformat.html#the_database_header
//
// Read the entire header.
constexpr int kHeaderSize = 100;
uint8_t header[kHeaderSize];
base::ReadFile(db_path, reinterpret_cast<char*>(header), sizeof(header));
constexpr int kWriteVersionHeaderOffset = 18;
constexpr int kReadVersionHeaderOffset = 19;
// If the read version is unsupported, we can't rely on our ability to
// interpret anything else in the header.
DCHECK_LE(header[kReadVersionHeaderOffset], 2)
<< "Unsupported SQLite file format";
return header[kWriteVersionHeaderOffset] == 2;
}
} // namespace } // namespace
namespace sql { namespace sql {
namespace test { namespace test {
bool CorruptSizeInHeader(const base::FilePath& db_path) { bool CorruptSizeInHeader(const base::FilePath& db_path) {
if (IsWalDatabase(db_path)) {
// Checkpoint the WAL file in Truncate mode before corrupting to ensure that // Checkpoint the WAL file in Truncate mode before corrupting to ensure that
// any future transaction always touches the DB file and not just the WAL // any future transaction always touches the DB file and not just the WAL
// file. // file.
if (base::FeatureList::IsEnabled(sql::features::kEnableWALModeByDefault)) {
base::ScopedAllowBlockingForTesting allow_blocking; base::ScopedAllowBlockingForTesting allow_blocking;
sql::Database db; // TODO: This function doesn't reliably work if connections to the DB are
// still open. Change any uses to ensure that we close all database
// connections before calling this function.
sql::Database db({.exclusive_locking = false, .wal_mode = true});
if (!db.Open(db_path)) if (!db.Open(db_path))
return false; return false;
if (!db.Execute("PRAGMA wal_checkpoint(TRUNCATE)")) int wal_log_size = 0;
int checkpointed_frame_count = 0;
int truncate_result = sqlite3_wal_checkpoint_v2(
db.db(InternalApiToken()), /*zDb=*/nullptr, SQLITE_CHECKPOINT_TRUNCATE,
&wal_log_size, &checkpointed_frame_count);
// A successful checkpoint in truncate mode sets these to zero.
DCHECK(wal_log_size == 0);
DCHECK(checkpointed_frame_count == 0);
if (truncate_result != SQLITE_OK)
return false; return false;
db.Close(); db.Close();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment