[CalendarServer-changes] [15588] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Thu May 12 12:28:46 PDT 2016
Revision: 15588
http://trac.calendarserver.org//changeset/15588
Author: sagen at apple.com
Date: 2016-05-12 12:28:46 -0700 (Thu, 12 May 2016)
Log Message:
-----------
Client sim changes: Eventer and Inviter now post attachments; by default each user has a secondary, passive, client which just syncs and downloads attachments; when we get an unexpected response code, the request and response are printed; managed attachments are fetched through the client's "home" server (or the server specified in the managed-attachments-server-URL property.
Modified Paths:
--------------
CalendarServer/trunk/contrib/performance/loadtest/clients.plist
CalendarServer/trunk/contrib/performance/loadtest/config.plist
CalendarServer/trunk/contrib/performance/loadtest/ical.py
CalendarServer/trunk/contrib/performance/loadtest/population.py
CalendarServer/trunk/contrib/performance/loadtest/profiles.py
CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request
CalendarServer/trunk/contrib/performance/loadtest/test_ical.py
CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
CalendarServer/trunk/requirements-dev.txt
Modified: CalendarServer/trunk/contrib/performance/loadtest/clients.plist
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/clients.plist 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/clients.plist 2016-05-12 19:28:46 UTC (rev 15588)
@@ -38,7 +38,7 @@
<dict>
<!-- Name that appears in logs. -->
<key>title</key>
- <string>10.11</string>
+ <string>10.11a</string>
<!-- OS_X_10_11 can poll the calendar home at some interval. This is
in seconds. -->
@@ -163,6 +163,24 @@
</dict>
</dict>
</dict>
+
+ <!-- Define the attachment size distribution. -->
+ <key>fileSizeDistribution</key>
+ <dict>
+ <key>type</key>
+ <string>contrib.performance.stats.NormalDistribution</string>
+ <key>params</key>
+ <dict>
+ <!-- mu gives the mean of the normal distribution (in seconds). -->
+ <key>mu</key>
+ <integer>500000</integer>
+
+ <!-- and sigma gives its standard deviation. -->
+ <key>sigma</key>
+ <integer>100000</integer>
+ </dict>
+ </dict>
+
</dict>
</dict>
@@ -343,7 +361,7 @@
<key>params</key>
<dict>
<key>enabled</key>
- <true/>
+ <false/>
<!-- Define the interval (in seconds) at which this profile will use
its client to create a new event. -->
@@ -578,6 +596,24 @@
</dict>
</dict>
</dict>
+
+ <!-- Define the attachment size distribution. -->
+ <key>fileSizeDistribution</key>
+ <dict>
+ <key>type</key>
+ <string>contrib.performance.stats.NormalDistribution</string>
+ <key>params</key>
+ <dict>
+ <!-- mu gives the mean of the normal distribution (in seconds). -->
+ <key>mu</key>
+ <integer>500000</integer>
+
+ <!-- and sigma gives its standard deviation. -->
+ <key>sigma</key>
+ <integer>100000</integer>
+ </dict>
+ </dict>
+
</dict>
</dict>
@@ -712,6 +748,58 @@
<key>weight</key>
<integer>1</integer>
</dict>
+
+ <dict>
+
+ <!-- Here is a more passive OS X client simulator. -->
+ <key>software</key>
+ <string>contrib.performance.loadtest.ical.OS_X_10_11</string>
+
+ <!-- Arguments to use to initialize the OS_X_10_11 instance. -->
+ <key>params</key>
+ <dict>
+ <!-- Name that appears in logs. -->
+ <key>title</key>
+ <string>10.11b</string>
+
+ <!-- OS_X_10_11 can poll the calendar home at some interval. This is
+ in seconds. -->
+ <key>calendarHomePollInterval</key>
+ <integer>30</integer>
+
+ <!-- If the server advertises xmpp push, OS_X_10_7 can wait for notifications
+ about calendar home changes instead of polling for them periodically. If
+ this option is true, then look for the server advertisement for xmpp push
+ and use it if possible. Still fall back to polling if there is no xmpp push
+ advertised. -->
+ <key>supportPush</key>
+ <true/>
+
+ <key>supportAmpPush</key>
+ <true/>
+ </dict>
+
+ <!-- The profiles define certain types of user behavior on top of the
+ client software being simulated. -->
+ <key>profiles</key>
+ <array>
+ <!-- This profile downloads attachments when an event changes. -->
+ <dict>
+ <key>class</key>
+ <string>contrib.performance.loadtest.profiles.AttachmentDownloader</string>
+
+ <key>params</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ </dict>
+ </dict>
+ </array>
+ <!-- Determine the frequency at which this client configuration will
+ appear in the clients which are created by the load tester. -->
+ <key>weight</key>
+ <integer>1</integer>
+ </dict>
</array>
</dict>
</plist>
Modified: CalendarServer/trunk/contrib/performance/loadtest/config.plist
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/config.plist 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/config.plist 2016-05-12 19:28:46 UTC (rev 15588)
@@ -157,7 +157,7 @@
<!-- Number of clients each user is assigned to. -->
<!-- Set weight of clients to 1 if this is > 1. Number of clients must match this value if > 1. -->
<key>clientsPerUser</key>
- <integer>1</integer>
+ <integer>2</integer>
</dict>
</dict>
@@ -168,7 +168,8 @@
<!-- ReportStatistics generates an end-of-run summary of the HTTP requests
made, their timings, and their results. -->
<dict>
- <key>type</key>
+ <key>type</key>
+
<string>contrib.performance.loadtest.population.ReportStatistics</string>
<key>params</key>
<dict>
Modified: CalendarServer/trunk/contrib/performance/loadtest/ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/ical.py 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/ical.py 2016-05-12 19:28:46 UTC (rev 15588)
@@ -85,10 +85,14 @@
@ivar response: The response which was received
@type response: L{twisted.web.client.Response}
+
+ @ivar responseBody: The body of the received response
+ @type responseBody: C{str}
"""
- def __init__(self, expected, response):
+ def __init__(self, expected, response, responseBody):
self.expected = expected
self.response = response
+ self.responseBody = responseBody
@@ -447,6 +451,8 @@
_client_type = "Generic"
+ _managed_attachments_server_url = None
+
USER_AGENT = None # Override this for specific clients
# The default interval, used if none is specified in external
@@ -618,9 +624,8 @@
before = self.reactor.seconds()
response = yield self.agent.request(method, url, headers, body)
- # XXX This is time to receive response headers, not time
- # to receive full response. Should measure the latter, if
- # not both.
+ responseBody = yield readBody(response)
+
after = self.reactor.seconds()
success = response.code in expectedResponseCodes
@@ -631,6 +636,7 @@
method=method_label if method_label else method,
headers=headers,
body=body,
+ responseBody=responseBody,
code=response.code,
user=self.record.uid,
client_type=self.title,
@@ -640,22 +646,30 @@
)
if success:
- returnValue(response)
+ returnValue((response, responseBody))
- raise IncorrectResponseCode(expectedResponseCodes, response)
+ raise IncorrectResponseCode(expectedResponseCodes, response, responseBody)
- def _parseMultiStatus(self, response, otherTokens=False):
+ def _parseMultiStatus(self, responseBody, otherTokens=False):
"""
Parse a <multistatus> - might need to return other top-level elements
in the response - e.g. DAV:sync-token
I{PROPFIND} request for the principal URL.
- @type response: C{str}
+ @type responseBody: C{str}
@rtype: C{cls}
"""
parser = PropFindParser()
- parser.parseData(response)
+ try:
+ parser.parseData(responseBody)
+ except:
+ print("=" * 80)
+ print("COULD NOT PARSE RESPONSE:")
+ print(responseBody)
+ print("=" * 80)
+ raise
+
if otherTokens:
return (parser.getResults(), parser.getOthers(),)
else:
@@ -674,7 +688,7 @@
hdrs = Headers({'content-type': ['text/xml']})
if depth is not None:
hdrs.addRawHeader('depth', depth)
- response = yield self._request(
+ response, responseBody = yield self._request(
allowedStatus,
'PROPFIND',
self.server["uri"] + url.encode('utf-8'),
@@ -683,8 +697,7 @@
method_label=method_label,
)
- body = yield readBody(response)
- result = self._parseMultiStatus(body) if response.code == MULTI_STATUS else None
+ result = self._parseMultiStatus(responseBody) if response.code == MULTI_STATUS else None
returnValue((response, result,))
@@ -695,7 +708,7 @@
Issue a PROPPATCH on the chosen URL
"""
hdrs = Headers({'content-type': ['text/xml']})
- response = yield self._request(
+ response, responseBody = yield self._request(
(OK, MULTI_STATUS,),
'PROPPATCH',
self.server["uri"] + url.encode('utf-8'),
@@ -704,8 +717,7 @@
method_label=method_label,
)
if response.code == MULTI_STATUS:
- body = yield readBody(response)
- result = self._parseMultiStatus(body)
+ result = self._parseMultiStatus(responseBody)
returnValue(result)
else:
returnValue(None)
@@ -716,15 +728,14 @@
"""
Issue a GET on the chosen URL
"""
- response = yield self._request(
+ response, responseBody = yield self._request(
allowedStatus,
'GET',
url,
method_label=method_label,
)
- body = yield readBody(response)
- returnValue(body)
+ returnValue(responseBody)
@inlineCallbacks
@@ -735,7 +746,7 @@
hdrs = Headers({'content-type': ['text/xml']})
if depth is not None:
hdrs.addRawHeader('depth', depth)
- response = yield self._request(
+ response, responseBody = yield self._request(
allowedStatus,
'REPORT',
self.server["uri"] + url.encode('utf-8'),
@@ -744,8 +755,7 @@
method_label=method_label,
)
- body = yield readBody(response)
- result = self._parseMultiStatus(body, otherTokens) if response.code == MULTI_STATUS else None
+ result = self._parseMultiStatus(responseBody, otherTokens) if response.code == MULTI_STATUS else None
returnValue(result)
@@ -890,8 +900,20 @@
if href == calendarHome:
text = results[href].getTextProperties()
+ hrefs = results[href].getHrefProperties()
+ # Extract managed attachments url
+ self._managed_attachments_server_url = None
try:
+ url = hrefs[caldavxml.managed_attachments_server_url]
+ if url.toString():
+ self._managed_attachments_server_url = url.toString()
+ except KeyError:
+ pass
+ if self._managed_attachments_server_url is None:
+ self._managed_attachments_server_url = self.server['uri']
+
+ try:
pushkey = text[csxml.pushkey]
except KeyError:
pass
@@ -929,10 +951,9 @@
if isCalendar:
textProps = results[href].getTextProperties()
componentTypes = set()
- if nodeType.tag == caldavxml.calendar:
- if caldavxml.supported_calendar_component_set in nodes:
- for comp in nodes[caldavxml.supported_calendar_component_set]:
- componentTypes.add(comp.get("name").upper())
+ if caldavxml.supported_calendar_component_set in nodes:
+ for comp in nodes[caldavxml.supported_calendar_component_set]:
+ componentTypes.add(comp.get("name").upper())
calendars.append(Calendar(
resourceType,
@@ -1022,7 +1043,7 @@
method_label="REPORT{sync}" if calendar.changeToken else "REPORT{sync-init}",
)
else:
- raise IncorrectResponseCode((MULTI_STATUS,), None)
+ raise IncorrectResponseCode((MULTI_STATUS,), None, None)
result, others = result
@@ -1208,7 +1229,7 @@
method_label="REPORT{sync}" if oldToken else "REPORT{sync-init}",
)
else:
- raise IncorrectResponseCode((MULTI_STATUS,), None)
+ raise IncorrectResponseCode((MULTI_STATUS,), None, None)
result, _ignore_others = result
@@ -1229,24 +1250,24 @@
if result[responseHref].getStatus() / 100 == 2:
# Get the notification
- response = yield self._request(
- OK,
+ response, responseBody = yield self._request(
+ (OK, NOT_FOUND),
'GET',
self.server["uri"] + responseHref.encode('utf-8'),
method_label="GET{notification}",
)
- body = yield readBody(response)
- node = ElementTree(file=StringIO(body)).getroot()
- if node.tag == str(csxml.notification):
- nurl = URL(url=responseHref)
- for child in node.getchildren():
- if child.tag == str(csxml.invite_notification):
- if child.find(str(csxml.invite_noresponse)) is not None:
- inviteNotifications.append(
- InviteNotification().parseFromNotification(
- nurl, child
+ if response.code == OK:
+ node = ElementTree(file=StringIO(responseBody)).getroot()
+ if node.tag == str(csxml.notification):
+ nurl = URL(url=responseHref)
+ for child in node.getchildren():
+ if child.tag == str(csxml.invite_notification):
+ if child.find(str(csxml.invite_noresponse)) is not None:
+ inviteNotifications.append(
+ InviteNotification().parseFromNotification(
+ nurl, child
+ )
)
- )
# Accept the invites
for notification in inviteNotifications:
@@ -1287,7 +1308,7 @@
# Delete all the notification resources
for responseHref in toDelete:
- response = yield self._request(
+ response, responseBody = yield self._request(
(NO_CONTENT, NOT_FOUND),
'DELETE',
self.server["uri"] + responseHref.encode('utf-8'),
@@ -1497,8 +1518,8 @@
def _receivedPush(self, inboundID, dataChangedTimestamp, priority=5):
- for href, id in self.ampPushKeys.iteritems():
- if inboundID == id:
+ for href, myId in self.ampPushKeys.iteritems():
+ if inboundID == myId:
self._checkCalendarsForEvents(href, push=True)
break
else:
@@ -1708,7 +1729,7 @@
headers.removeHeader('if-match')
# At last, upload the new event definition
- response = yield self._request(
+ response, responseBody = yield self._request(
(NO_CONTENT, PRECONDITION_FAILED,),
'PUT',
self.server["uri"] + href.encode('utf-8'),
@@ -1803,7 +1824,7 @@
if len(attendees) > 75:
label_suffix = "huge"
- response = yield self._request(
+ response, responseBody = yield self._request(
okCodes,
'PUT',
self.server["uri"] + href.encode('utf-8'),
@@ -1825,7 +1846,7 @@
self._removeEvent(href)
- response = yield self._request(
+ response, responseBody = yield self._request(
(NO_CONTENT, NOT_FOUND),
'DELETE',
self.server["uri"] + href.encode('utf-8'),
@@ -1835,7 +1856,7 @@
@inlineCallbacks
- def addEvent(self, href, component, invite=False):
+ def addEvent(self, href, component, invite=False, attachmentSize=0):
headers = Headers({
'content-type': ['text/calendar'],
})
@@ -1849,7 +1870,7 @@
if len(attendees) > 75:
label_suffix = "huge"
- response = yield self._request(
+ response, responseBody = yield self._request(
CREATED,
'PUT',
self.server["uri"] + href.encode('utf-8'),
@@ -1862,9 +1883,14 @@
if not response.headers.hasHeader("etag"):
response = yield self.updateEvent(href)
+ # Add optional attachment
+ if attachmentSize:
+ yield self.postAttachment(href, 'x' * attachmentSize)
+
+
@inlineCallbacks
- def addInvite(self, href, component):
+ def addInvite(self, href, component, attachmentSize=0):
"""
Add an event that is an invite - i.e., has attendees. We will do attendee lookups and freebusy
checks on each attendee to simulate what happens when an organizer creates a new invite.
@@ -1878,7 +1904,7 @@
yield self._attendeeAutoComplete(component, attendee)
# Now do a normal PUT
- yield self.addEvent(href, component, invite=True)
+ yield self.addEvent(href, component, invite=True, attachmentSize=attachmentSize)
@inlineCallbacks
@@ -1894,7 +1920,7 @@
headers.removeHeader('if-match')
# At last, upload the new event definition
- response = yield self._request(
+ response, responseBody = yield self._request(
(NO_CONTENT, PRECONDITION_FAILED,),
'PUT',
self.server["uri"] + href.encode('utf-8'),
@@ -1920,7 +1946,7 @@
@inlineCallbacks
def updateEvent(self, href):
- response = yield self._request(
+ response, responseBody = yield self._request(
OK,
'GET',
self.server["uri"] + href.encode('utf-8'),
@@ -1929,8 +1955,7 @@
headers = response.headers
etag = headers.getRawHeaders('etag')[0]
scheduleTag = headers.getRawHeaders('schedule-tag', [None])[0]
- body = yield readBody(response)
- self.eventChanged(href, etag, scheduleTag, body)
+ self.eventChanged(href, etag, scheduleTag, responseBody)
returnValue(response)
@@ -1988,7 +2013,7 @@
if len(users) > 75:
label_suffix = "huge"
- response = yield self._request(
+ response, responseBody = yield self._request(
OK, 'POST', outbox,
Headers({
'content-type': ['text/calendar'],
@@ -2006,8 +2031,7 @@
}),
method_label="POST{fb-%s}" % (label_suffix,),
)
- body = yield readBody(response)
- returnValue(body)
+ returnValue(responseBody)
@inlineCallbacks
@@ -2017,15 +2041,14 @@
headers = Headers({
'Content-Disposition': ['attachment; filename="{}"'.format(filename)]
})
- response = yield self._request(
+ response, responseBody = yield self._request(
CREATED,
'POST',
url,
headers=headers,
body=StringProducer(content),
- method_label="POST{attach}"
+ method_label="POST{attachment}"
)
- body = yield readBody(response)
# We don't want to download an attachment we uploaded, so look for the
# Cal-Managed-Id: and Location: headers and remember those
@@ -2034,7 +2057,7 @@
self._attachments[managedId] = location
yield self.updateEvent(href)
- returnValue(body)
+ returnValue(responseBody)
@inlineCallbacks
@@ -2043,7 +2066,10 @@
# If we've already downloaded this managedId, skip it.
if managedId not in self._attachments:
self._attachments[managedId] = href
- yield self._newOperation("download", self._get(href, 200))
+ yield self._newOperation(
+ "download",
+ self._get(href, (OK, FORBIDDEN), method_label="GET{attachment}")
+ )
@inlineCallbacks
@@ -2051,7 +2077,7 @@
headers = Headers({
'content-type': ['text/xml']
})
- response = yield self._request(
+ response, responseBody = yield self._request(
(OK, CREATED, MULTI_STATUS),
'POST',
self.server["uri"] + href,
@@ -2059,8 +2085,7 @@
body=StringProducer(content),
method_label=label
)
- body = yield readBody(response)
- returnValue(body)
+ returnValue(responseBody)
@@ -2496,7 +2521,7 @@
class RequestLogger(object):
- format = u"%(user)s request %(code)s%(success)s[%(duration)5.2f s] %(method)8s %(url)s"
+ format = u"%(user)s %(client)s request %(code)s%(success)s[%(duration)5.2f s] %(method)8s %(url)s"
success = u"\N{CHECK MARK}"
failure = u"\N{BALLOT X}"
@@ -2508,6 +2533,7 @@
url=urlunparse(('', '') + urlparse(event['url'])[2:]),
code=event['code'],
duration=event['duration'],
+ client=event['client_type'],
)
if event['success']:
@@ -2516,7 +2542,28 @@
formatArgs['success'] = self.failure
print((self.format % formatArgs).encode('utf-8'))
+ if not event['success']:
+ body = event['body']
+ if isinstance(body, StringProducer):
+ body = body._body
+ if body:
+ body = body[:5000]
+ responseBody = event['responseBody']
+ if responseBody:
+ responseBody = responseBody[:5000]
+
+ print("=" * 80)
+ print("INCORRECT ERROR CODE: {}".format(event['code']))
+ print("URL: {}".format(event['url']))
+ print("METHOD: {}".format(event['method']))
+ print("USER: {}".format(event['user']))
+ print("REQUEST BODY:\n{}".format(body))
+ print("RESPONSE BODY:\n{}".format(responseBody))
+ print("=" * 80)
+
+
+
def report(self, output):
pass
Modified: CalendarServer/trunk/contrib/performance/loadtest/population.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/population.py 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/population.py 2016-05-12 19:28:46 UTC (rev 15588)
@@ -516,7 +516,10 @@
# Get means for each type of method
means = {}
for method, results in self._perMethodTimes.items():
- means[method] = mean([duration for success, duration in results if success])
+ try:
+ means[method] = mean([duration for success, duration in results if success])
+ except ZeroDivisionError:
+ pass # Ignore the case where all samples were unsuccessful?
# Determine percentage differences with weighting
differences = []
Modified: CalendarServer/trunk/contrib/performance/loadtest/profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/profiles.py 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/profiles.py 2016-05-12 19:28:46 UTC (rev 15588)
@@ -24,6 +24,7 @@
import json
import random
import sys
+from urlparse import urljoin, urlparse
from uuid import uuid4
from caldavclientlibrary.protocol.caldav.definitions import caldavxml
@@ -260,6 +261,7 @@
120 * 60
]),
recurrenceDistribution=RecurrenceDistribution(False),
+ fileSizeDistribution=NormalDistribution(1024, 1),
):
self.enabled = enabled
self._sendInvitationDistribution = sendInvitationDistribution
@@ -269,6 +271,7 @@
self._eventStartDistribution = eventStartDistribution
self._eventDurationDistribution = eventDurationDistribution
self._recurrenceDistribution = recurrenceDistribution
+ self._fileSizeDistribution = fileSizeDistribution
def run(self):
@@ -333,7 +336,7 @@
return succeed(None)
# Find calendars which are eligible for invites
- calendars = self._calendarsOfType(caldavxml.calendar, "VEVENT")
+ calendars = self._calendarsOfType(caldavxml.calendar, "VEVENT", justOwned=True)
while calendars:
# Pick one at random from which to try to create an event
@@ -368,8 +371,9 @@
except CannotAddAttendee:
continue
+ attachmentSize = int(self._fileSizeDistribution.sample())
href = '%s%s.ics' % (calendar.url, uid)
- d = self._client.addInvite(href, vcalendar)
+ d = self._client.addInvite(href, vcalendar, attachmentSize=attachmentSize)
return self._newOperation("invite", d)
@@ -561,12 +565,17 @@
for attachment in attachments:
attachmentHref = attachment.value()
managedId = attachment.parameterValue('MANAGED-ID')
+ attachmentHref = self._normalizeHref(attachmentHref)
+
self._reactor.callLater(
0, self._client.getAttachment, attachmentHref, managedId
)
+ def _normalizeHref(self, href):
+ return urljoin(self._client._managed_attachments_server_url, urlparse(href).path)
+
class Eventer(ProfileBase):
"""
A Calendar user who creates new events.
@@ -600,12 +609,14 @@
120 * 60
]),
recurrenceDistribution=RecurrenceDistribution(False),
+ fileSizeDistribution=NormalDistribution(1024, 1),
):
self.enabled = enabled
self._interval = interval
self._eventStartDistribution = eventStartDistribution
self._eventDurationDistribution = eventDurationDistribution
self._recurrenceDistribution = recurrenceDistribution
+ self._fileSizeDistribution = fileSizeDistribution
def run(self):
@@ -642,8 +653,9 @@
if rrule is not None:
vevent.addProperty(Property(None, None, None, pycalendar=rrule))
+ attachmentSize = int(self._fileSizeDistribution.sample())
href = '%s%s.ics' % (calendar.url, uid)
- d = self._client.addEvent(href, vcalendar)
+ d = self._client.addEvent(href, vcalendar, attachmentSize=attachmentSize)
return self._newOperation("create", d)
Modified: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request 2016-05-12 19:28:46 UTC (rev 15588)
@@ -39,5 +39,6 @@
<B:supported-calendar-component-sets xmlns:B="urn:ietf:params:xml:ns:caldav"/>
<A:supported-report-set/>
<A:sync-token/>
+ <B:managed-attachments-server-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
</A:prop>
</A:propfind>
Modified: CalendarServer/trunk/contrib/performance/loadtest/test_ical.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_ical.py 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_ical.py 2016-05-12 19:28:46 UTC (rev 15588)
@@ -1358,7 +1358,7 @@
response = MemoryResponse(
('HTTP', '1', '1'), CREATED, "Created", Headers({"etag": ["foo"]}),
StringProducer(""))
- result.callback(response)
+ result.callback((response, ""))
finished.addCallback(requested)
return d
@@ -1399,7 +1399,7 @@
('HTTP', '1', '1'), MULTI_STATUS, "MultiStatus", Headers({}),
StringProducer("<?xml version='1.0' encoding='UTF-8'?><multistatus xmlns='DAV:' />"))
- returnValue(response)
+ returnValue((response, "<?xml version='1.0' encoding='UTF-8'?><multistatus xmlns='DAV:' />"))
@inlineCallbacks
def _testPost(*args, **kwargs):
@@ -1418,7 +1418,7 @@
('HTTP', '1', '1'), OK, "OK", Headers({}),
StringProducer(""))
- returnValue(response)
+ returnValue((response, ""))
def _testPost02(*args, **kwargs):
return _testPost(*args, attendee="ATTENDEE:mailto:user02 at example.com", **kwargs)
@@ -1445,7 +1445,7 @@
('HTTP', '1', '1'), CREATED, "Created", Headers({}),
StringProducer(""))
- returnValue(response)
+ returnValue((response, ""))
def _testGet(*args, **kwargs):
expectedResponseCode, method, url = args
@@ -1458,7 +1458,7 @@
('HTTP', '1', '1'), OK, "OK", Headers({"etag": ["foo"]}),
StringProducer(EVENT_INVITE))
- return succeed(response)
+ return succeed((response, EVENT_INVITE))
requests = [_testReport, _testPost02, _testReport, _testPost03, _testPut, _testGet]
@@ -1499,7 +1499,7 @@
response = MemoryResponse(
('HTTP', '1', '1'), NO_CONTENT, "No Content", None,
StringProducer(""))
- result.callback(response)
+ result.callback((response, ""))
return d
@@ -1935,9 +1935,13 @@
self.assertEqual((MULTI_STATUS, FORBIDDEN), expectedResponseCode)
result.callback(
- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)),
+ self._CALENDAR_PROPFIND_RESPONSE_BODY
+ )
+ )
result, req = requests.pop(0)
expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
@@ -1949,9 +1953,13 @@
del self.client._events["/something/anotherthing.ics"]
result.callback(
- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY)))
+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY)),
+ self._CALENDAR_REPORT_RESPONSE_BODY
+ )
+ )
# Verify that processing proceeded to the response after the one with a
# 404 status.
@@ -1978,9 +1986,13 @@
self.assertEqual((MULTI_STATUS, FORBIDDEN), expectedResponseCode)
result.callback(
- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)),
+ self._CALENDAR_PROPFIND_RESPONSE_BODY
+ )
+ )
result, req = requests.pop(0)
expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
@@ -1989,9 +2001,13 @@
self.assertEqual((MULTI_STATUS,), expectedResponseCode)
result.callback(
- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_1)))
+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_1)),
+ self._CALENDAR_REPORT_RESPONSE_BODY_1
+ )
+ )
self.assertTrue(self.client._events['/something/anotherthing.ics'].etag is not None)
self.assertTrue(self.client._events['/something/else.ics'].etag is None)
@@ -2003,9 +2019,13 @@
self.assertEqual((MULTI_STATUS,), expectedResponseCode)
result.callback(
- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_2)))
+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_2)),
+ self._CALENDAR_REPORT_RESPONSE_BODY_2
+ )
+ )
self.assertTrue(self.client._events['/something/anotherthing.ics'].etag is not None)
self.assertTrue(self.client._events['/something/else.ics'].etag is not None)
@@ -2077,7 +2097,7 @@
response = MemoryResponse(
('HTTP', '1', '1'), OK, "Ok", Headers({}),
StringProducer(""))
- result.callback(response)
+ result.callback((response, ""))
finished.addCallback(requested)
return d
Modified: CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py
===================================================================
--- CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py 2016-05-12 19:28:46 UTC (rev 15588)
@@ -31,7 +31,7 @@
from twistedcaldav.ical import Component, Property
-from contrib.performance.loadtest.profiles import Eventer, Inviter, Accepter, OperationLogger, AlarmAcknowledger
+from contrib.performance.loadtest.profiles import Eventer, Inviter, Accepter, OperationLogger, AlarmAcknowledger, AttachmentDownloader
from contrib.performance.loadtest.population import Populator, CalendarClientSimulator
from contrib.performance.loadtest.ical import IncorrectResponseCode, Calendar, Event, BaseClient
from contrib.performance.loadtest.sim import _DirectoryRecord
@@ -258,13 +258,13 @@
return path
- def addEvent(self, href, vevent):
+ def addEvent(self, href, vevent, attachmentSize=0):
self._events[href] = Event(self.serializePath, href, None, vevent)
return succeed(None)
- def addInvite(self, href, vevent):
- return self.addEvent(href, vevent)
+ def addInvite(self, href, vevent, attachmentSize=0):
+ return self.addEvent(href, vevent, attachmentSize=attachmentSize)
def deleteEvent(self, href,):
@@ -291,11 +291,14 @@
def changeEventAttendee(self, href, old, new):
if href in self.rescheduled:
- return fail(IncorrectResponseCode(
- NO_CONTENT,
- Response(
- ('HTTP', 1, 1), PRECONDITION_FAILED,
- 'Precondition Failed', None, None))
+ return fail(
+ IncorrectResponseCode(
+ NO_CONTENT,
+ Response(
+ ('HTTP', 1, 1), PRECONDITION_FAILED,
+ 'Precondition Failed', None, None),
+ None
+ ),
)
vevent = self._events[href].component
@@ -851,7 +854,7 @@
NO_CONTENT,
Response(
('HTTP', 1, 1), PRECONDITION_FAILED,
- 'Precondition Failed', None, None))
+ 'Precondition Failed', None, None), None)
)
accepter = Accepter(clock, self.sim, client, userNumber)
accepter.eventChanged(inboxEvent.url)
@@ -1034,7 +1037,25 @@
# XXX Vary the event period/interval and the uid
+class AttachmentDownloaderTests(TestCase):
+ """
+ Tests for L{AttachmentDownloader}.
+ """
+ def setUp(self):
+ self.sim = CalendarClientSimulator(
+ AnyUser(), Populator(None), None, None, None, None, None, None)
+
+ def test_normalize(self):
+ client = StubClient(1, self.mktemp())
+ client._managed_attachments_server_url = "https://thisserver:8443"
+ downloader = AttachmentDownloader(Clock(), self.sim, client, None)
+ self.assertEquals(
+ downloader._normalizeHref("http://otherserver/attachment/url"),
+ "https://thisserver:8443/attachment/url"
+ )
+
+
class OperationLoggerTests(TestCase):
"""
Tests for L{OperationLogger}.
Modified: CalendarServer/trunk/requirements-dev.txt
===================================================================
--- CalendarServer/trunk/requirements-dev.txt 2016-05-11 16:03:38 UTC (rev 15587)
+++ CalendarServer/trunk/requirements-dev.txt 2016-05-12 19:28:46 UTC (rev 15588)
@@ -4,5 +4,5 @@
mockldap
q
tl.eggdeps
---editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVClientLibrary/trunk@15425#egg=CalDAVClientLibrary
+--editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVClientLibrary/trunk@15578#egg=CalDAVClientLibrary
--editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVTester/trunk@15551#egg=CalDAVTester
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20160512/19f355bb/attachment-0001.html>
More information about the calendarserver-changes
mailing list