diff --git a/caldav/compatibility_hints.py b/caldav/compatibility_hints.py index bd68a8c7..733759c9 100644 --- a/caldav/compatibility_hints.py +++ b/caldav/compatibility_hints.py @@ -32,11 +32,6 @@ ## caldav_server_checker, and things got messy from there. We should ## double-check the values where there is any doubt, and clean up. -## NEW STYLE -## (we're gradually moving stuff from the good old -## "incompatibility_description" below over to -## "compatibility_features") - class FeatureSet: """Work in progress ... TODO: write a better class description. @@ -433,6 +428,7 @@ class FeatureSet: "save-load.todo.recurrences.count": {"description": "The server will receive and store a recurring task with a count set in the RRULE", "default": {"support": "full"}}, "save-load.todo.recurrences.thisandfuture": {"description": "Completing a recurring task with rrule_mode='thisandfuture' works (modifies RRULE and saves back to server)", "default": {"support": "full"}}, "save-load.todo.mixed-calendar": {"description": "The same calendar may contain both events and tasks (Zimbra only allows tasks to be placed on special task lists)", "default": {"support": "full"}}, + "save-load.todo.uncomplete": {"description": "A completed task can be made pending again (Todo.uncomplete()): after saving it with STATUS:NEEDS-ACTION and without COMPLETED, it loads back that way.", "default": {"support": "full"}}, "save-load.journal": { "description": "The server will even accept journals", "default": { "support": "full" } @@ -546,7 +542,12 @@ class FeatureSet: }, "search.time-range.todo": {"description": "basic time range searches for tasks works", "default": {"support": "full"}}, "search.time-range.todo.no-dtstart": { - "description": "A VTODO without DTSTART (but with DUE) is returned by a date-range search. RFC5545 and RFC4791 section 9.9 say such a task has a defined time span and should be found, so 'full' (the default) is the compliant behaviour; some servers (Davical, Stalwart, Synology) skip any task lacking DTSTART. Probed with a closed window; servers that skip such tasks only in closed ranges (returning them in open-ended ones) are instead tracked by the 'vtodo_datesearch_nodtstart_task_is_skipped_in_closed_date_range' flag.", + "description": "A VTODO without DTSTART (but with DUE) is returned by a date-range search. RFC5545 and RFC4791 section 9.9 say such a task has a defined time span and should be found, so 'full' (the default) is the compliant behaviour; some servers (Davical, Stalwart, Synology) skip any task lacking DTSTART. Probed with a closed window.", + "default": {"support": "full"}, + "links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-9.9"], + }, + "search.time-range.todo.no-dtstart.no-due": { + "description": "A VTODO with neither DTSTART nor DUE (nor COMPLETED or CREATED) is returned by a date-range search. RFC4791 section 9.9 says such a task overlaps every time range. Zimbra only finds tasks with DTSTART or DUE set. Unless set, it follows search.time-range.todo.no-dtstart, as a server skipping tasks without DTSTART skips these too. Probed with a closed window.", "default": {"support": "full"}, "links": ["https://datatracker.ietf.org/doc/html/rfc4791#section-9.9"], }, @@ -850,7 +851,6 @@ def __init__(self, feature_set_dict=None): if isinstance(feature_set_dict, FeatureSet): self._server_features = copy.deepcopy(feature_set_dict._server_features) self.backward_compatibility_mode = feature_set_dict.backward_compatibility_mode - self._old_flags = copy.copy(feature_set_dict._old_flags) if hasattr(feature_set_dict, '_old_flags') else [] return ## TODO: copy the FEATURES dict, or just the feature_set dict? @@ -858,8 +858,6 @@ def __init__(self, feature_set_dict=None): ## changed ... but we need test code in place) self.backward_compatibility_mode = feature_set_dict is None self._server_features = {} - ## TODO: remove this when it can be removed - self._old_flags = [] if feature_set_dict: self._copy_feature_set(feature_set_dict, collapse=False) @@ -908,10 +906,6 @@ def copy_feature_set(self, feature_set, collapse=True): ## warning with stacklevel=3 points at the user's code def _copy_feature_set(self, feature_set, collapse=True): for feature in feature_set: - ## TODO: temp - should be removed - if feature == 'old_flags': - self._old_flags = feature_set[feature] - continue if feature == 'write-delay': warnings.warn( "The 'write-delay' feature is deprecated - use 'synchronous-write', " @@ -1357,60 +1351,10 @@ def compare(self, observed): ) return mismatches -#### OLD STYLE - -## THE LIST BELOW IS TO BE REMOVED COMPLETELY. DO NOT USE IT. - -## It's not considered to be part of the public API (though, it should -## have been prefixed with _ to make it clear). The list is being -## removed little-by-little, without regards of SemVer. - -## The lists below are specifying what tests should be skipped or -## modified to accept non-conforming resultsets from the different -## calendar servers. In addition there are some hacks in the library -## code itself to work around some known compatibility issues, like -## the caldav.lib.vcal.fix function. -## Here is a list of all observed (in)compatibility issues the test framework needs to know about -## TODO: -## * references to the relevant parts of the RFC would be nice. -## * Research should be done to triple-check that the issue is on the server side, and not on the client side -## * Some of the things below should be possible to probe the server for. -## * Perhaps some more readable format should be considered (yaml?). -## * Consider how to get this into the documentation -incompatibility_description = { - 'event_by_url_is_broken': - """A GET towards a valid calendar object resource URL will yield 404 (wtf?)""", - - 'vtodo_datesearch_nodtstart_task_is_skipped_in_closed_date_range': - """only open-ended date searches for todo-items will find tasks without a dtstart""", - - 'vtodo_datesearch_notime_task_is_skipped': - """date searches for todo-items will (only) find tasks that has either """ - """a dtstart or due set""", - - 'vtodo_no_due_infinite_duration': - """date search will find todo-items without due if dtstart is """ - """before the date search interval. This is in breach of rfc4791""" - """section 9.9""", - - 'vtodo-cannot-be-uncompleted': - """If a VTODO object has been set with STATUS:COMPLETE, it's not possible to delete the COMPLTEDED attribute and change back to STATUS:IN-ACTION""", - - 'sticky_events': - """Events should be deleted before the calendar is deleted, """ - """and/or deleting a calendar may not have immediate effect""", - - 'dav_not_supported': - """when asked, the server may claim it doesn't support the DAV protocol. Observed by one baikal server, should be investigated more (TODO) and robur""", - - 'fastmail_buggy_noexpand_date_search': - """The 'blissful anniversary' recurrent example event is returned when asked for a no-expand date search for some timestamps covering a completely different date""", - - 'robur_rrule_freq_yearly_expands_monthly': - """Robur expands a yearly event into a monthly event. I believe I've reported this one upstream at some point, but can't find back to it""", - -} - +## Server profiles. Each dict below is a feature set (see +## FeatureSet.FEATURES above) describing one server. In addition there +## are some hacks in the library code itself to work around some known +## compatibility issues, like the caldav.lib.vcal.fix function. xandikos = { ## Probed 2026-10-11 on 0.4.8, unchanged on master (0.4.9): a token that ## isn't a hex SHA crashes the git store lookup, and a hex one is refused @@ -1677,20 +1621,14 @@ def compare(self, observed): 'save-load.icalendar.related-to': {'support': 'unsupported'}, 'search.time-range.open.start': {'support': 'broken'}, - "old_flags": [ - ## setting display name in zimbra does not work (display name, - ## calendar-ID and URL is the same, the display name cannot be - ## changed, it can only be given if no calendar-ID is given. In - ## earlier versions of Zimbra display-name could be changed, but - ## then the calendar would not be available on the old URL - ## anymore) - ## 'event_by_url_is_broken' removed - works in zimbra/zcs-foss:latest - 'vtodo_datesearch_notime_task_is_skipped', + ## Probed 2026-10-20: a task with DUE but no DTSTART is found by a + ## date search, one with neither is not + 'search.time-range.todo.no-dtstart.no-due': {'support': 'unsupported'}, ## TODO: I just discovered that when searching for a date some ## years after a recurring daily event was made, the event does ## not appear. - ], + ## extra properties not specified in RFC4791/RFC5545. Zimbra stores ## calendar-order, and stores calendar-color only when set as a hex value - ## it rejects/ignores a colour name like "blue". (The old 'calendar_color' @@ -2079,12 +2017,6 @@ def compare(self, observed): 'principal-search': {'support': 'unsupported'}, ## DAViCal skips VTODOs without DTSTART in date-range searches. 'search.time-range.todo.no-dtstart': {'support': 'unsupported'}, - "old_flags": [ - #'no_journal', ## it threw a 500 internal server error! ## for old versions - #'nofreebusy', ## for old versions - ## 'fragile_sync_tokens' removed - covered by 'sync-token': {'support': 'fragile'} - 'vtodo_datesearch_notime_task_is_skipped', - ], ## extra properties not specified in RFC4791/RFC5545 "calendar-color": {"support": "full"}, "calendar-order": {"support": "full"}, @@ -2184,7 +2116,10 @@ def compare(self, observed): 'test-calendar': {'cleanup-regime': 'wipe-calendar'}, } -## Old notes for sogo (todo - incorporate them in the structure above) +## Old notes for sogo (todo - incorporate them in the structure above). +## The commented-out lists here and below name flags of the old-style +## system, removed in https://github.com/python-caldav/caldav/issues/585 - +## kept only as a record of what was once observed on those servers. ## https://www.sogo.nu/bugs/view.php?id=3065 ## left a note about time-based sync tokens on https://www.sogo.nu/bugs/view.php?id=5163 ## https://www.sogo.nu/bugs/view.php?id=5282 @@ -2606,10 +2541,11 @@ def compare(self, observed): ## Open-start searches used to work (no hint, so full), but were ## rejected with 403 Forbidden 2026-10-11 'search.time-range.open.start': {'support': 'fragile'}, - "old_flags": [ - #"text_search_is_case_insensitive", - "vtodo-cannot-be-uncompleted", - ] + ## Probed 2026-10-20 + "save-load.todo.uncomplete": { + "support": "unsupported", + "behaviour": "the task is saved as pending, but loads back with STATUS:COMPLETED", + }, } ## https://ox.io/ diff --git a/changes/585.added.md b/changes/585.added.md new file mode 100644 index 00000000..b94819a5 --- /dev/null +++ b/changes/585.added.md @@ -0,0 +1 @@ +Compatibility features `save-load.todo.uncomplete` (a completed task can be made pending again with `Todo.uncomplete()`; GMX keeps it completed) and `search.time-range.todo.no-dtstart.no-due` (a date search finds a task with neither DTSTART nor DUE, as RFC 4791 section 9.9 says it should; Zimbra does not). They replace the last two old-style flags in the Zimbra and GMX profiles. diff --git a/changes/585.removed.md b/changes/585.removed.md new file mode 100644 index 00000000..c92da999 --- /dev/null +++ b/changes/585.removed.md @@ -0,0 +1 @@ +The old-style compatibility flags are gone: `caldav.compatibility_hints.incompatibility_description`, and the `old_flags` key in a feature set. They were only ever used by the test suite to skip or adjust tests, and every flag still in use is now a compatibility feature. diff --git a/docs/design/FEATURE_COMPLETE_ROADMAP.md b/docs/design/FEATURE_COMPLETE_ROADMAP.md index 014b2aee..14c71d0e 100644 --- a/docs/design/FEATURE_COMPLETE_ROADMAP.md +++ b/docs/design/FEATURE_COMPLETE_ROADMAP.md @@ -535,7 +535,7 @@ Also related: [#697](https://github.com/python-caldav/caldav/issues/697) - smart - **Related issues:** [#585](https://github.com/python-caldav/caldav/issues/585), [#482](https://github.com/python-caldav/caldav/issues/482), [#128](https://github.com/python-caldav/caldav/issues/128), [#619](https://github.com/python-caldav/caldav/issues/619), [#515](https://github.com/python-caldav/caldav/issues/515) **Tasks:** -- [ ] Remove old incompatibility flags ([#585](https://github.com/python-caldav/caldav/issues/585)) +- [x] Remove old incompatibility flags ([#585](https://github.com/python-caldav/caldav/issues/585)) - [ ] Obsolete `get_duration`, `get_due`, `get_dtend` ([#482](https://github.com/python-caldav/caldav/issues/482)) - [ ] v4.0: remove everything that raises a `DeprecationWarning`, and add the warning to methods deprecated without one ([#619](https://github.com/python-caldav/caldav/issues/619)) diff --git a/docs/design/TODO_COMPATIBILITY_HINTS.md b/docs/design/TODO_COMPATIBILITY_HINTS.md index 5928ddd3..1fdcd8f5 100644 --- a/docs/design/TODO_COMPATIBILITY_HINTS.md +++ b/docs/design/TODO_COMPATIBILITY_HINTS.md @@ -44,26 +44,13 @@ and once in the post-loop block (line 606). The second call (line 606) uses the Needs careful review; may be an off-by-one in the loop termination logic, or the two conditions are truly independent and the second call is simply redundant. -## 3. `_old_flags` — in-progress migration, clear removal path +## 3. `_old_flags` (fixed) -**Location**: `FeatureSet.__init__`, `FeatureSet.copy_feature_set`, and ~12 server config -dicts in `compatibility_hints.py` - -`_old_flags` is explicitly marked `## TODO: remove this when it can be removed`. It is -a shim that carries forward the legacy flat-list quirk system while server configs are -being migrated to the new dotted-feature style. - -`test_caldav.py:1006` still reads `self.caldav.features._old_flags` to validate that old -flags are known strings. The test at line 1015-1016 confirms all flags are present in -`incompatibility_description`. - -**Fix**: For each server dict that still has `'old_flags': [...]`, translate the listed -flags into equivalent new-style features and remove the `old_flags` key. Once all server -dicts are migrated: -- Remove the `if feature == 'old_flags':` special-case in `copy_feature_set` -- Remove `self._old_flags = []` from `__init__` -- Remove the `_old_flags` copy in the copy-constructor branch -- Remove the validation in `test_caldav.py` +The last old-style flags are gone (https://github.com/python-caldav/caldav/issues/585): +the two still set by a server profile became `save-load.todo.uncomplete` and +`search.time-range.todo.no-dtstart.no-due`, each probed by caldav-server-tester, and the +rest were set by no profile. `incompatibility_description`, the `old_flags` key and the +test helpers reading them are removed. ## 4. `feature_tree` / `_dots_to_tree` — internal detail that can be simplified @@ -104,8 +91,7 @@ return ( The `enable`, `value` and `observed` slots exist to correctly compare `client-feature` and `server-observation` nodes during collapse. In practice, collapse is only ever invoked -on server-feature nodes (the only type that appears in the server config dicts, aside -from `old_flags`). The extra slots add noise without effect. +on server-feature nodes (the only type that appears in the server config dicts). The extra slots add noise without effect. **Fix**: Simplify to `return feature_dict.get('support')` once the broader type-system simplification is settled. @@ -150,5 +136,5 @@ want a second look at the same time. ## Ordering / dependencies -Items 3 (old_flags) and 6 (rename) are independent and safe to do first. Items 1, 2, +Items 3 (old_flags) and 6 (rename) are done. Items 1, 2, 4, 5 are interrelated around the type-system simplification and are best tackled together. diff --git a/tests/test_async_integration.py b/tests/test_async_integration.py index 404b3a27..ca23923b 100644 --- a/tests/test_async_integration.py +++ b/tests/test_async_integration.py @@ -192,9 +192,6 @@ def skip_unless_support(self, feature): msg = self._features.find_feature(feature).get("description", feature) pytest.skip("Test skipped due to server incompatibility issue: " + msg) - def check_compatibility_flag(self, flag: str) -> bool: - return flag in getattr(self._features, "_old_flags", []) - @pytest.fixture(scope="class") @classmethod def test_server(cls) -> TestServer: @@ -1740,13 +1737,10 @@ async def test_todo_datesearch(self, async_task_list: Any) -> None: foo = 5 if not self.is_supported("search.recurrences.includes-implicit.todo"): foo -= 1 - if not self.is_supported( - "search.time-range.todo.no-dtstart" - ) or self.check_compatibility_flag( - "vtodo_datesearch_nodtstart_task_is_skipped_in_closed_date_range" - ): - foo -= 2 - elif self.check_compatibility_flag("vtodo_datesearch_notime_task_is_skipped"): + if not self.is_supported("search.time-range.todo.no-dtstart"): + foo -= 1 + ## follows no-dtstart unless set + if not self.is_supported("search.time-range.todo.no-dtstart.no-due"): foo -= 1 if implicit_fragile: @@ -1779,14 +1773,9 @@ async def test_create_journal_list_and_journal_entry(self, async_journal_list: A # ==================== Group E – Properties & Meta ==================== - def _skip_on_compatibility_flag(self, flag: str) -> None: - if self.check_compatibility_flag(flag): - pytest.skip(f"Test skipped due to compatibility flag: {flag}") - @pytest.mark.asyncio async def test_support(self, async_client: Any) -> None: """check_dav_support / check_cdav_support / check_scheduling_support.""" - self._skip_on_compatibility_flag("dav_not_supported") assert await async_client.check_dav_support() assert await async_client.check_cdav_support() if self.is_supported("scheduling", return_type=str) != "unknown": diff --git a/tests/test_caldav.py b/tests/test_caldav.py index 46e6950f..c3e95a9a 100644 --- a/tests/test_caldav.py +++ b/tests/test_caldav.py @@ -53,10 +53,7 @@ _config = load_test_server_config() rfc6638_users = _config.get("rfc6638_users", []) from caldav import Calendar, DAVObject, Event, FreeBusy, Principal, Todo -from caldav.compatibility_hints import ( - incompatibility_description, ## TEMP - should be removed in the future - write_delay, -) +from caldav.compatibility_hints import write_delay from caldav.davclient import CONNKEYS, DAVClient, DAVResponse from caldav.elements import cdav, dav, ical from caldav.lib import error @@ -1404,24 +1401,12 @@ class RepeatedFunctionalTestsBaseClass: ## TODO: move to davclient or compatibility_hints def is_supported(self, feature, return_type=bool, accept_fragile=False): """ - New-style. It will replace check_compatibility_flag. - TODO: write a better docstring """ return self.caldav.features.is_supported( feature, return_type, accept_fragile=accept_fragile ) - def check_compatibility_flag(self, flag): - ## yield an assertion error if checking for the wrong thig - assert flag in incompatibility_description - return flag in self.old_features - - def skip_on_compatibility_flag(self, flag): - if self.check_compatibility_flag(flag): - msg = incompatibility_description[flag] - pytest.skip("Test skipped due to server incompatibility issue: " + msg) - def skip_unless_support(self, feature): if not self.is_supported(feature): msg = self.caldav.features.find_feature(feature).get("description", feature) @@ -1435,19 +1420,12 @@ def setup_method(self): self.caldav = client(**self.server_params) self.caldav.__enter__() - ## Temp thing - self.old_features = self.caldav.features._old_flags - calendar_info = self.is_supported("test-calendar", dict) self.cleanup_regime = calendar_info.get("cleanup-regime", "light") if "cleanup" in self.server_params: self.cleanup_regime = self.server_params["cleanup"] - ## verify that all old flags are valid - for flag in self.old_features: - assert flag in incompatibility_description - self.testcal_id = "pythoncaldav-test" self.testcal_id2 = "pythoncaldav-test2" @@ -1469,14 +1447,7 @@ def setup_method(self): ## Instance-level wrap (like rate-limit), torn down with the client. self.caldav.request = _write_delay_decorator(self.caldav.request, t=delay) - if False and self.check_compatibility_flag("no-current-user-principal"): - self.principal = Principal(client=self.caldav, url=self.server_params["principal_url"]) - else: - self.principal = self.caldav.principal() - - # if self.check_compatibility_flag('delete_calendar_on_startup'): - # for x in self._fixCalendar().search(): - # x.delete() + self.principal = self.caldav.principal() self._cleanup("pre") @@ -1588,12 +1559,6 @@ def _cleanup(self, mode=None): def _teardownCalendar(self, name=None, cal_id=None): try: cal = self.principal.calendar(name=name, cal_id=cal_id) - if self.check_compatibility_flag("sticky_events"): - for goo in cal.objects(): - try: - goo.delete() - except: - pass cal.delete() except: pass @@ -1761,7 +1726,6 @@ def testSupport(self): """ Test the check_*_support methods """ - self.skip_on_compatibility_flag("dav_not_supported") assert self.caldav.check_dav_support() assert self.caldav.check_cdav_support() if self.is_supported("scheduling", str) != "unknown": @@ -2821,16 +2785,14 @@ def testLoadEvent(self): assert c2 is not None e1_ = c1.add_event(near_now_ics(ev1)) - if not self.check_compatibility_flag("event_by_url_is_broken"): - e1_.load() + e1_.load() e1 = c1.get_events()[0] - if not self.check_compatibility_flag("event_by_url_is_broken"): - ## e1 came from a search and may carry a different (canonical) URL - ## than the PUT URL on servers that don't preserve it (e.g. OX); see - ## save-load.stable-url. - if self.is_supported("save-load.stable-url"): - assert e1.url == e1_.url - e1.load() + ## e1 came from a search and may carry a different (canonical) URL + ## than the PUT URL on servers that don't preserve it (e.g. OX); see + ## save-load.stable-url. + if self.is_supported("save-load.stable-url"): + assert e1.url == e1_.url + e1.load() if self.cleanup_regime == "post": self._teardownCalendar(cal_id=self.testcal_id) self._teardownCalendar(cal_id=self.testcal_id2) @@ -2867,13 +2829,12 @@ def testCopyEvent(self): ## which shares the id with the event in c1? e1_in_c2.vobject_instance.vevent.summary.value = "asdf" e1_in_c2.save() - if not self.check_compatibility_flag("event_by_url_is_broken"): - e1.load() - ## should e1.summary be 'asdf' or 'Bastille Day Party'? I do - ## not know, but all implementations I've tested will treat - ## the copy in the other calendar as a distinct entity, even - ## if the uid is the same. - assert e1.vobject_instance.vevent.summary.value == "Bastille Day Party" + e1.load() + ## should e1.summary be 'asdf' or 'Bastille Day Party'? I do + ## not know, but all implementations I've tested will treat + ## the copy in the other calendar as a distinct entity, even + ## if the uid is the same. + assert e1.vobject_instance.vevent.summary.value == "Bastille Day Party" assert c2.get_events()[0].vobject_instance.vevent.uid == e1.vobject_instance.vevent.uid ## Duplicate the event in the same calendar, with same uid - @@ -2950,8 +2911,7 @@ def testSearchEvent(self): start=datetime(2006, 7, 13, 13, 0), end=datetime(2006, 7, 15, 13, 0), ) - if not self.check_compatibility_flag("fastmail_buggy_noexpand_date_search"): - assert len(some_events) == 1 + assert len(some_events) == 1 ## Search for misc text fields ## UID is a special case, supported by almost all servers @@ -3275,8 +3235,7 @@ def testSearchTodos(self): assert not t3.is_pending() ## Test that uncomplete works - ## (except for GMX ... their server is weird) - self.skip_on_compatibility_flag("vtodo-cannot-be-uncompleted") + self.skip_unless_support("save-load.todo.uncomplete") t5.uncomplete() some_todos = c.search(todo=True) assert len(some_todos) == 4 + pre_cnt @@ -3762,7 +3721,7 @@ def testTodoDatesearch(self): c = self._fixCalendar(supported_calendar_component_set=["VTODO"]) # add todo-item - t1 = c.add_todo(todo) + c.add_todo(todo) t2 = c.add_todo(todo2) assert t2 is not None t3 = c.add_todo(todo3) @@ -3837,13 +3796,10 @@ def testTodoDatesearch(self): ) if not self.is_supported("search.recurrences.includes-implicit.todo"): foo -= 1 ## t6 will not be returned - if not self.is_supported( - "search.time-range.todo.no-dtstart" - ) or self.check_compatibility_flag( - "vtodo_datesearch_nodtstart_task_is_skipped_in_closed_date_range" - ): - foo -= 2 ## t1 and t4 not returned - elif self.check_compatibility_flag("vtodo_datesearch_notime_task_is_skipped"): + if not self.is_supported("search.time-range.todo.no-dtstart"): + foo -= 1 ## t1 not returned + ## follows no-dtstart unless set, so t4 goes too where t1 does + if not self.is_supported("search.time-range.todo.no-dtstart.no-due"): foo -= 1 ## t4 not returned if implicit_todo_fragile: assert len(todos1) in (foo, foo + 1) @@ -3889,8 +3845,8 @@ def testTodoDatesearch(self): ) ## * t6 should be returned, as it's a yearly task spanning over 2025 - ## * t1 should probably be returned, as it has no due date set and hence - ## has an infinite duration. + ## * t1 should not be returned, as it has no dtstart and is due in 2007 + ## (RFC4791 section 9.9) ## * t4 should probably be returned, as it has no dtstart nor due and ## hence is also considered to span over infinite time urls_found = [x.url for x in todos1] @@ -3899,12 +3855,8 @@ def testTodoDatesearch(self): urls_found = set(urls_found) if self.is_supported("search.recurrences.includes-implicit.todo", accept_fragile=True): urls_found.discard(t6.url) - if self.is_supported( - "search.time-range.todo.no-dtstart" - ) and not self.check_compatibility_flag("vtodo_datesearch_notime_task_is_skipped"): + if self.is_supported("search.time-range.todo.no-dtstart.no-due"): urls_found.discard(t4.url) - if self.check_compatibility_flag("vtodo_no_due_infinite_duration"): - urls_found.discard(t1.url) ## everything should be popped from urls_found by now assert len(urls_found) == 0 @@ -4093,9 +4045,8 @@ def testTodoCompletion(self): t2.delete() # ... the deleted one is gone ... - if not self.check_compatibility_flag("event_by_url_is_broken"): - todos = c.get_todos(include_completed=True) - assert len(todos) == 2 + todos = c.get_todos(include_completed=True) + assert len(todos) == 2 # date search should not include completed events ... hum. # TODO, fixme. @@ -4385,8 +4336,7 @@ def testCreateOverwriteDeleteEvent(self): assert e1.url is not None if todo_ok: assert t1.url is not None - if not self.check_compatibility_flag("event_by_url_is_broken"): - assert c.event_by_url(e1.url).url == e1.url + assert c.event_by_url(e1.url).url == e1.url ## get_event_by_uid may return a different (canonical) URL than the PUT ## URL on servers that don't preserve it (e.g. OX); see save-load.stable-url e_by_uid = c.get_event_by_uid(e1.id) @@ -4426,9 +4376,8 @@ def testCreateOverwriteDeleteEvent(self): ) t2.save(no_create=no_create) - if not self.check_compatibility_flag("event_by_url_is_broken"): - e3 = c.event_by_url(e1.url) - assert e3.vobject_instance.vevent.summary.value == "Bastille Day Party!" + e3 = c.event_by_url(e1.url) + assert e3.vobject_instance.vevent.summary.value == "Bastille Day Party!" ## "no_overwrite" should throw a ConsistencyError. with pytest.raises(error.ConsistencyError): @@ -4451,9 +4400,8 @@ def testCreateOverwriteDeleteEvent(self): ): with pytest.raises(self._notFound()): c.event_by_url(e2.url) - if not self.check_compatibility_flag("event_by_url_is_broken"): - with pytest.raises(error.NotFoundError): - c.get_event_by_uid("20010712T182145Z-123401@example.com") + with pytest.raises(error.NotFoundError): + c.get_event_by_uid("20010712T182145Z-123401@example.com") @pytest.mark.filterwarnings("ignore:use `calendar.search:DeprecationWarning") def testDateSearchAndFreeBusy(self): @@ -4640,16 +4588,10 @@ def testRecurringDateSearch(self): ## resultset should be one vcalendar with two events. assert len(r1) == 1 assert "RRULE" not in r1[0].data - if self.check_compatibility_flag("robur_rrule_freq_yearly_expands_monthly"): - assert r1[0].data.count("END:VEVENT") == 13 - else: - assert r1[0].data.count("END:VEVENT") == 2 + assert r1[0].data.count("END:VEVENT") == 2 ## However, the new search method will by default split it into - ## two events. Or 13, in the case of robur - if self.check_compatibility_flag("robur_rrule_freq_yearly_expands_monthly"): - assert len(r2) == 13 - else: - assert len(r2) == 2 + ## two events. + assert len(r2) == 2 assert "RRULE" not in r2[0].data assert "RRULE" not in r2[1].data assert r2[0].data.count("END:VEVENT") == 1