Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(799)

Unified Diff: chrome/browser/sync/engine/syncer_unittest.cc

Issue 2844037: Fix handling of undeletion within the syncer. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Whitespace. Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/sync/engine/syncer_proto_util_unittest.cc ('k') | chrome/browser/sync/engine/syncer_util.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/sync/engine/syncer_unittest.cc
diff --git a/chrome/browser/sync/engine/syncer_unittest.cc b/chrome/browser/sync/engine/syncer_unittest.cc
index 685a7ba7f1f4d4ab7ed9a04adba1f3db259829e7..f0a8cb0124973d323861cc7157e291b33acfcb35 100644
--- a/chrome/browser/sync/engine/syncer_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_unittest.cc
@@ -84,6 +84,7 @@ using syncable::SERVER_VERSION;
using syncable::UNIQUE_CLIENT_TAG;
using syncable::UNIQUE_SERVER_TAG;
using syncable::SPECIFICS;
+using syncable::SYNCING;
using syncable::UNITTEST;
using sessions::ConflictProgress;
@@ -391,6 +392,51 @@ class SyncerTest : public testing::Test,
mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
}
+ template<typename FieldType, typename ValueType>
+ ValueType GetField(int64 metahandle, FieldType field,
+ ValueType default_value) const {
+ ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
+ EXPECT_TRUE(dir.good());
+ ReadTransaction trans(dir, __FILE__, __LINE__);
+ Entry entry(&trans, GET_BY_HANDLE, metahandle);
+ EXPECT_TRUE(entry.good());
+ if (!entry.good()) {
+ return default_value;
+ }
+ EXPECT_EQ(metahandle, entry.Get(META_HANDLE));
+ return entry.Get(field);
+ }
+
+ // Helper getters that work without a transaction, to reduce boilerplate.
+ Id Get(int64 metahandle, syncable::IdField field) const {
+ return GetField(metahandle, field, syncable::kNullId);
+ }
+
+ string Get(int64 metahandle, syncable::StringField field) const {
+ return GetField(metahandle, field, string());
+ }
+
+ int64 Get(int64 metahandle, syncable::Int64Field field) const {
+ return GetField(metahandle, field, syncable::kInvalidMetaHandle);
+ }
+
+ int64 Get(int64 metahandle, syncable::BaseVersion field) const {
+ const int64 kDefaultValue = -100;
+ return GetField(metahandle, field, kDefaultValue);
+ }
+
+ bool Get(int64 metahandle, syncable::IndexedBitField field) const {
+ return GetField(metahandle, field, false);
+ }
+
+ bool Get(int64 metahandle, syncable::IsDelField field) const {
+ return GetField(metahandle, field, false);
+ }
+
+ bool Get(int64 metahandle, syncable::BitField field) const {
+ return GetField(metahandle, field, false);
+ }
+
// Some ids to aid tests. Only the root one's value is specific. The rest
// are named for test clarity.
// TODO(chron): Get rid of these inbuilt IDs. They only make it
@@ -1640,7 +1686,8 @@ class EntryCreatedInNewFolderTest : public SyncerTest {
TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) {
ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
- CHECK(dir.good());
+ ASSERT_TRUE(dir.good());
+ dir->set_store_birthday(mock_server_->store_birthday());
{
WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
MutableEntry entry(&trans, syncable::CREATE, trans.root_id(),
@@ -1709,7 +1756,7 @@ TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) {
}
// Run the syncer.
for (int i = 0 ; i < 30 ; ++i) {
- syncer_->SyncShare(this);
+ syncer_->SyncShare(this);
}
}
@@ -1789,8 +1836,12 @@ TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
version = entry.Get(BASE_VERSION);
server_position_in_parent = entry.Get(SERVER_POSITION_IN_PARENT);
}
- mock_server_->AddUpdateDirectory(id, root_id_, "Pete", version, 10);
- mock_server_->SetLastUpdatePosition(server_position_in_parent);
+ sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
+ EXPECT_EQ("Pete", update->name());
+ EXPECT_EQ(id.GetServerId(), update->id_string());
+ EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
+ EXPECT_EQ(version, update->version());
+ EXPECT_EQ(server_position_in_parent, update->position_in_parent());
syncer_->SyncShare(this);
{
ReadTransaction trans(dir, __FILE__, __LINE__);
@@ -2547,7 +2598,6 @@ TEST_F(SyncerTest, DISABLED_ServerDeletingFolderWeHaveAnOpenEntryIn) {
syncer_events_.clear();
}
-
TEST_F(SyncerTest, WeMovedSomethingIntoAFolderServerHasDeleted) {
ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
CHECK(dir.good());
@@ -2604,44 +2654,39 @@ TEST_F(SyncerTest, WeMovedSomethingIntoAFolderServerHasDeleted) {
class FolderMoveDeleteRenameTest : public SyncerTest {
public:
- FolderMoveDeleteRenameTest() : move_bob_count_(0), done_(false) {}
+ FolderMoveDeleteRenameTest() : done_(false) {}
static const int64 bob_id_number = 1;
static const int64 fred_id_number = 2;
void MoveBobIntoID2Runner() {
if (!done_) {
- done_ = MoveBobIntoID2();
+ MoveBobIntoID2();
+ done_ = true;
}
}
protected:
- int move_bob_count_;
- bool done_;
-
- bool MoveBobIntoID2() {
+ void MoveBobIntoID2() {
ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
CHECK(dir.good());
- if (--move_bob_count_ > 0) {
- return false;
- }
-
- if (move_bob_count_ == 0) {
WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
- Entry alice(&trans, GET_BY_ID,
- TestIdFactory::FromNumber(fred_id_number));
+ Entry alice(&trans, GET_BY_ID,
+ TestIdFactory::FromNumber(fred_id_number));
CHECK(alice.good());
- CHECK(!alice.Get(IS_DEL));
- MutableEntry bob(&trans, GET_BY_ID,
- TestIdFactory::FromNumber(bob_id_number));
+ EXPECT_TRUE(!alice.Get(IS_DEL));
+ EXPECT_TRUE(alice.Get(SYNCING)) << "Expected to be called mid-commit.";
+ MutableEntry bob(&trans, GET_BY_ID,
+ TestIdFactory::FromNumber(bob_id_number));
CHECK(bob.good());
bob.Put(IS_UNSYNCED, true);
+
+ bob.Put(SYNCING, false);
bob.Put(PARENT_ID, alice.Get(ID));
- return true;
}
- return false;
-}
+
+ bool done_;
};
TEST_F(FolderMoveDeleteRenameTest,
@@ -2664,6 +2709,7 @@ TEST_F(FolderMoveDeleteRenameTest,
MutableEntry fred(&trans, GET_BY_ID, fred_id);
ASSERT_TRUE(fred.good());
fred.Put(IS_UNSYNCED, true);
+ fred.Put(SYNCING, false);
fred.Put(NON_UNIQUE_NAME, "Alice");
}
mock_server_->AddUpdateDirectory(fred_id, TestIdFactory::root(),
@@ -2673,7 +2719,6 @@ TEST_F(FolderMoveDeleteRenameTest,
// This test is a little brittle. We want to move the item into the folder
// such that we think we're dealing with a simple conflict, but in reality
// it's actually a conflict set.
- move_bob_count_ = 2;
mock_server_->SetMidCommitCallback(
NewCallback<FolderMoveDeleteRenameTest>(this,
&FolderMoveDeleteRenameTest::MoveBobIntoID2Runner));
@@ -3981,29 +4026,38 @@ TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
}
}
-TEST_F(SyncerTest, ClientTagClientCreatedConflictUpdate) {
+TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
EXPECT_TRUE(dir.good());
- int64 original_metahandle;
+ int64 original_metahandle = 0;
+
+ sync_pb::EntitySpecifics local_bookmark(DefaultBookmarkSpecifics());
+ local_bookmark.MutableExtension(sync_pb::bookmark)->
+ set_url("http://foo/localsite");
+ sync_pb::EntitySpecifics server_bookmark(DefaultBookmarkSpecifics());
+ server_bookmark.MutableExtension(sync_pb::bookmark)->
+ set_url("http://bar/serversite");
{
WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
MutableEntry perm_folder(&trans, CREATE, ids_.root(), "clientname");
ASSERT_TRUE(perm_folder.good());
perm_folder.Put(UNIQUE_CLIENT_TAG, "clientperm");
- perm_folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
+ perm_folder.Put(SPECIFICS, local_bookmark);
perm_folder.Put(IS_UNSYNCED, true);
EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
EXPECT_FALSE(perm_folder.Get(ID).ServerKnows());
original_metahandle = perm_folder.Get(META_HANDLE);
}
- mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100);
+ mock_server_->AddUpdateBookmark(1, 0, "permitem_renamed", 10, 100);
mock_server_->SetLastUpdateClientTag("clientperm");
+ mock_server_->GetMutableLastUpdate()->mutable_specifics()->
+ CopyFrom(server_bookmark);
mock_server_->set_conflict_all_commits(true);
syncer_->SyncShare(this);
- // This should cause client tag overwrite.
+ // This should cause client tag reunion, preserving the metahandle.
{
ReadTransaction trans(dir, __FILE__, __LINE__);
@@ -4011,20 +4065,43 @@ TEST_F(SyncerTest, ClientTagClientCreatedConflictUpdate) {
ASSERT_TRUE(perm_folder.good());
EXPECT_FALSE(perm_folder.Get(IS_DEL));
EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
- EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
- EXPECT_EQ(perm_folder.Get(BASE_VERSION), 10);
- // Entry should have been moved aside.
- EXPECT_NE(perm_folder.Get(META_HANDLE), original_metahandle);
- EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "clientperm");
- EXPECT_TRUE(perm_folder.Get(NON_UNIQUE_NAME) == "permitem_renamed");
+ EXPECT_TRUE(perm_folder.Get(IS_UNSYNCED));
+ EXPECT_EQ(10, perm_folder.Get(BASE_VERSION));
+ // Entry should have been given the new ID while preserving the
+ // metahandle; client should have won the conflict resolution.
+ EXPECT_EQ(original_metahandle, perm_folder.Get(META_HANDLE));
+ EXPECT_EQ("clientperm", perm_folder.Get(UNIQUE_CLIENT_TAG));
+ EXPECT_EQ("clientname", perm_folder.Get(NON_UNIQUE_NAME));
+ EXPECT_EQ(local_bookmark.SerializeAsString(),
+ perm_folder.Get(SPECIFICS).SerializeAsString());
+ EXPECT_TRUE(perm_folder.Get(ID).ServerKnows());
+ }
+
+ mock_server_->set_conflict_all_commits(false);
+ syncer_->SyncShare(this);
- Entry moved_aside(&trans, GET_BY_HANDLE, original_metahandle);
- EXPECT_TRUE(moved_aside.good());
- EXPECT_TRUE(moved_aside.Get(IS_DEL));
+ // The resolved entry ought to commit cleanly.
+ {
+ ReadTransaction trans(dir, __FILE__, __LINE__);
+
+ Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "clientperm");
+ ASSERT_TRUE(perm_folder.good());
+ EXPECT_FALSE(perm_folder.Get(IS_DEL));
+ EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
+ EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
+ EXPECT_TRUE(10 < perm_folder.Get(BASE_VERSION));
+ // Entry should have been given the new ID while preserving the
+ // metahandle; client should have won the conflict resolution.
+ EXPECT_EQ(original_metahandle, perm_folder.Get(META_HANDLE));
+ EXPECT_EQ("clientperm", perm_folder.Get(UNIQUE_CLIENT_TAG));
+ EXPECT_EQ("clientname", perm_folder.Get(NON_UNIQUE_NAME));
+ EXPECT_EQ(local_bookmark.SerializeAsString(),
+ perm_folder.Get(SPECIFICS).SerializeAsString());
+ EXPECT_TRUE(perm_folder.Get(ID).ServerKnows());
}
}
-TEST_F(SyncerTest, ClientTagOverwitesDeletedClientEntry) {
+TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
EXPECT_TRUE(dir.good());
@@ -4032,7 +4109,9 @@ TEST_F(SyncerTest, ClientTagOverwitesDeletedClientEntry) {
WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
MutableEntry perm_folder(&trans, CREATE, ids_.root(), "clientname");
ASSERT_TRUE(perm_folder.good());
+ ASSERT_FALSE(perm_folder.Get(ID).ServerKnows());
perm_folder.Put(UNIQUE_CLIENT_TAG, "clientperm");
+ perm_folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
perm_folder.Put(IS_UNSYNCED, true);
perm_folder.Put(IS_DEL, true);
}
@@ -4048,12 +4127,12 @@ TEST_F(SyncerTest, ClientTagOverwitesDeletedClientEntry) {
Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "clientperm");
ASSERT_TRUE(perm_folder.good());
- EXPECT_FALSE(perm_folder.Get(IS_DEL));
+ ASSERT_TRUE(perm_folder.Get(ID).ServerKnows());
+ EXPECT_TRUE(perm_folder.Get(IS_DEL));
EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
- EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
+ EXPECT_TRUE(perm_folder.Get(IS_UNSYNCED));
EXPECT_EQ(perm_folder.Get(BASE_VERSION), 10);
EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "clientperm");
- EXPECT_TRUE(perm_folder.Get(NON_UNIQUE_NAME) == "permitem_renamed");
}
}
@@ -4139,6 +4218,473 @@ TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
}
+// Test what happens if a client deletes, then recreates, an object very
+// quickly. It is possible that the deletion gets sent as a commit, and
+// the undelete happens during the commit request. The principle here
+// is that with a single committing client, conflicts should never
+// be encountered, and a client encountering its past actions during
+// getupdates should never feed back to override later actions.
+//
+// In cases of ordering A-F below, the outcome should be the same.
+// Exercised by UndeleteDuringCommit:
+// A. Delete - commit - undelete - commitresponse.
+// B. Delete - commit - undelete - commitresponse - getupdates.
+// Exercised by UndeleteBeforeCommit:
+// C. Delete - undelete - commit - commitresponse.
+// D. Delete - undelete - commit - commitresponse - getupdates.
+// Exercised by UndeleteAfterCommit:
+// E. Delete - commit - commitresponse - undelete - commit
+// - commitresponse.
+// F. Delete - commit - commitresponse - undelete - commit -
+// - commitresponse - getupdates.
+class SyncerUndeletionTest : public SyncerTest {
+ public:
+ SyncerUndeletionTest()
+ : client_tag_("foobar"),
+ metahandle_(syncable::kInvalidMetaHandle) {
+ }
+
+ void Create() {
+ ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
+ EXPECT_TRUE(dir.good());
+ WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
+ MutableEntry perm_folder(&trans, CREATE, ids_.root(), "clientname");
+ ASSERT_TRUE(perm_folder.good());
+ perm_folder.Put(UNIQUE_CLIENT_TAG, client_tag_);
+ perm_folder.Put(IS_UNSYNCED, true);
+ perm_folder.Put(SYNCING, false);
+ perm_folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
+ EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
+ EXPECT_FALSE(perm_folder.Get(ID).ServerKnows());
+ metahandle_ = perm_folder.Get(META_HANDLE);
+ }
+
+ void Delete() {
+ ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
+ EXPECT_TRUE(dir.good());
+ WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
+ MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
+ ASSERT_TRUE(entry.good());
+ EXPECT_EQ(metahandle_, entry.Get(META_HANDLE));
+ entry.Put(IS_DEL, true);
+ entry.Put(IS_UNSYNCED, true);
+ entry.Put(SYNCING, false);
+ }
+
+ void Undelete() {
+ ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
+ EXPECT_TRUE(dir.good());
+ WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
+ MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
+ ASSERT_TRUE(entry.good());
+ EXPECT_EQ(metahandle_, entry.Get(META_HANDLE));
+ EXPECT_TRUE(entry.Get(IS_DEL));
+ entry.Put(IS_DEL, false);
+ entry.Put(IS_UNSYNCED, true);
+ entry.Put(SYNCING, false);
+ }
+
+ int64 GetMetahandleOfTag() {
+ ScopedDirLookup dir(syncdb_.manager(), syncdb_.name());
+ EXPECT_TRUE(dir.good());
+ ReadTransaction trans(dir, __FILE__, __LINE__);
+ Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
+ EXPECT_TRUE(entry.good());
+ if (!entry.good()) {
+ return syncable::kInvalidMetaHandle;
+ }
+ return entry.Get(META_HANDLE);
+ }
+
+ void ExpectUnsyncedCreation() {
+ EXPECT_EQ(metahandle_, GetMetahandleOfTag());
+ EXPECT_FALSE(Get(metahandle_, IS_DEL));
+ EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL)); // Never been committed.
+ EXPECT_GE(0, Get(metahandle_, BASE_VERSION));
+ EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
+ EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
+ }
+
+ void ExpectUnsyncedUndeletion() {
+ EXPECT_EQ(metahandle_, GetMetahandleOfTag());
+ EXPECT_FALSE(Get(metahandle_, IS_DEL));
+ EXPECT_TRUE(Get(metahandle_, SERVER_IS_DEL));
+ EXPECT_EQ(0, Get(metahandle_, BASE_VERSION));
+ EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
+ EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
+ EXPECT_TRUE(Get(metahandle_, ID).ServerKnows());
+ }
+
+ void ExpectUnsyncedEdit() {
+ EXPECT_EQ(metahandle_, GetMetahandleOfTag());
+ EXPECT_FALSE(Get(metahandle_, IS_DEL));
+ EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
+ EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
+ EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
+ EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
+ EXPECT_TRUE(Get(metahandle_, ID).ServerKnows());
+ }
+
+ void ExpectUnsyncedDeletion() {
+ EXPECT_EQ(metahandle_, GetMetahandleOfTag());
+ EXPECT_TRUE(Get(metahandle_, IS_DEL));
+ EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
+ EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
+ EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
+ EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
+ EXPECT_LT(0, Get(metahandle_, SERVER_VERSION));
+ }
+
+ void ExpectSyncedAndCreated() {
+ EXPECT_EQ(metahandle_, GetMetahandleOfTag());
+ EXPECT_FALSE(Get(metahandle_, IS_DEL));
+ EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
+ EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
+ EXPECT_EQ(Get(metahandle_, BASE_VERSION), Get(metahandle_, SERVER_VERSION));
+ EXPECT_FALSE(Get(metahandle_, IS_UNSYNCED));
+ EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
+ }
+
+ void ExpectSyncedAndDeleted() {
+ EXPECT_EQ(metahandle_, GetMetahandleOfTag());
+ EXPECT_TRUE(Get(metahandle_, IS_DEL));
+ EXPECT_TRUE(Get(metahandle_, SERVER_IS_DEL));
+ EXPECT_FALSE(Get(metahandle_, IS_UNSYNCED));
+ EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
+ EXPECT_GE(0, Get(metahandle_, BASE_VERSION));
+ EXPECT_GE(0, Get(metahandle_, SERVER_VERSION));
+ }
+
+ protected:
+ const std::string client_tag_;
+ int64 metahandle_;
+};
+
+TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Delete, begin committing the delete, then undelete while committing.
+ Delete();
+ ExpectUnsyncedDeletion();
+ mock_server_->SetMidCommitCallback(
+ NewCallback<SyncerUndeletionTest>(this,
+ &SyncerUndeletionTest::Undelete));
+ syncer_->SyncShare(this);
+
+ // The item ought to exist as an unsynced undeletion (meaning,
+ // we think that the next commit ought to be a recreation commit).
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectUnsyncedUndeletion();
+
+ // Now, encounter a GetUpdates corresponding to the deletion from
+ // the server. The undeletion should prevail again and be committed.
+ // None of this should trigger any conflict detection -- it is perfectly
+ // normal to recieve updates from our own commits.
+ mock_server_->SetMidCommitCallback(NULL);
+ mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+}
+
+TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Delete and undelete, then sync to pick up the result.
+ Delete();
+ ExpectUnsyncedDeletion();
+ Undelete();
+ ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
+ syncer_->SyncShare(this);
+
+ // The item ought to have committed successfully.
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+ EXPECT_EQ(2, Get(metahandle_, BASE_VERSION));
+
+ // Now, encounter a GetUpdates corresponding to the just-committed
+ // update.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+}
+
+TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Delete and commit.
+ Delete();
+ ExpectUnsyncedDeletion();
+ syncer_->SyncShare(this);
+
+ // The item ought to have committed successfully.
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // Before the GetUpdates, the item is locally undeleted.
+ Undelete();
+ ExpectUnsyncedUndeletion();
+
+ // Now, encounter a GetUpdates corresponding to the just-committed
+ // deletion update. The undeletion should prevail.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+}
+
+TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Delete and commit.
+ Delete();
+ ExpectUnsyncedDeletion();
+ syncer_->SyncShare(this);
+
+ // The item ought to have committed successfully.
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // Now, encounter a GetUpdates corresponding to the just-committed
+ // deletion update. Should be consistent.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // After the GetUpdates, the item is locally undeleted.
+ Undelete();
+ ExpectUnsyncedUndeletion();
+
+ // Now, encounter a GetUpdates corresponding to the just-committed
+ // deletion update. The undeletion should prevail.
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+}
+
+// Test processing of undeletion GetUpdateses.
+TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Add a delete from the server.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Some other client deletes the item.
+ mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
+ syncer_->SyncShare(this);
+
+ // The update ought to have applied successfully.
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // Undelete it locally.
+ Undelete();
+ ExpectUnsyncedUndeletion();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+
+ // Now, encounter a GetUpdates corresponding to the just-committed
+ // deletion update. The undeletion should prevail.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+}
+
+TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Some other client deletes the item before we get a chance
+ // to GetUpdates our original request.
+ mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
+ syncer_->SyncShare(this);
+
+ // The update ought to have applied successfully.
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // Undelete it locally.
+ Undelete();
+ ExpectUnsyncedUndeletion();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+
+ // Now, encounter a GetUpdates corresponding to the just-committed
+ // deletion update. The undeletion should prevail.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+}
+
+TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Get the updates of our just-committed entry.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // We delete the item.
+ Delete();
+ ExpectUnsyncedDeletion();
+ syncer_->SyncShare(this);
+
+ // The update ought to have applied successfully.
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // Now, encounter a GetUpdates corresponding to the just-committed
+ // deletion update.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // Some other client undeletes the item.
+ mock_server_->AddUpdateBookmark(Get(metahandle_, ID),
+ Get(metahandle_, PARENT_ID),
+ "Thadeusz", 100, 1000);
+ mock_server_->SetLastUpdateClientTag(client_tag_);
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+ EXPECT_EQ("Thadeusz", Get(metahandle_, NON_UNIQUE_NAME));
+}
+
+TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
+ StatusController* status = session_->status_controller();
+
+ Create();
+ ExpectUnsyncedCreation();
+ syncer_->SyncShare(this);
+
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // Get the updates of our just-committed entry.
+ mock_server_->AddUpdateFromLastCommit();
+ syncer_->SyncShare(this);
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ ExpectSyncedAndCreated();
+
+ // We delete the item.
+ Delete();
+ ExpectUnsyncedDeletion();
+ syncer_->SyncShare(this);
+
+ // The update ought to have applied successfully.
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndDeleted();
+
+ // Some other client undeletes before we see the update from our
+ // commit.
+ mock_server_->AddUpdateBookmark(Get(metahandle_, ID),
+ Get(metahandle_, PARENT_ID),
+ "Thadeusz", 100, 1000);
+ mock_server_->SetLastUpdateClientTag(client_tag_);
+ syncer_->SyncShare(this);
+ EXPECT_EQ(0, status->TotalNumConflictingItems());
+ EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
+ ExpectSyncedAndCreated();
+ EXPECT_EQ("Thadeusz", Get(metahandle_, NON_UNIQUE_NAME));
+}
+
+// A group of tests exercising the syncer's handling of sibling ordering, as
+// represented in the sync protocol.
class SyncerPositionUpdateTest : public SyncerTest {
public:
SyncerPositionUpdateTest() : next_update_id_(1), next_revision_(1) {}
« no previous file with comments | « chrome/browser/sync/engine/syncer_proto_util_unittest.cc ('k') | chrome/browser/sync/engine/syncer_util.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698