[CalendarServer-changes] [6819] CalendarServer/trunk/contrib/performance/loadtest
source_changes at macosforge.org
source_changes at macosforge.org
Fri Jan 28 14:25:53 PST 2011
Revision: 6819
http://trac.macosforge.org/projects/calendarserver/changeset/6819
Author: exarkun at twistedmatrix.com
Date: 2011-01-28 14:25:52 -0800 (Fri, 28 Jan 2011)
Log Message:
-----------
Track schedule-tag; expose event change events to observers; add a method for changing an attendee on an event; track events globally and per-calendar.
Modified Paths:
--------------
CalendarServer/trunk/contrib/performance/loadtest/ical.py
CalendarServer/trunk/contrib/performance/loadtest/test_ical.py
Modified: CalendarServer/trunk/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/ical.py 2011-01-27 21:48:24 UTC (rev 6818)
+++ CalendarServer/trunk/contrib/performance/loadtest/ical.py 2011-01-28 22:25:52 UTC (rev 6819)
@@ -42,6 +42,8 @@
from httpclient import StringProducer, readBody
from httpauth import AuthHandlerAgent
+from subscribe import Periodical
+
def loadRequestBody(label):
return FilePath(__file__).sibling('request-data').child(label + '.request').getContent()
@@ -53,6 +55,7 @@
def __init__(self, url, etag, vevent=None):
self.url = url
self.etag = etag
+ self.scheduleTag = None
self.vevent = vevent
@@ -63,15 +66,24 @@
self.name = name
self.url = url
self.ctag = ctag
+ self.events = {}
class BaseClient(object):
+ user = None
+ _events = None
+ _calendars = None
+
def addEventAttendee(self, href, attendee):
raise NotImplementedError("%r does not implement addEventAttendee" % (self.__class__,))
+ def changeEventAttendee(self, href, oldAttendee, newAttendee):
+ raise NotImplementedError("%r does not implement changeEventAttendee" % (self.__class__,))
+
+
class SnowLeopard(BaseClient):
"""
Implementation of the SnowLeopard iCal network behavior.
@@ -86,7 +98,7 @@
USER_AGENT = "DAVKit/4.0.3 (732); CalendarStore/4.0.3 (991); iCal/4.0.3 (1388); Mac OS X/10.6.4 (10F569)"
- CALENDAR_HOME_POLL_INTERVAL = 15 * 60
+ CALENDAR_HOME_POLL_INTERVAL = 15 # * 60
_STARTUP_PRINCIPAL_PROPFIND = loadRequestBody('sl_startup_principal_propfind')
_STARTUP_PRINCIPALS_REPORT = loadRequestBody('sl_startup_principals_report')
@@ -118,7 +130,12 @@
# part of), values are Event instances.
self._events = {}
+ # Allow events to go out into the world.
+ self.catalog = {
+ "eventChanged": Periodical(),
+ }
+
def _request(self, expectedResponseCode, method, url, headers=None, body=None):
if headers is None:
headers = Headers({})
@@ -128,12 +145,15 @@
before = self.reactor.seconds()
def report(response):
success = response.code == expectedResponseCode
+ # if not success:
+ # import pdb; pdb.set_trace()
after = self.reactor.seconds()
# XXX This is time to receive response headers, not time
# to receive full response. Should measure the latter, if
# not both.
msg(
type="response", success=success, method=method,
+ headers=headers, body=body,
duration=(after - before), url=url)
return response
d.addCallback(report)
@@ -253,8 +273,6 @@
Headers({'content-type': ['text/xml'], 'depth': ['1']}),
StringProducer(self._CALENDAR_PROPFIND))
- # XXX Check the response status code
-
body = yield readBody(response)
result = self._parseMultiStatus(body)
@@ -269,24 +287,39 @@
continue
if responseHref not in self._events:
- self._events[responseHref] = Event(responseHref, None)
+ self._setEvent(responseHref, Event(responseHref, None))
event = self._events[responseHref]
if event.etag != etag:
- response = yield self._updateEvent(url, responseHref)
+ response = yield self._eventReport(url, responseHref)
body = yield readBody(response)
- self.eventChanged(responseHref, body)
+ res = self._parseMultiStatus(body)[responseHref]
+ text = res.getTextProperties()
+ etag = text[davxml.getetag]
+ try:
+ scheduleTag = text[caldavxml.schedule_tag]
+ except KeyError:
+ scheduleTag = None
+ body = text[caldavxml.calendar_data]
+ self.eventChanged(responseHref, etag, scheduleTag, body)
- def eventChanged(self, href, body):
+ def _setEvent(self, href, event):
+ self._events[href] = event
+ calendar, uid = href.rsplit('/', 1)
+ self._calendars[calendar + '/'].events[uid] = event
+
+
+ def eventChanged(self, href, etag, scheduleTag, body):
event = self._events[href]
- res = self._parseMultiStatus(body)[href]
- text = res.getTextProperties()
- event.etag = text[davxml.getetag]
- event.vevent = list(readComponents(text[caldavxml.calendar_data]))[0]
+ event.etag = etag
+ if scheduleTag is not None:
+ event.scheduleTag = scheduleTag
+ event.vevent = list(readComponents(body))[0]
+ self.catalog["eventChanged"].issue(href)
- def _updateEvent(self, calendar, event):
+ def _eventReport(self, calendar, event):
# Next do a REPORT on each event that might have information
# we don't know about.
return self._request(
@@ -465,18 +498,43 @@
StringProducer(vevent.serialize()))
return d
d.addCallback(availability)
- def check(ignored):
- # Finally, re-retrieve the event
- return self._request(OK, 'GET', self.root + href[1:])
- d.addCallback(check)
+ # Finally, re-retrieve the event to update the etag
+ d.addCallback(self._updateEvent, href)
+ return d
+
+
+ def changeEventAttendee(self, href, oldAttendee, newAttendee):
+ event = self._events[href]
+ vevent = event.vevent
+
+ # Change the event to have the new attendee instead of the old attendee
+ attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+ attendees.remove(oldAttendee)
+ attendees.append(newAttendee)
+ headers = Headers({
+ 'content-type': ['text/calendar'],
+ })
+ if event.scheduleTag is not None:
+ headers.addRawHeader('if-schedule-tag-match', event.scheduleTag)
+
+ d = self._request(
+ NO_CONTENT, 'PUT', self.root + href[1:],
+ headers, StringProducer(vevent.serialize()))
+ d.addCallback(self._updateEvent, href)
+ return d
+
+
+ def _updateEvent(self, ignored, href):
+ d = self._request(OK, 'GET', self.root + href[1:])
def getETag(response):
- etag = response.headers.getRawHeaders('etag')[0]
- return readBody(response).addCallback(lambda body: (etag, body))
+ headers = response.headers
+ etag = headers.getRawHeaders('etag')[0]
+ scheduleTag = headers.getRawHeaders('schedule-tag')[0]
+ return readBody(response).addCallback(
+ lambda body: (etag, scheduleTag, body))
d.addCallback(getETag)
- def record((etag, body)):
- event = self._events[href]
- event.etag = etag
- event.vevent = list(readComponents(body))[0]
+ def record((etag, scheduleTag, body)):
+ self.eventChanged(href, etag, scheduleTag, body)
d.addCallback(record)
return d
Modified: CalendarServer/trunk/contrib/performance/loadtest/test_ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_ical.py 2011-01-27 21:48:24 UTC (rev 6818)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_ical.py 2011-01-28 22:25:52 UTC (rev 6819)
@@ -15,6 +15,10 @@
#
##
+from vobject import readComponents
+from vobject.base import ContentLine
+
+from twisted.internet.defer import Deferred
from twisted.trial.unittest import TestCase
from protocol.url import URL
@@ -22,7 +26,8 @@
from protocol.caldav.definitions import caldavxml
from protocol.caldav.definitions import csxml
-from ical import SnowLeopard
+from loadtest.ical import Event, SnowLeopard
+from httpclient import MemoryConsumer
PRINCIPAL_PROPFIND_RESPONSE = """\
@@ -719,9 +724,47 @@
</multistatus>
"""
+EVENT = """\
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.3//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:America/New_York
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0500
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:EDT
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0400
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:EST
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20101018T155454Z
+UID:D94F247D-7433-43AF-B84B-ADD684D023B0
+DTEND;TZID=America/New_York:20101028T130000
+ATTENDEE;CN="User 03";CUTYPE=INDIVIDUAL;EMAIL="user03 at example.com";PARTS
+ TAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:user03 at example.co
+ m
+ATTENDEE;CN="User 01";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:user01@
+ example.com
+TRANSP:OPAQUE
+SUMMARY:Attended Event
+DTSTART;TZID=America/New_York:20101028T120000
+DTSTAMP:20101018T155513Z
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+SEQUENCE:3
+END:VEVENT
+END:VCALENDAR
+"""
-
-
class SnowLeopardTests(TestCase):
"""
Tests for L{SnowLeopard}.
@@ -801,3 +844,44 @@
self.assertEquals(outbox.name, None)
self.assertEquals(outbox.url, "/calendars/__uids__/user01/outbox/")
self.assertEquals(outbox.ctag, None)
+
+
+ def test_changeEventAttendee(self):
+ """
+ SnowLeopard.changeEventAttendee removes one attendee from an
+ existing event and appends another.
+ """
+ requests = []
+ def request(*args):
+ result = Deferred()
+ requests.append((result, args))
+ return result
+ self.client._request = request
+
+ vevent = list(readComponents(EVENT))[0]
+ attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+ old = attendees[0]
+ new = ContentLine.duplicate(old)
+ new.params[u'CN'] = [u'Some Other Guy']
+ event = Event('/some/calendar/1234.ics', None, vevent)
+ self.client._events[event.url] = event
+ self.client.changeEventAttendee(event.url, old, new)
+
+ result, req = requests.pop(0)
+
+ # iCal PUTs the new VCALENDAR object.
+ expectedResponseCode, method, url, headers, body = req
+ self.assertEquals(method, 'PUT')
+ self.assertEquals(url, 'http://127.0.0.1:80' + event.url)
+ self.assertEquals(headers.getRawHeaders('content-type'), ['text/calendar'])
+
+ consumer = MemoryConsumer()
+ finished = body.startProducing(consumer)
+ def cbFinished(ignored):
+ vevent = list(readComponents(consumer.value()))[0]
+ attendees = vevent.contents[u'vevent'][0].contents[u'attendee']
+ self.assertEquals(len(attendees), 2)
+ self.assertEquals(attendees[0].params[u'CN'], [u'User 01'])
+ self.assertEquals(attendees[1].params[u'CN'], [u'Some Other Guy'])
+ finished.addCallback(cbFinished)
+ return finished
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110128/21336265/attachment-0001.html>
More information about the calendarserver-changes
mailing list