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

Unified Diff: sql/recover_unittest.cc

Issue 18180013: Scoped recovery module for sql/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Oops, add recover_unittest.cc Created 7 years, 6 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 side-by-side diff with in-line comments
Download patch
Index: sql/recover_unittest.cc
diff --git a/sql/recover_unittest.cc b/sql/recover_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..918031dc578138267d296692ceba0a7a4eaf08b0
--- /dev/null
+++ b/sql/recover_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "sql/connection.h"
+#include "sql/meta_table.h"
+#include "sql/recovery.h"
+#include "sql/statement.h"
+#include "sql/test/scoped_error_ignorer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/sqlite/sqlite3.h"
+
+namespace {
+
+// Helper to return the count of items in sqlite_master. Return -1 in
+// case of error.
+int SqliteMasterCount(sql::Connection* db) {
+ const char* kMasterCount = "SELECT COUNT(*) FROM sqlite_master";
+ sql::Statement s(db->GetUniqueStatement(kMasterCount));
+ return s.Step() ? s.ColumnInt(0) : -1;
+}
+
+// Helper to execute a statement and return stringified results.
+std::string ExecuteWithResults(sql::Connection* db,
+ const char* sql,
+ const char* column_sep,
+ const char* row_sep) {
+ sql::Statement s(db->GetUniqueStatement(sql));
+ std::string ret;
+ while (s.Step()) {
+ for (int i = 0; i < s.ColumnCount(); ++i) {
+ if (i > 0)
+ ret += column_sep;
+ ret += s.ColumnString(i);
+ }
+ ret += row_sep;
+ }
+ return ret;
+}
+
+void DumpMaster(sql::Connection* db, const char* name) {
+ std::string kSql = base::StringPrintf(
+ "SELECT type, name, tbl_name, sql FROM %s ORDER BY 1,2,3,4",
+ name);
+ std::string ret = ExecuteWithResults(db, kSql.c_str(), "|", "\n");
+ if (ret.empty()) {
+ LOG(ERROR) << name << ": <empty>";
+ } else {
+ LOG(ERROR) << name << ":\n" << ret;
+ }
+}
+
+std::string GetSchema(sql::Connection* db) {
+ const char kSql[] =
+ "SELECT COALESCE(sql, name) FROM sqlite_master ORDER BY 1";
+ return ExecuteWithResults(db, kSql, "|", "\n");
+}
+
+class SQLRecoverTest : public testing::Test {
+ public:
+ SQLRecoverTest() {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ ASSERT_TRUE(db_.Open(db_path()));
+ }
+
+ virtual void TearDown() {
+ db_.Close();
+ }
+
+ sql::Connection& db() { return db_; }
+
+ base::FilePath db_path() {
+ return temp_dir_.path().AppendASCII("SQLRecoverTest.db");
+ }
+
+ bool Reopen() {
+ db_.Close();
+ return db_.Open(db_path());
+ }
+
+ private:
+ base::ScopedTempDir temp_dir_;
+ sql::Connection db_;
+};
+
+TEST_F(SQLRecoverTest, RecoverBasic) {
+ const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
+ const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')";
+ ASSERT_TRUE(db().Execute(kCreateSql));
+ ASSERT_TRUE(db().Execute(kInsertSql));
+ ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n");
+
+ // If the Recovery object goes out of scope without being marked
+ // Recovered(), it clears the original database entirely.
+ {
+ sql::Recovery recovery(&db());
+ ASSERT_TRUE(recovery.Open(db_path()));
+ }
+
+ // If the Recovery object goes out of scope without being marked
+ // Recovered(), it clears the original database entirely.
+ ASSERT_TRUE(Reopen());
+ ASSERT_EQ(0, SqliteMasterCount(&db()));
+
+ ASSERT_TRUE(db().Execute(kCreateSql));
+ ASSERT_TRUE(db().Execute(kInsertSql));
+ ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n");
+
+ // Unrecoverable() clears the original database entirely and poisons
+ // the connection.
+ {
+ sql::Recovery recovery(&db());
+ ASSERT_TRUE(recovery.Open(db_path()));
+ recovery.Unrecoverable();
+
+ // TODO(shess): Test that calls to recover.db() start failing.
+ }
+
+ // Unrecoverable() clears the original database entirely.
+ ASSERT_TRUE(Reopen());
+ ASSERT_EQ(0, SqliteMasterCount(&db()));
+
+ ASSERT_TRUE(db().Execute(kCreateSql));
+ ASSERT_TRUE(db().Execute(kInsertSql));
+ ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n");
+
+ // Successfully recover the database by copying from the existing
+ // database.
+ {
+ sql::Recovery recovery(&db());
+ ASSERT_TRUE(recovery.Open(db_path()));
+
+ // Create the new version of the table.
+ ASSERT_TRUE(recovery.db()->Execute(kCreateSql));
+
+ const char kRecoverSql[] = "INSERT INTO x SELECT t FROM corrupt.x";
+ ASSERT_TRUE(recovery.db()->Execute(kRecoverSql));
+
+ // Successfully recovered.
+ ASSERT_TRUE(recovery.Recovered());
+ }
+
+ // Since the database was not corrupt, the entire schema and all
+ // data should be recovered.
+ ASSERT_TRUE(Reopen());
+ ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n");
+
+ const char* kXSql = "SELECT * FROM x ORDER BY 1";
+ ASSERT_EQ(ExecuteWithResults(&db(), kXSql, "|", "\n"),
+ "This is a test\n");
+}
+
+} // namespace

Powered by Google App Engine
This is Rietveld 408576698