diff options
author | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-07 02:35:38 +0000 |
---|---|---|
committer | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-07 02:35:38 +0000 |
commit | 41a97c81e1bd78eddc704b00bdad106bf602778c (patch) | |
tree | 9fa9ee2eb911762eab05aea401f17e0c87ea6f3a /sql/connection_unittest.cc | |
parent | f9036e33644b0fccd9987cb759a17ba7e51b0b2e (diff) | |
download | chromium_src-41a97c81e1bd78eddc704b00bdad106bf602778c.zip chromium_src-41a97c81e1bd78eddc704b00bdad106bf602778c.tar.gz chromium_src-41a97c81e1bd78eddc704b00bdad106bf602778c.tar.bz2 |
Implement sql::Connection::RazeAndClose().
Raze() clears out the database, but cannot be called within a
transaction. Close() can only be called once all statements have
cleared. Error callbacks happen while executing statements (thus
often in a transaction, and at least one statement is outstanding).
RazeAndClose() forcibly breaks outstanding transactions, calls Raze()
to clear the database, then calls Close() to close the handle. All
future operations against the database should fail safely (without
affecting storage and without crashing).
BUG=166419, 136846
Review URL: https://chromiumcodereview.appspot.com/12096073
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@181152 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sql/connection_unittest.cc')
-rw-r--r-- | sql/connection_unittest.cc | 110 |
1 files changed, 106 insertions, 4 deletions
diff --git a/sql/connection_unittest.cc b/sql/connection_unittest.cc index 983a689..2aaeb27 100644 --- a/sql/connection_unittest.cc +++ b/sql/connection_unittest.cc @@ -4,6 +4,7 @@ #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/logging.h" #include "sql/connection.h" #include "sql/meta_table.h" #include "sql/statement.h" @@ -279,6 +280,111 @@ TEST_F(SQLConnectionTest, RazeLocked) { ASSERT_TRUE(db().Raze()); } +// Basic test of RazeAndClose() operation. +TEST_F(SQLConnectionTest, RazeAndClose) { + const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; + const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; + + // Test that RazeAndClose() closes the database, and that the + // database is empty when re-opened. + ASSERT_TRUE(db().Execute(kCreateSql)); + ASSERT_TRUE(db().Execute(kPopulateSql)); + ASSERT_TRUE(db().RazeAndClose()); + ASSERT_FALSE(db().is_open()); + db().Close(); + ASSERT_TRUE(db().Open(db_path())); + { + sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master")); + ASSERT_FALSE(s.Step()); + } + + // Test that RazeAndClose() can break transactions. + ASSERT_TRUE(db().Execute(kCreateSql)); + ASSERT_TRUE(db().Execute(kPopulateSql)); + ASSERT_TRUE(db().BeginTransaction()); + ASSERT_TRUE(db().RazeAndClose()); + ASSERT_FALSE(db().is_open()); + ASSERT_FALSE(db().CommitTransaction()); + db().Close(); + ASSERT_TRUE(db().Open(db_path())); + { + sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master")); + ASSERT_FALSE(s.Step()); + } +} + +// Test that various operations fail without crashing after +// RazeAndClose(). +TEST_F(SQLConnectionTest, RazeAndCloseDiagnostics) { + const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"; + const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)"; + const char* kSimpleSql = "SELECT 1"; + + ASSERT_TRUE(db().Execute(kCreateSql)); + ASSERT_TRUE(db().Execute(kPopulateSql)); + + // Test baseline expectations. + db().Preload(); + ASSERT_TRUE(db().DoesTableExist("foo")); + ASSERT_TRUE(db().IsSQLValid(kSimpleSql)); + ASSERT_EQ(SQLITE_OK, db().ExecuteAndReturnErrorCode(kSimpleSql)); + ASSERT_TRUE(db().Execute(kSimpleSql)); + ASSERT_TRUE(db().is_open()); + { + sql::Statement s(db().GetUniqueStatement(kSimpleSql)); + ASSERT_TRUE(s.Step()); + } + { + sql::Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql)); + ASSERT_TRUE(s.Step()); + } + ASSERT_TRUE(db().BeginTransaction()); + ASSERT_TRUE(db().CommitTransaction()); + ASSERT_TRUE(db().BeginTransaction()); + db().RollbackTransaction(); + + ASSERT_TRUE(db().RazeAndClose()); + + // At this point, they should all fail, but not crash. + db().Preload(); + ASSERT_FALSE(db().DoesTableExist("foo")); + ASSERT_FALSE(db().IsSQLValid(kSimpleSql)); + ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode(kSimpleSql)); + ASSERT_FALSE(db().Execute(kSimpleSql)); + ASSERT_FALSE(db().is_open()); + { + sql::Statement s(db().GetUniqueStatement(kSimpleSql)); + ASSERT_FALSE(s.Step()); + } + { + sql::Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql)); + ASSERT_FALSE(s.Step()); + } + ASSERT_FALSE(db().BeginTransaction()); + ASSERT_FALSE(db().CommitTransaction()); + ASSERT_FALSE(db().BeginTransaction()); + db().RollbackTransaction(); + + // Close normally to reset the poisoned flag. + db().Close(); + + // DEATH tests not supported on Android or iOS. +#if !defined(OS_ANDROID) && !defined(OS_IOS) + // Once the real Close() has been called, various calls enforce API + // usage by becoming fatal in debug mode. Since DEATH tests are + // expensive, just test one of them. + if (DLOG_IS_ON(FATAL)) { + ASSERT_DEATH({ + db().IsSQLValid(kSimpleSql); + }, "Illegal use of connection without a db"); + } +#endif +} + +// TODO(shess): Spin up a background thread to hold other_db, to more +// closely match real life. That would also allow testing +// RazeWithTimeout(). + #if defined(OS_ANDROID) TEST_F(SQLConnectionTest, SetTempDirForSQL) { @@ -291,7 +397,3 @@ TEST_F(SQLConnectionTest, SetTempDirForSQL) { ASSERT_TRUE(meta_table.Init(&db(), 4, 4)); } #endif - -// TODO(shess): Spin up a background thread to hold other_db, to more -// closely match real life. That would also allow testing -// RazeWithTimeout(). |