| Index: sync/syncable/syncable_unittest.cc
|
| diff --git a/sync/syncable/syncable_unittest.cc b/sync/syncable/syncable_unittest.cc
|
| index cf34a94ec93eef46ec419698ce3602683cbbbd4e..e61504b04ccab49e6905541feb7e0d40236d8442 100644
|
| --- a/sync/syncable/syncable_unittest.cc
|
| +++ b/sync/syncable/syncable_unittest.cc
|
| @@ -392,28 +392,6 @@ TEST_F(SyncableGeneralTest, ToValue) {
|
| dir.SaveChanges();
|
| }
|
|
|
| -// A Directory whose backing store always fails SaveChanges by returning false.
|
| -class TestUnsaveableDirectory : public Directory {
|
| - public:
|
| - class UnsaveableBackingStore : public OnDiskDirectoryBackingStore {
|
| - public:
|
| - UnsaveableBackingStore(const std::string& dir_name,
|
| - const FilePath& backing_filepath)
|
| - : OnDiskDirectoryBackingStore(dir_name, backing_filepath) { }
|
| - virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) {
|
| - return false;
|
| - }
|
| - };
|
| -
|
| - TestUnsaveableDirectory(const std::string& dir_name,
|
| - const FilePath& backing_filepath)
|
| - : Directory(&encryptor_, &handler_, NULL,
|
| - new UnsaveableBackingStore(dir_name, backing_filepath)) {}
|
| - private:
|
| - FakeEncryptor encryptor_;
|
| - TestUnrecoverableErrorHandler handler_;
|
| -};
|
| -
|
| // A test fixture for syncable::Directory. Uses an in-memory database to keep
|
| // the unit tests fast.
|
| class SyncableDirectoryTest : public testing::Test {
|
| @@ -520,6 +498,15 @@ class SyncableDirectoryTest : public testing::Test {
|
| // not have any pointers to the directory (open transactions included)
|
| // when you call this.
|
| DirOpenResult SimulateSaveAndReloadDir();
|
| +
|
| + // This function will close and re-open the directory without saving any
|
| + // pending changes. This is intended to simulate the recovery from a crash
|
| + // scenario. The same warnings for SimulateSaveAndReloadDir apply here.
|
| + DirOpenResult SimulateCrashAndReloadDir();
|
| +
|
| + private:
|
| + // A helper function for Simulate{Save,Crash}AndReloadDir.
|
| + DirOpenResult ReloadDirImpl();
|
| };
|
|
|
| TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) {
|
| @@ -1261,6 +1248,27 @@ TEST_F(SyncableDirectoryTest,
|
| EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
|
| }
|
|
|
| +// Ask the directory to generate a unique ID. Close and re-open the database
|
| +// without saving, then ask for another unique ID. Verify IDs are not reused.
|
| +// This scenario simulates a crash within the first few seconds of operation.
|
| +TEST_F(SyncableDirectoryTest, LocalIdReuseTest) {
|
| + Id pre_crash_id = dir_->NextId();
|
| + SimulateCrashAndReloadDir();
|
| + Id post_crash_id = dir_->NextId();
|
| + EXPECT_NE(pre_crash_id, post_crash_id);
|
| +}
|
| +
|
| +// Ask the directory to generate a unique ID. Save the directory. Close and
|
| +// re-open the database without saving, then ask for another unique ID. Verify
|
| +// IDs are not reused. This scenario simulates a steady-state crash.
|
| +TEST_F(SyncableDirectoryTest, LocalIdReuseTestWithSave) {
|
| + Id pre_crash_id = dir_->NextId();
|
| + dir_->SaveChanges();
|
| + SimulateCrashAndReloadDir();
|
| + Id post_crash_id = dir_->NextId();
|
| + EXPECT_NE(pre_crash_id, post_crash_id);
|
| +}
|
| +
|
| // Ensure that the unsynced, is_del and server unkown entries that may have been
|
| // left in the database by old clients will be deleted when we open the old
|
| // database.
|
| @@ -1333,6 +1341,102 @@ TEST_F(SyncableDirectoryTest, OldClientLeftUnsyncedDeletedLocalItem) {
|
| }
|
| }
|
|
|
| +// An OnDirectoryBackingStore that can be set to always fail SaveChanges.
|
| +class TestBackingStore : public OnDiskDirectoryBackingStore {
|
| + public:
|
| + TestBackingStore(const std::string& dir_name,
|
| + const FilePath& backing_filepath);
|
| +
|
| + virtual ~TestBackingStore();
|
| +
|
| + virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot)
|
| + OVERRIDE;
|
| +
|
| + void StartFailingSaveChanges() {
|
| + fail_save_changes_ = true;
|
| + }
|
| +
|
| + private:
|
| + bool fail_save_changes_;
|
| +};
|
| +
|
| +TestBackingStore::TestBackingStore(const std::string& dir_name,
|
| + const FilePath& backing_filepath)
|
| + : OnDiskDirectoryBackingStore(dir_name, backing_filepath),
|
| + fail_save_changes_(false) {
|
| +}
|
| +
|
| +TestBackingStore::~TestBackingStore() { }
|
| +
|
| +bool TestBackingStore::SaveChanges(
|
| + const Directory::SaveChangesSnapshot& snapshot){
|
| + if (fail_save_changes_) {
|
| + return false;
|
| + } else {
|
| + return OnDiskDirectoryBackingStore::SaveChanges(snapshot);
|
| + }
|
| +}
|
| +
|
| +// A directory whose Save() function can be set to always fail.
|
| +class TestDirectory : public Directory {
|
| + public:
|
| + // A factory function used to work around some initialization order issues.
|
| + static TestDirectory* Create(
|
| + Encryptor *encryptor,
|
| + UnrecoverableErrorHandler *handler,
|
| + const std::string& dir_name,
|
| + const FilePath& backing_filepath);
|
| +
|
| + virtual ~TestDirectory();
|
| +
|
| + void StartFailingSaveChanges() {
|
| + backing_store_->StartFailingSaveChanges();
|
| + }
|
| +
|
| + private:
|
| + TestDirectory(Encryptor* encryptor,
|
| + UnrecoverableErrorHandler* handler,
|
| + TestBackingStore* backing_store);
|
| +
|
| + TestBackingStore* backing_store_;
|
| +};
|
| +
|
| +TestDirectory* TestDirectory::Create(
|
| + Encryptor *encryptor,
|
| + UnrecoverableErrorHandler *handler,
|
| + const std::string& dir_name,
|
| + const FilePath& backing_filepath) {
|
| + TestBackingStore* backing_store =
|
| + new TestBackingStore(dir_name, backing_filepath);
|
| + return new TestDirectory(encryptor, handler, backing_store);
|
| +}
|
| +
|
| +TestDirectory::TestDirectory(Encryptor* encryptor,
|
| + UnrecoverableErrorHandler* handler,
|
| + TestBackingStore* backing_store)
|
| + : Directory(encryptor, handler, NULL, backing_store),
|
| + backing_store_(backing_store) {
|
| +}
|
| +
|
| +TestDirectory::~TestDirectory() { }
|
| +
|
| +TEST(OnDiskSyncableDirectory, FailInitialWrite) {
|
| + FakeEncryptor encryptor;
|
| + TestUnrecoverableErrorHandler handler;
|
| + ScopedTempDir temp_dir;
|
| + FilePath file_path = temp_dir.path().Append(
|
| + FILE_PATH_LITERAL("Test.sqlite3"));
|
| + std::string name = "user@x.com";
|
| + NullDirectoryChangeDelegate delegate;
|
| +
|
| + scoped_ptr<TestDirectory> test_dir(
|
| + TestDirectory::Create(&encryptor, &handler, name, file_path));
|
| +
|
| + test_dir->StartFailingSaveChanges();
|
| + ASSERT_EQ(FAILED_INITIAL_WRITE, test_dir->Open(name, &delegate,
|
| + NullTransactionObserver()));
|
| +}
|
| +
|
| // A variant of SyncableDirectoryTest that uses a real sqlite database.
|
| class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest {
|
| protected:
|
| @@ -1343,12 +1447,7 @@ class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest {
|
| file_path_ = temp_dir_.path().Append(
|
| FILE_PATH_LITERAL("Test.sqlite3"));
|
| file_util::Delete(file_path_, true);
|
| - dir_.reset(new Directory(&encryptor_, &handler_, NULL,
|
| - new OnDiskDirectoryBackingStore(kName, file_path_)));
|
| - ASSERT_TRUE(dir_.get());
|
| - ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_,
|
| - NullTransactionObserver()));
|
| - ASSERT_TRUE(dir_->good());
|
| + CreateDirectory();
|
| }
|
|
|
| virtual void TearDown() {
|
| @@ -1358,33 +1457,27 @@ class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest {
|
| file_util::Delete(file_path_, true);
|
| }
|
|
|
| - void ReloadDir() {
|
| - dir_.reset(new Directory(&encryptor_, &handler_, NULL,
|
| - new OnDiskDirectoryBackingStore(kName, file_path_)));
|
| + // Creates a new directory. Deletes the old directory, if it exists.
|
| + void CreateDirectory() {
|
| + test_directory_ =
|
| + TestDirectory::Create(&encryptor_, &handler_, kName, file_path_);
|
| + dir_.reset(test_directory_);
|
| ASSERT_TRUE(dir_.get());
|
| ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_,
|
| NullTransactionObserver()));
|
| + ASSERT_TRUE(dir_->good());
|
| }
|
|
|
| void SaveAndReloadDir() {
|
| dir_->SaveChanges();
|
| - ReloadDir();
|
| + CreateDirectory();
|
| }
|
|
|
| - void SwapInUnsaveableDirectory() {
|
| - dir_.reset(); // Delete the old directory.
|
| -
|
| - // We first assign the object to a pointer of type TestUnsaveableDirectory
|
| - // because the OpenUnsaveable function is not available in the parent class.
|
| - scoped_ptr<TestUnsaveableDirectory> dir(new TestUnsaveableDirectory(
|
| - kName, file_path_));
|
| - ASSERT_TRUE(dir.get());
|
| - ASSERT_EQ(OPENED, dir->Open(kName, &delegate_, NullTransactionObserver()));
|
| -
|
| - // Finally, move the unsaveable directory to the dir_ variable.
|
| - dir_ = dir.Pass();
|
| + void StartFailingSaveChanges() {
|
| + test_directory_->StartFailingSaveChanges();
|
| }
|
|
|
| + TestDirectory *test_directory_; // mirrors scoped_ptr<Directory> dir_
|
| ScopedTempDir temp_dir_;
|
| FilePath file_path_;
|
| };
|
| @@ -1615,9 +1708,8 @@ TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailure) {
|
| }
|
| ASSERT_TRUE(dir_->SaveChanges());
|
|
|
| - // Now do some operations using a directory for which SaveChanges will
|
| - // always fail.
|
| - SwapInUnsaveableDirectory();
|
| + // Now do some operations when SaveChanges() will fail.
|
| + StartFailingSaveChanges();
|
| ASSERT_TRUE(dir_->good());
|
|
|
| int64 handle2 = 0;
|
| @@ -1687,9 +1779,8 @@ TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailureWithPurge) {
|
| }
|
| ASSERT_TRUE(dir_->SaveChanges());
|
|
|
| - // Now do some operations using a directory for which SaveChanges will
|
| - // always fail.
|
| - SwapInUnsaveableDirectory();
|
| + // Now do some operations while SaveChanges() is set to fail.
|
| + StartFailingSaveChanges();
|
| ASSERT_TRUE(dir_->good());
|
|
|
| ModelTypeSet set(BOOKMARKS);
|
| @@ -1721,6 +1812,14 @@ DirOpenResult SyncableDirectoryTest::SimulateSaveAndReloadDir() {
|
| if (!dir_->SaveChanges())
|
| return FAILED_IN_UNITTEST;
|
|
|
| + return ReloadDirImpl();
|
| +}
|
| +
|
| +DirOpenResult SyncableDirectoryTest::SimulateCrashAndReloadDir() {
|
| + return ReloadDirImpl();
|
| +}
|
| +
|
| +DirOpenResult SyncableDirectoryTest::ReloadDirImpl() {
|
| // Do some tricky things to preserve the backing store.
|
| DirectoryBackingStore* saved_store = dir_->store_.release();
|
|
|
|
|