From 6bd5bf74225a314dcfe4d68b3695a237dd0b8a5e Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Mon, 20 Oct 2014 17:37:19 +0200 Subject: [PATCH] Simplify sync code Before deletion, vdirsyncer will now check if the item changed on the other side, and induce a re-upload if the item did change. Because of this behavior it is now possible to remove the special-casing if no status is available. Fix #128 --- tests/test_sync.py | 15 +++++++++++++ vdirsyncer/sync.py | 54 ++++++++++++++++++++++++++++++---------------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/tests/test_sync.py b/tests/test_sync.py index aed544d..0a6b24d 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -148,6 +148,21 @@ def test_conflict_resolution_both_etags_new(winning_storage): assert u'item {}'.format(winning_storage) in n +def test_updated_and_deleted(): + a = MemoryStorage() + b = MemoryStorage() + href_a, etag_a = a.upload(Item(u'UID:1')) + status = {} + sync(a, b, status, force_delete=True) + + (href_b, etag_b), = b.list() + b.delete(href_b, etag_b) + a.update(href_a, Item(u'UID:1\nupdated'), etag_a) + sync(a, b, status, force_delete=True) + + assert len(list(a.list())) == len(list(b.list())) == 1 + + def test_conflict_resolution_invalid_mode(): a = MemoryStorage() b = MemoryStorage() diff --git a/vdirsyncer/sync.py b/vdirsyncer/sync.py index abb92ca..fc29f14 100644 --- a/vdirsyncer/sync.py +++ b/vdirsyncer/sync.py @@ -273,25 +273,41 @@ def _get_actions(storages, status): a = a_idents.get(ident, None) b = b_idents.get(ident, None) - if ident not in status: - if a and b: # missing status - yield _action_conflict_resolve(ident) - elif a and not b: # new item was created in a - yield _action_upload(ident, 'b') - elif not a and b: # new item was created in b - yield _action_upload(ident, 'a') - else: + assert not a or a['etag'] is not None + assert not b or b['etag'] is not None + + try: _, status_etag_a, _, status_etag_b = status[ident] - if a and b: - if a['etag'] != status_etag_a and b['etag'] != status_etag_b: - yield _action_conflict_resolve(ident) - elif a['etag'] != status_etag_a: # item was updated in a - yield _action_update(ident, 'b') - elif b['etag'] != status_etag_b: # item was updated in b - yield _action_update(ident, 'a') - elif a and not b: # was deleted from b + except KeyError: + status_etag_a = status_etag_b = None + + if a and b: + if a['etag'] != status_etag_a and b['etag'] != status_etag_b: + # item was modified on both sides + # OR: missing status + yield _action_conflict_resolve(ident) + elif a['etag'] != status_etag_a: + # item was only modified in a + yield _action_update(ident, 'b') + elif b['etag'] != status_etag_b: + # item was only modified in b + yield _action_update(ident, 'a') + elif a and not b: + if a['etag'] != status_etag_a: + # was deleted from b but modified on a + # OR: new item was created in a + yield _action_upload(ident, 'b') + else: + # was deleted from b and not modified on a yield _action_delete(ident, 'a') - elif not a and b: # was deleted from a + elif not a and b: + if b['etag'] != status_etag_b: + # was deleted from a but modified on b + # OR: new item was created in b + yield _action_upload(ident, 'a') + else: + # was deleted from a and not changed on b yield _action_delete(ident, 'b') - elif not a and not b: # was deleted from a and b - yield _action_delete(ident, None) + elif not a and not b: + # was deleted from a and b, clean up status + yield _action_delete(ident, None)