Index: sql/connection_unittest.cc |
diff --git a/sql/connection_unittest.cc b/sql/connection_unittest.cc |
index 1afd2dded3366baffe2dc6f831225a8b5395020f..27e68d45947f8d04032bf85553b411778a9b029a 100644 |
--- a/sql/connection_unittest.cc |
+++ b/sql/connection_unittest.cc |
@@ -2,16 +2,67 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include "base/bind.h" |
#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" |
+#include "sql/test/error_callback_support.h" |
#include "sql/test/scoped_error_ignorer.h" |
#include "testing/gtest/include/gtest/gtest.h" |
#include "third_party/sqlite/sqlite3.h" |
+namespace { |
+ |
+// Track the number of valid references which share the same pointer. |
+// This is used to allow testing an implicitly use-after-free case by |
+// explicitly having the ref count live longer than the object. |
+class RefCounter { |
+ public: |
+ RefCounter(size_t* counter) |
+ : counter_(counter) { |
+ (*counter_)++; |
+ } |
+ RefCounter(const RefCounter& other) |
+ : counter_(other.counter_) { |
+ (*counter_)++; |
+ } |
+ ~RefCounter() { |
+ (*counter_)--; |
+ } |
+ |
+ private: |
+ size_t* counter_; |
+ |
+ DISALLOW_ASSIGN(RefCounter); |
+}; |
+ |
+// Empty callback for implementation of ErrorCallbackSetHelper(). |
+void IgnoreErrorCallback(int error, sql::Statement* stmt) { |
+} |
+ |
+void ErrorCallbackSetHelper(sql::Connection* db, |
+ size_t* counter, |
+ const RefCounter& r, |
+ int error, sql::Statement* stmt) { |
+ // The ref count should not go to zero when changing the callback. |
+ EXPECT_GT(*counter, 0u); |
+ db->set_error_callback(base::Bind(&IgnoreErrorCallback)); |
+ EXPECT_GT(*counter, 0u); |
+} |
+ |
+void ErrorCallbackResetHelper(sql::Connection* db, |
+ size_t* counter, |
+ const RefCounter& r, |
+ int error, sql::Statement* stmt) { |
+ // The ref count should not go to zero when clearing the callback. |
+ EXPECT_GT(*counter, 0u); |
+ db->reset_error_callback(); |
+ EXPECT_GT(*counter, 0u); |
+} |
+ |
class SQLConnectionTest : public testing::Test { |
public: |
SQLConnectionTest() {} |
@@ -150,6 +201,61 @@ TEST_F(SQLConnectionTest, ScopedIgnoreError) { |
ASSERT_TRUE(ignore_errors.CheckIgnoredErrors()); |
} |
+TEST_F(SQLConnectionTest, ErrorCallback) { |
+ const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)"; |
+ ASSERT_TRUE(db().Execute(kCreateSql)); |
+ ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)")); |
+ |
+ int error = SQLITE_OK; |
+ { |
+ sql::ScopedErrorCallback sec( |
+ &db(), base::Bind(&sql::CaptureErrorCallback, &error)); |
+ |
+ // Inserting something other than a number into the primary key |
+ // should result in the callback seeing SQLITE_MISMATCH. |
+ EXPECT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)")); |
+ EXPECT_EQ(SQLITE_CONSTRAINT, error); |
+ } |
+ |
+ // Callback is no longer in force due to reset. |
+ { |
+ error = SQLITE_OK; |
+ sql::ScopedErrorIgnorer ignore_errors; |
+ ignore_errors.IgnoreError(SQLITE_CONSTRAINT); |
+ ASSERT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)")); |
+ ASSERT_TRUE(ignore_errors.CheckIgnoredErrors()); |
+ EXPECT_EQ(SQLITE_OK, error); |
+ } |
+ |
+ // base::Bind() can curry arguments to be passed by const reference |
+ // to the callback function. If the callback function causes |
+ // re/set_error_callback() to be called, the storage for those |
+ // arguments can be deleted. |
+ // |
+ // RefCounter() counts how many objects are live using an external |
+ // count. The same counter is passed to the callback, so that it |
+ // can check directly even if the RefCounter object is no longer |
+ // live. |
+ { |
+ size_t count = 0; |
+ sql::ScopedErrorCallback sec( |
+ &db(), base::Bind(&ErrorCallbackSetHelper, |
+ &db(), &count, RefCounter(&count))); |
+ |
+ EXPECT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)")); |
+ } |
+ |
+ // Same test, but reset_error_callback() case. |
+ { |
+ size_t count = 0; |
+ sql::ScopedErrorCallback sec( |
+ &db(), base::Bind(&ErrorCallbackResetHelper, |
+ &db(), &count, RefCounter(&count))); |
+ |
+ EXPECT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)")); |
+ } |
+} |
+ |
// Test that sql::Connection::Raze() results in a database without the |
// tables from the original database. |
TEST_F(SQLConnectionTest, Raze) { |
@@ -426,3 +532,5 @@ TEST_F(SQLConnectionTest, Delete) { |
EXPECT_FALSE(file_util::PathExists(db_path())); |
EXPECT_FALSE(file_util::PathExists(journal)); |
} |
+ |
+} // namespace |