diff --git a/.travis.yml b/.travis.yml index e2c7778..a78ccb9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - "3.4" env: - BUILD=tests DAV_SERVER=radicale RADICALE_BACKEND=filesystem REQUIREMENTS=release + - BUILD=tests DAV_SERVER=radicale RADICALE_BACKEND=filesystem REQUIREMENTS=release PKGS='icalendar==3.6' - BUILD=tests DAV_SERVER=radicale RADICALE_BACKEND=filesystem REQUIREMENTS=devel - BUILD=tests DAV_SERVER=radicale RADICALE_BACKEND=database REQUIREMENTS=devel - BUILD=tests DAV_SERVER=owncloud REQUIREMENTS=release @@ -12,5 +13,6 @@ env: install: - "./build.sh install" + - '[ -z "$PKGS" ] || pip install $PKGS' script: - "./build.sh run" diff --git a/tests/utils/test_vobject.py b/tests/utils/test_vobject.py index 227c65d..9ece4ab 100644 --- a/tests/utils/test_vobject.py +++ b/tests/utils/test_vobject.py @@ -7,8 +7,7 @@ :license: MIT, see LICENSE for more details. ''' -from vdirsyncer.utils.vobject import split_collection, join_collection, \ - hash_item +import vdirsyncer.utils.vobject as vobject from .. import normalize_item, VCARD_TEMPLATE, BARE_EVENT_TEMPLATE, \ EVENT_TEMPLATE @@ -18,7 +17,7 @@ _simple_joined = u'\r\n'.join(( VCARD_TEMPLATE.format(r=123), VCARD_TEMPLATE.format(r=345), VCARD_TEMPLATE.format(r=678), - u'END:VADDRESSBOOK' + u'END:VADDRESSBOOK\r\n' )) _simple_split = [ @@ -29,16 +28,19 @@ _simple_split = [ def test_split_collection_simple(): - given = split_collection(_simple_joined) + given = list(vobject.split_collection(_simple_joined)) assert [normalize_item(item) for item in given] == \ [normalize_item(item) for item in _simple_split] + if vobject.ICALENDAR_ORIGINAL_ORDER_SUPPORT: + assert [x.splitlines() for x in given] == \ + [x.splitlines() for x in _simple_split] def test_join_collection_simple(): - given = join_collection(_simple_split) - print(given) - print(_simple_joined) + given = vobject.join_collection(_simple_split) assert normalize_item(given) == normalize_item(_simple_joined) + if vobject.ICALENDAR_ORIGINAL_ORDER_SUPPORT: + assert given.splitlines() == _simple_joined.splitlines() def test_split_collection_timezones(): @@ -66,7 +68,8 @@ def test_split_collection_timezones(): [timezone, u'END:VCALENDAR'] ) - given = set(normalize_item(item) for item in split_collection(full)) + given = set(normalize_item(item) + for item in vobject.split_collection(full)) expected = set( normalize_item(u'\r\n'.join(( u'BEGIN:VCALENDAR', item, timezone, u'END:VCALENDAR' @@ -81,4 +84,4 @@ def test_hash_item(): a = EVENT_TEMPLATE.format(r=1) b = u'\n'.join(line for line in a.splitlines() if u'PRODID' not in line and u'VERSION' not in line) - assert hash_item(a) == hash_item(b) + assert vobject.hash_item(a) == vobject.hash_item(b) diff --git a/vdirsyncer/utils/vobject.py b/vdirsyncer/utils/vobject.py index a634106..9e01272 100644 --- a/vdirsyncer/utils/vobject.py +++ b/vdirsyncer/utils/vobject.py @@ -10,6 +10,7 @@ import hashlib import icalendar.cal import icalendar.parser +import icalendar.caselessdict from . import text_type, itervalues @@ -36,6 +37,15 @@ IGNORE_PROPS = _process_properties( 'REV' ) +# Whether the installed icalendar version has +# https://github.com/collective/icalendar/pull/136 +# (support for keeping the order of properties and parameters) +# +# This basically checks whether the superclass of all icalendar classes has a +# method from OrderedDict. +ICALENDAR_ORIGINAL_ORDER_SUPPORT = \ + hasattr(icalendar.caselessdict.CaselessDict, '__reversed__') + def normalize_item(text, ignore_props=IGNORE_PROPS, use_icalendar=True): try: @@ -90,7 +100,12 @@ def to_unicode_lines(item): '''icalendar doesn't provide an efficient way of getting the ical data as unicode. So let's do it ourselves.''' - for content_line in item.content_lines(): + if ICALENDAR_ORIGINAL_ORDER_SUPPORT: + content_lines = item.content_lines(sorted=False) + else: + content_lines = item.content_lines() + + for content_line in content_lines: if content_line: # https://github.com/untitaker/vdirsyncer/issues/70 # icalendar escapes semicolons which are not supposed to get