diff options
Diffstat (limited to 'sync/engine/syncer_unittest.cc')
-rw-r--r-- | sync/engine/syncer_unittest.cc | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/sync/engine/syncer_unittest.cc b/sync/engine/syncer_unittest.cc index 073ac51..b2a92de 100644 --- a/sync/engine/syncer_unittest.cc +++ b/sync/engine/syncer_unittest.cc @@ -62,6 +62,7 @@ #include "sync/util/cryptographer.h" #include "sync/util/extensions_activity.h" #include "sync/util/time.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using base::TimeDelta; @@ -2809,6 +2810,177 @@ TEST_F(SyncerTest, DeletingEntryInFolder) { EXPECT_EQ(0, GetCommitCounters(BOOKMARKS).num_commits_conflict); } +// Test conflict resolution when deleting a hierarchy of nodes within a folder +// and running into a conflict in one of items. The conflict in a deleted +// item must prevent all deleted ancestors from being committed as well; +// otherwise the conflicting item would end up being orphaned. +TEST_F(SyncerTest, DeletingFolderWithConflictInSubfolder) { + int64_t top_handle, nested_handle, leaf_handle; + { + WriteTransaction trans(FROM_HERE, UNITTEST, directory()); + MutableEntry top_entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "top"); + ASSERT_TRUE(top_entry.good()); + top_entry.PutIsDir(true); + top_entry.PutSpecifics(DefaultBookmarkSpecifics()); + top_entry.PutIsUnsynced(true); + top_handle = top_entry.GetMetahandle(); + + MutableEntry nested_entry(&trans, CREATE, BOOKMARKS, top_entry.GetId(), + "nested"); + ASSERT_TRUE(nested_entry.good()); + nested_entry.PutIsDir(true); + nested_entry.PutSpecifics(DefaultBookmarkSpecifics()); + nested_entry.PutIsUnsynced(true); + nested_handle = nested_entry.GetMetahandle(); + + MutableEntry leaf_entry(&trans, CREATE, BOOKMARKS, nested_entry.GetId(), + "leaf"); + ASSERT_TRUE(leaf_entry.good()); + leaf_entry.PutSpecifics(DefaultBookmarkSpecifics()); + leaf_entry.PutIsUnsynced(true); + leaf_handle = leaf_entry.GetMetahandle(); + } + EXPECT_TRUE(SyncShareNudge()); + + // Delete all 3 entries and also add unapplied update to the middle one. + { + WriteTransaction trans(FROM_HERE, UNITTEST, directory()); + MutableEntry leaf_entry(&trans, GET_BY_HANDLE, leaf_handle); + ASSERT_TRUE(leaf_entry.good()); + EXPECT_TRUE(leaf_entry.GetId().ServerKnows()); + leaf_entry.PutIsUnsynced(true); + leaf_entry.PutIsDel(true); + + MutableEntry nested_entry(&trans, GET_BY_HANDLE, nested_handle); + ASSERT_TRUE(nested_entry.good()); + EXPECT_TRUE(nested_entry.GetId().ServerKnows()); + nested_entry.PutIsUnsynced(true); + nested_entry.PutIsDel(true); + + sync_pb::EntitySpecifics specifics; + specifics.mutable_bookmark()->set_url("http://demo/"); + specifics.mutable_bookmark()->set_favicon("PNG"); + nested_entry.PutServerSpecifics(specifics); + // This will put the entry into conflict. + nested_entry.PutIsUnappliedUpdate(true); + nested_entry.PutServerVersion(nested_entry.GetBaseVersion() + 1); + + MutableEntry top_entry(&trans, GET_BY_HANDLE, top_handle); + ASSERT_TRUE(top_entry.good()); + EXPECT_TRUE(top_entry.GetId().ServerKnows()); + top_entry.PutIsUnsynced(true); + top_entry.PutIsDel(true); + } + EXPECT_TRUE(SyncShareNudge()); + + // Verify that the top folder hasn't been committed. Doing so would + // orphan the nested folder. + syncable::Id top_id; + { + syncable::ReadTransaction trans(FROM_HERE, directory()); + Entry top_entry(&trans, GET_BY_HANDLE, top_handle); + ASSERT_TRUE(top_entry.good()); + top_id = top_entry.GetId(); + + EXPECT_TRUE(top_entry.GetIsUnsynced()); + EXPECT_TRUE(top_entry.GetIsDel()); + } + + EXPECT_THAT(mock_server_->committed_ids(), + testing::Not(testing::Contains(top_id))); +} + +// Test conflict resolution when committing a hierarchy of items and running +// into a conflict in a parent folder. A conflicting parent must prevent any +// of its descendants from being committed. +TEST_F(SyncerTest, CommittingItemsWithConflictInParentFolder) { + int64_t top_handle, nested_handle, leaf_handle; + { + WriteTransaction trans(FROM_HERE, UNITTEST, directory()); + MutableEntry top_entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "top"); + ASSERT_TRUE(top_entry.good()); + top_entry.PutIsDir(true); + top_entry.PutSpecifics(DefaultBookmarkSpecifics()); + top_entry.PutIsUnsynced(true); + top_handle = top_entry.GetMetahandle(); + + MutableEntry nested_entry(&trans, CREATE, BOOKMARKS, top_entry.GetId(), + "nested"); + ASSERT_TRUE(nested_entry.good()); + nested_entry.PutIsDir(true); + nested_entry.PutSpecifics(DefaultBookmarkSpecifics()); + nested_entry.PutIsUnsynced(true); + nested_handle = nested_entry.GetMetahandle(); + + MutableEntry leaf_entry(&trans, CREATE, BOOKMARKS, nested_entry.GetId(), + "leaf"); + ASSERT_TRUE(leaf_entry.good()); + leaf_entry.PutSpecifics(DefaultBookmarkSpecifics()); + leaf_entry.PutIsUnsynced(true); + leaf_handle = leaf_entry.GetMetahandle(); + } + EXPECT_TRUE(SyncShareNudge()); + + // Touch all 3 entries and also add unapplied update to the top one. + syncable::Id top_id, nested_id, leaf_id; + { + WriteTransaction trans(FROM_HERE, UNITTEST, directory()); + sync_pb::EntitySpecifics specifics; + specifics.mutable_bookmark()->set_url("http://demo/"); + + MutableEntry top_entry(&trans, GET_BY_HANDLE, top_handle); + ASSERT_TRUE(top_entry.good()); + top_id = top_entry.GetId(); + EXPECT_TRUE(top_id.ServerKnows()); + top_entry.PutIsUnsynced(true); + top_entry.PutSpecifics(specifics); + + // This will put the top entry into conflict. + top_entry.PutIsUnappliedUpdate(true); + top_entry.PutServerIsDel(true); + top_entry.PutServerVersion(top_entry.GetBaseVersion() + 1); + + MutableEntry nested_entry(&trans, GET_BY_HANDLE, nested_handle); + ASSERT_TRUE(nested_entry.good()); + nested_id = nested_entry.GetId(); + EXPECT_TRUE(nested_id.ServerKnows()); + nested_entry.PutSpecifics(specifics); + nested_entry.PutIsUnsynced(true); + + MutableEntry leaf_entry(&trans, GET_BY_HANDLE, leaf_handle); + ASSERT_TRUE(leaf_entry.good()); + leaf_id = leaf_entry.GetId(); + EXPECT_TRUE(leaf_id.ServerKnows()); + leaf_entry.PutSpecifics(specifics); + leaf_entry.PutIsUnsynced(true); + } + EXPECT_TRUE(SyncShareNudge()); + + // Verify that all 3 entries remain unsynced + EXPECT_THAT(mock_server_->committed_ids(), + testing::Not(testing::Contains(top_id))); + EXPECT_THAT(mock_server_->committed_ids(), + testing::Not(testing::Contains(nested_id))); + EXPECT_THAT(mock_server_->committed_ids(), + testing::Not(testing::Contains(leaf_id))); + + { + syncable::ReadTransaction trans(FROM_HERE, directory()); + + Entry top_entry(&trans, GET_BY_HANDLE, top_handle); + ASSERT_TRUE(top_entry.good()); + ASSERT_TRUE(top_entry.GetIsUnsynced()); + + Entry nested_entry(&trans, GET_BY_HANDLE, nested_handle); + ASSERT_TRUE(nested_entry.good()); + ASSERT_TRUE(nested_entry.GetIsUnsynced()); + + Entry leaf_entry(&trans, GET_BY_HANDLE, leaf_handle); + ASSERT_TRUE(leaf_entry.good()); + ASSERT_TRUE(leaf_entry.GetIsUnsynced()); + } +} + // Test conflict resolution when handling an update for an item with specified // Parent ID and having an implicit (unset) Parent ID in the update. TEST_F(SyncerTest, ConflictWithImplicitParent) { |