[CalendarServer-changes] [9761] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Fri Aug 31 12:13:52 PDT 2012
Revision: 9761
http://trac.macosforge.org/projects/calendarserver/changeset/9761
Author: cdaboo at apple.com
Date: 2012-08-31 12:13:51 -0700 (Fri, 31 Aug 2012)
Log Message:
-----------
Add optional duplicate alarm removal.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tools/calverify.py
CalendarServer/trunk/twistedcaldav/ical.py
CalendarServer/trunk/twistedcaldav/method/put_common.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
Modified: CalendarServer/trunk/calendarserver/tools/calverify.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/calverify.py 2012-08-31 18:55:58 UTC (rev 9760)
+++ CalendarServer/trunk/calendarserver/tools/calverify.py 2012-08-31 19:13:51 UTC (rev 9761)
@@ -164,9 +164,34 @@
return fixed, unfixed
+def new_hasDuplicateAlarms(self, doFix=False):
+ """
+ test and optionally remove alarms that have the same ACTION and TRIGGER values in the same component.
+ """
+ changed = False
+ if self.name() in ("VCALENDAR", "X-CALENDARSERVER-PERUSER",):
+ for component in self.subcomponents():
+ if component.name() in ("VTIMEZONE",):
+ continue
+ changed = component.hasDuplicateAlarms(doFix) or changed
+ else:
+ action_trigger = set()
+ for component in tuple(self.subcomponents()):
+ if component.name() == "VALARM":
+ item = (component.propertyValue("ACTION"), component.propertyValue("TRIGGER"),)
+ if item in action_trigger:
+ if doFix:
+ self.removeComponent(component)
+ changed = True
+ else:
+ action_trigger.add(item)
+ return changed
+
Component.validRecurrenceIDs = new_validRecurrenceIDs
+if not hasattr(Component, "maxAlarmCounts"):
+ Component.hasDuplicateAlarms = new_hasDuplicateAlarms
-VERSION = "6"
+VERSION = "7"
def printusage(e=None):
if e:
@@ -934,6 +959,8 @@
component.validCalendarData(doFix=False, validateRecurrences=True)
component.validCalendarForCalDAV(methodAllowed=isinbox)
component.validOrganizerForScheduling(doFix=False)
+ if component.hasDuplicateAlarms(doFix=False):
+ raise InvalidICalendarDataError("Duplicate VALARMS")
self.noPrincipalPathCUAddresses(component, doFix=False)
except ValueError, e:
result = False
@@ -1058,6 +1085,7 @@
component.validCalendarData(doFix=True, validateRecurrences=True)
component.validCalendarForCalDAV(methodAllowed=isinbox)
component.validOrganizerForScheduling(doFix=True)
+ component.hasDuplicateAlarms(doFix=True)
self.noPrincipalPathCUAddresses(component, doFix=True)
except ValueError:
result = False
Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py 2012-08-31 18:55:58 UTC (rev 9760)
+++ CalendarServer/trunk/twistedcaldav/ical.py 2012-08-31 19:13:51 UTC (rev 9761)
@@ -2301,7 +2301,30 @@
for component in tuple(self.subcomponents()):
if component.name() == "VALARM":
self.removeComponent(component)
-
+
+ def hasDuplicateAlarms(self, doFix=False):
+ """
+ Test and optionally remove alarms that have the same ACTION and TRIGGER values in the same component.
+ """
+ changed = False
+ if self.name() in ("VCALENDAR", "X-CALENDARSERVER-PERUSER",):
+ for component in self.subcomponents():
+ if component.name() in ("VTIMEZONE",):
+ continue
+ changed = component.hasDuplicateAlarms(doFix) or changed
+ else:
+ action_trigger = set()
+ for component in tuple(self.subcomponents()):
+ if component.name() == "VALARM":
+ item = (component.propertyValue("ACTION"), component.propertyValue("TRIGGER"),)
+ if item in action_trigger:
+ if doFix:
+ self.removeComponent(component)
+ changed = True
+ else:
+ action_trigger.add(item)
+ return changed
+
def filterProperties(self, remove=None, keep=None, do_subcomponents=True):
"""
Remove all properties that do not match the provided set.
Modified: CalendarServer/trunk/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/put_common.py 2012-08-31 18:55:58 UTC (rev 9760)
+++ CalendarServer/trunk/twistedcaldav/method/put_common.py 2012-08-31 19:13:51 UTC (rev 9761)
@@ -805,41 +805,45 @@
returnValue(changed)
- def addDefaultAlarm(self):
+ def processAlarms(self):
"""
- Add a default alarm if required.
+ Remove duplicate alarms. Add a default alarm if required.
@return: indicate whether a change was made
@rtype: C{bool}
"""
+ # Remove duplicate alarms
+ changed = False
+ if config.RemoveDuplicateAlarms:
+ changed = self.calendar.hasDuplicateAlarms(doFix=True)
+
# Only if feature enabled
if not config.EnableDefaultAlarms:
- return False
+ return changed
# Check that we are creating and this is not the inbox
if not self.destinationcal or self.destination.exists() or self.isiTIP:
- return False
+ return changed
# Never add default alarms to calendar data in shared calendars
if self.destinationparent.isVirtualShare():
- return False
+ return changed
# Add default alarm for VEVENT and VTODO only
mtype = self.calendar.mainType().upper()
if self.calendar.mainType().upper() not in ("VEVENT", "VTODO"):
- return False
+ return changed
vevent = mtype == "VEVENT"
# Check timed or all-day
start, _ignore_end = self.calendar.mainComponent(allow_multiple=True).getEffectiveStartEnd()
if start is None:
# Yes VTODOs might have no DTSTART or DUE - in this case we do not add a default
- return False
+ return changed
timed = not start.isDateOnly()
# See if default exists and add using appropriate logic
- changed = False
alarm = self.destinationparent.getDefaultAlarm(vevent, timed)
if alarm:
changed = self.calendar.addAlarms(alarm)
@@ -1167,8 +1171,8 @@
# Handle sharing dropbox normalization
dropboxChanged = (yield self.dropboxPathNormalization())
- # Default alarms
- alarmChanged = self.addDefaultAlarm()
+ # Default/duplicate alarms
+ alarmChanged = self.processAlarms()
# Do scheduling
implicit_result = (yield self.doImplicitScheduling())
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2012-08-31 18:55:58 UTC (rev 9760)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2012-08-31 19:13:51 UTC (rev 9761)
@@ -561,7 +561,8 @@
# the database, merge the data from the filesystem
# into the database homes.
- "EnableDefaultAlarms" : True, # Support for default alarms generated by the server
+ "EnableDefaultAlarms" : True, # Support for default alarms generated by the server
+ "RemoveDuplicateAlarms": True, # Remove duplicate alarms on PUT
# CardDAV Features
"DirectoryAddressBook": {
Modified: CalendarServer/trunk/twistedcaldav/test/test_icalendar.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2012-08-31 18:55:58 UTC (rev 9760)
+++ CalendarServer/trunk/twistedcaldav/test/test_icalendar.py 2012-08-31 19:13:51 UTC (rev 9761)
@@ -7441,3 +7441,613 @@
component.removeComponent(subComponent)
self.assertEquals(subComponent._parent, None)
self.assertEquals(component._cachedCopy, None) # cache is invalidated
+
+ def test_hasDuplicateAlarms(self):
+ """
+ Test that L{Component.hasDuplicateAlarms} correctly detects, but does not fix, duplicate alarms.
+ """
+
+ data = (
+ # No Alarms
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+
+ # One alarm
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+
+ # Two different alarms
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+
+ # Two duplicates, one different
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+
+ # Two duplicates in one component, three different in another
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:PT0S
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+
+ # Four duplicates in X-CALENDARSERVER-PERINSTANCE
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ True,
+ ),
+ )
+
+ for txt, result in data:
+ component = Component.fromString(txt)
+ self.assertEqual(component.hasDuplicateAlarms(doFix=False), result)
+ self.assertEqual(str(component), txt.replace("\n", "\r\n"))
+
+ def test_hasDuplicateAlarms_withFix(self):
+ """
+ Test that L{Component.hasDuplicateAlarms} correctly removes duplicate alarms.
+ """
+
+ data = (
+ # No Alarms
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+
+ # One alarm
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+
+ # Two different alarms
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ False,
+ ),
+
+ # Two duplicates, one different
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+
+ # Two duplicates in one component, three different in another
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+RRULE:FREQ=DAILY
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT0M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:VEVENT
+BEGIN:VEVENT
+UID:12345-67890
+RECURRENCE-ID:20080602T120000Z
+DTSTART:20080602T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT5M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:PT0S
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""",
+ True,
+ ),
+
+ # Four duplicates in X-CALENDARSERVER-PERINSTANCE
+ (
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+SUMMARY:Test
+RRULE:FREQ=DAILY
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:12345-67890
+DTSTART:20080601T120000Z
+DURATION:PT1H
+DTSTAMP:20080601T120000Z
+RRULE:FREQ=DAILY
+SUMMARY:Test
+END:VEVENT
+BEGIN:X-CALENDARSERVER-PERUSER
+UID:12345-67890
+X-CALENDARSERVER-PERUSER-UID:user01
+BEGIN:X-CALENDARSERVER-PERINSTANCE
+BEGIN:VALARM
+ACTION:DISPLAY
+DESCRIPTION:Test
+TRIGGER;RELATED=START:-PT10M
+END:VALARM
+END:X-CALENDARSERVER-PERINSTANCE
+END:X-CALENDARSERVER-PERUSER
+END:VCALENDAR
+""",
+ True,
+ ),
+ )
+
+ for txt, result, result_changed in data:
+ component = Component.fromString(txt)
+ changed = component.hasDuplicateAlarms(doFix=True)
+ self.assertEqual(str(component), result.replace("\n", "\r\n"))
+ self.assertEqual(changed, result_changed)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120831/9d93a737/attachment-0001.html>
More information about the calendarserver-changes
mailing list