| 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 470e858eb4caa563c4ba4db17bbf8e95964d0038..435533c969d409ee3e30468f6667ade1fcb335ab 100644
|
| --- a/sync/syncable/directory_backing_store_unittest.cc
|
| +++ b/sync/syncable/directory_backing_store_unittest.cc
|
| @@ -8,6 +8,7 @@
|
|
|
| #include "base/files/file_path.h"
|
| #include "base/files/file_util.h"
|
| +#include "base/files/scoped_file.h"
|
| #include "base/files/scoped_temp_dir.h"
|
| #include "base/memory/scoped_ptr.h"
|
| #include "base/message_loop/message_loop.h"
|
| @@ -30,6 +31,8 @@
|
| #include "sync/util/time.h"
|
| #include "testing/gtest/include/gtest/gtest-param-test.h"
|
|
|
| +namespace syncer {
|
| +namespace syncable {
|
| namespace {
|
|
|
| // A handler that simply sets |catastrophic_error_handler_was_called| to true.
|
| @@ -37,10 +40,16 @@ void CatastrophicErrorHandler(bool* catastrophic_error_handler_was_called) {
|
| *catastrophic_error_handler_was_called = true;
|
| }
|
|
|
| -} // namespace
|
| +// Create a dirty EntryKernel with an ID derived from |id|.
|
| +scoped_ptr<EntryKernel> CreateEntry(int id) {
|
| + scoped_ptr<EntryKernel> entry(new EntryKernel());
|
| + entry->put(ID, Id::CreateFromClientString(base::Int64ToString(id)));
|
| + entry->put(META_HANDLE, id);
|
| + entry->mark_dirty(NULL);
|
| + return entry;
|
| +}
|
|
|
| -namespace syncer {
|
| -namespace syncable {
|
| +} // namespace
|
|
|
| SYNC_EXPORT_PRIVATE extern const int32 kCurrentDBVersion;
|
|
|
| @@ -4025,8 +4034,10 @@ TEST_F(DirectoryBackingStoreTest, CatastrophicErrorHandler_KeptAcrossReset) {
|
| ASSERT_TRUE(dbs->db_->has_error_callback());
|
| }
|
|
|
| -// Verify that database corruption will trigger the catastrohpic error handler.
|
| -TEST_F(DirectoryBackingStoreTest, CatastrophicErrorHandler_Invocation) {
|
| +// Verify that database corruption encountered during Load will trigger the
|
| +// catastrohpic error handler.
|
| +TEST_F(DirectoryBackingStoreTest,
|
| + CatastrophicErrorHandler_InvocationDuringLoad) {
|
| bool was_called = false;
|
| const base::Closure handler =
|
| base::Bind(&CatastrophicErrorHandler, &was_called);
|
| @@ -4040,11 +4051,7 @@ TEST_F(DirectoryBackingStoreTest, CatastrophicErrorHandler_Invocation) {
|
| 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());
|
| + snapshot.dirty_metas.insert(CreateEntry(2).release());
|
| ASSERT_TRUE(dbs->SaveChanges(snapshot));
|
| }
|
|
|
| @@ -4081,5 +4088,55 @@ TEST_F(DirectoryBackingStoreTest, CatastrophicErrorHandler_Invocation) {
|
| ASSERT_TRUE(was_called);
|
| }
|
|
|
| +// Verify that database corruption encountered during SaveChanges will trigger
|
| +// the catastrohpic error handler.
|
| +TEST_F(DirectoryBackingStoreTest,
|
| + CatastrophicErrorHandler_InvocationDuringSaveChanges) {
|
| + bool was_called = false;
|
| + const base::Closure handler =
|
| + base::Bind(&CatastrophicErrorHandler, &was_called);
|
| +
|
| + // Create a DB with many entries.
|
| + scoped_ptr<OnDiskDirectoryBackingStoreForTest> dbs(
|
| + new OnDiskDirectoryBackingStoreForTest(GetUsername(), GetDatabasePath()));
|
| + dbs->SetCatastrophicErrorHandler(handler);
|
| + ASSERT_TRUE(dbs->db_->has_error_callback());
|
| + ASSERT_TRUE(LoadAndIgnoreReturnedData(dbs.get()));
|
| + ASSERT_FALSE(dbs->DidFailFirstOpenAttempt());
|
| + Directory::SaveChangesSnapshot snapshot;
|
| + const int num_entries = 4000;
|
| + for (int i = 0; i < num_entries; ++i) {
|
| + snapshot.dirty_metas.insert(CreateEntry(i).release());
|
| + }
|
| + ASSERT_TRUE(dbs->SaveChanges(snapshot));
|
| +
|
| + // Corrupt the DB by write a bunch of zeros at the beginning.
|
| + {
|
| + // Because the file is already open for writing (see dbs above), it's
|
| + // important that we open it in a sharing compatible way for platforms that
|
| + // have the concept of shared/exclusive file access (e.g. Windows).
|
| + base::ScopedFILE db_file(base::OpenFile(GetDatabasePath(), "wb"));
|
| + ASSERT_TRUE(db_file.get());
|
| + const std::string zeros(4096, '\0');
|
| + ASSERT_EQ(1U, fwrite(zeros.data(), zeros.size(), 1, db_file.get()));
|
| + }
|
| +
|
| + // Attempt to save all those entries again. See that it fails (because of the
|
| + // corruption).
|
| + //
|
| + // If this test fails because SaveChanges returned true, it may mean that you
|
| + // need to increase the number of entries written to the DB. Try increasing
|
| + // the value of num_entries above. The value needs to be large enough to force
|
| + // the underlying DB to be read from disk before writing. The value *may*
|
| + // depend on the underlying DB page size as well as the DB's cache_size
|
| + // PRAGMA.
|
| + ASSERT_FALSE(dbs->SaveChanges(snapshot));
|
| + // 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
|
|
|