Index: sync/syncable/directory_backing_store_unittest.cc |
diff --git a/sync/syncable/directory_backing_store_unittest.cc b/sync/syncable/directory_backing_store_unittest.cc |
index ed721e08ae1c15b4a41833bd66fd691fa51b1823..a45a25b92d53b596cc9f93d790d1bd5817b6bdc4 100644 |
--- a/sync/syncable/directory_backing_store_unittest.cc |
+++ b/sync/syncable/directory_backing_store_unittest.cc |
@@ -10,10 +10,14 @@ |
#include "base/files/file_util.h" |
#include "base/files/scoped_temp_dir.h" |
#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
#include "base/stl_util.h" |
#include "base/strings/string_number_conversions.h" |
#include "sql/connection.h" |
#include "sql/statement.h" |
+#include "sql/test/scoped_error_ignorer.h" |
+#include "sql/test/test_helpers.h" |
#include "sync/base/sync_export.h" |
#include "sync/internal_api/public/base/node_ordinal.h" |
#include "sync/protocol/bookmark_specifics.pb.h" |
@@ -26,6 +30,15 @@ |
#include "sync/util/time.h" |
#include "testing/gtest/include/gtest/gtest-param-test.h" |
+namespace { |
+ |
+// A handler that simply sets |catastrophic_error_handler_was_called| to true. |
+void CatastrophicErrorHandler(bool* catastrophic_error_handler_was_called) { |
+ *catastrophic_error_handler_was_called = true; |
+} |
+ |
+} // namespace |
+ |
namespace syncer { |
namespace syncable { |
@@ -89,6 +102,7 @@ class MigrationTest : public testing::TestWithParam<int> { |
} |
private: |
+ base::MessageLoop message_loop_; |
base::ScopedTempDir temp_dir_; |
}; |
@@ -3995,5 +4009,77 @@ TEST_F(DirectoryBackingStoreTest, IncreaseDatabasePageSizeFrom4KTo32K) { |
EXPECT_EQ(32768, pageSize); |
} |
+// See that a catastrophic error handler remains set across instances of the |
+// underlying sql:Connection. |
+TEST_F(DirectoryBackingStoreTest, CatastrophicErrorHandler_KeptAcrossReset) { |
+ scoped_ptr<OnDiskDirectoryBackingStoreForTest> dbs( |
+ new OnDiskDirectoryBackingStoreForTest(GetUsername(), GetDatabasePath())); |
+ // See that by default there is no catastrophic error handler. |
+ ASSERT_FALSE(dbs->db_->has_error_callback()); |
+ // Set one and see that it was set. |
+ dbs->SetCatastrophicErrorHandler( |
+ base::Bind(&CatastrophicErrorHandler, nullptr)); |
+ ASSERT_TRUE(dbs->db_->has_error_callback()); |
+ // Recreate the Connection and see that the handler remains set. |
+ dbs->ResetAndCreateConnection(); |
+ ASSERT_TRUE(dbs->db_->has_error_callback()); |
+} |
+ |
+// Verify that database corruption will trigger the catastrohpic error handler. |
+TEST_F(DirectoryBackingStoreTest, CatastrophicErrorHandler_Invocation) { |
+ bool was_called = false; |
+ const base::Closure handler = |
Nicolas Zea
2015/04/13 20:16:33
nit: const vars should be of form kVariableName.
maniscalco
2015/04/14 17:44:14
Definitely when they are global or class scoped.
|
+ base::Bind(&CatastrophicErrorHandler, &was_called); |
+ { |
+ scoped_ptr<OnDiskDirectoryBackingStoreForTest> dbs( |
+ new OnDiskDirectoryBackingStoreForTest(GetUsername(), |
+ GetDatabasePath())); |
+ dbs->SetCatastrophicErrorHandler(handler); |
+ ASSERT_TRUE(dbs->db_->has_error_callback()); |
+ // Load the DB, and save one entry. |
+ ASSERT_TRUE(LoadAndIgnoreReturnedData(dbs.get())); |
+ ASSERT_FALSE(dbs->DidFailFirstOpenAttempt()); |
+ Directory::SaveChangesSnapshot snapshot; |
+ scoped_ptr<EntryKernel> entry(new EntryKernel()); |
+ entry->put(ID, Id::CreateFromClientString("test_entry")); |
+ entry->put(META_HANDLE, 2); |
+ entry->mark_dirty(NULL); |
+ snapshot.dirty_metas.insert(entry.release()); |
+ ASSERT_TRUE(dbs->SaveChanges(snapshot)); |
+ } |
+ |
+ base::RunLoop().RunUntilIdle(); |
+ // No catastrophic errors have happened. See that it hasn't be called yet. |
+ ASSERT_FALSE(was_called); |
+ |
+ // Corrupt the DB. Some forms of corruption (like this one) will be detected |
+ // upon loading the Sync DB. |
+ ASSERT_TRUE(sql::test::CorruptSizeInHeader(GetDatabasePath())); |
+ |
+ { |
+ scoped_ptr<OnDiskDirectoryBackingStoreForTest> dbs( |
+ new OnDiskDirectoryBackingStoreForTest(GetUsername(), |
+ GetDatabasePath())); |
+ dbs->SetCatastrophicErrorHandler(handler); |
+ ASSERT_TRUE(dbs->db_->has_error_callback()); |
+ { |
+ // The corruption will be detected when we attempt to load the data. Use a |
+ // ScopedErrorIgnorer to ensure we don't crash in debug builds. |
+ sql::ScopedErrorIgnorer error_ignorer; |
+ error_ignorer.IgnoreError(SQLITE_CORRUPT); |
+ ASSERT_TRUE(LoadAndIgnoreReturnedData(dbs.get())); |
+ ASSERT_TRUE(error_ignorer.CheckIgnoredErrors()); |
+ } |
+ // See that the first open failed as expected. |
+ ASSERT_TRUE(dbs->DidFailFirstOpenAttempt()); |
+ } |
+ |
+ // At this point the handler has been posted but not executed. |
+ ASSERT_FALSE(was_called); |
+ // Pump the message loop and see that it is executed. |
+ base::RunLoop().RunUntilIdle(); |
+ ASSERT_TRUE(was_called); |
+} |
+ |
} // namespace syncable |
} // namespace syncer |