<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[15588] CalendarServer/trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/15588">15588</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2016-05-12 12:28:46 -0700 (Thu, 12 May 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>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.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkcontribperformanceloadtestclientsplist">CalendarServer/trunk/contrib/performance/loadtest/clients.plist</a></li>
<li><a href="#CalendarServertrunkcontribperformanceloadtestconfigplist">CalendarServer/trunk/contrib/performance/loadtest/config.plist</a></li>
<li><a href="#CalendarServertrunkcontribperformanceloadtesticalpy">CalendarServer/trunk/contrib/performance/loadtest/ical.py</a></li>
<li><a href="#CalendarServertrunkcontribperformanceloadtestpopulationpy">CalendarServer/trunk/contrib/performance/loadtest/population.py</a></li>
<li><a href="#CalendarServertrunkcontribperformanceloadtestprofilespy">CalendarServer/trunk/contrib/performance/loadtest/profiles.py</a></li>
<li><a href="#CalendarServertrunkcontribperformanceloadtestrequestdataOS_X_10_11poll_calendarhome_depth1_propfindrequest">CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request</a></li>
<li><a href="#CalendarServertrunkcontribperformanceloadtesttest_icalpy">CalendarServer/trunk/contrib/performance/loadtest/test_ical.py</a></li>
<li><a href="#CalendarServertrunkcontribperformanceloadtesttest_profilespy">CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py</a></li>
<li><a href="#CalendarServertrunkrequirementsdevtxt">CalendarServer/trunk/requirements-dev.txt</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkcontribperformanceloadtestclientsplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/clients.plist (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -38,7 +38,7 @@
</span><span class="cx">                                 <dict>
</span><span class="cx">                                         <!-- Name that appears in logs. -->
</span><span class="cx">                                         <key>title</key>
</span><del>-                                        <string>10.11</string>
</del><ins>+                                        <string>10.11a</string>
</ins><span class="cx">
</span><span class="cx">                                         <!-- OS_X_10_11 can poll the calendar home at some interval. This is
</span><span class="cx">                                                 in seconds. -->
</span><span class="lines">@@ -163,6 +163,24 @@
</span><span class="cx">                                                                         </dict>
</span><span class="cx">                                                                 </dict>
</span><span class="cx">                                                         </dict>
</span><ins>+
+                                                        <!-- 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>
+
</ins><span class="cx">                                                 </dict>
</span><span class="cx">                                         </dict>
</span><span class="cx">
</span><span class="lines">@@ -343,7 +361,7 @@
</span><span class="cx">                                                 <key>params</key>
</span><span class="cx">                                                 <dict>
</span><span class="cx">                                                         <key>enabled</key>
</span><del>-                                                        <true/>
</del><ins>+                                                        <false/>
</ins><span class="cx">
</span><span class="cx">                                                         <!-- Define the interval (in seconds) at which this profile will use
</span><span class="cx">                                                                 its client to create a new event. -->
</span><span class="lines">@@ -578,6 +596,24 @@
</span><span class="cx">                                                                         </dict>
</span><span class="cx">                                                                 </dict>
</span><span class="cx">                                                         </dict>
</span><ins>+
+                                                        <!-- 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>
+
</ins><span class="cx">                                                 </dict>
</span><span class="cx">                                         </dict>
</span><span class="cx">
</span><span class="lines">@@ -712,6 +748,58 @@
</span><span class="cx">                                 <key>weight</key>
</span><span class="cx">                                 <integer>1</integer>
</span><span class="cx">                         </dict>
</span><ins>+
+                        <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>
</ins><span class="cx">                 </array>
</span><span class="cx">         </dict>
</span><span class="cx"> </plist>
</span></span></pre></div>
<a id="CalendarServertrunkcontribperformanceloadtestconfigplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/config.plist (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -157,7 +157,7 @@
</span><span class="cx">                                 <!-- Number of clients each user is assigned to. -->
</span><span class="cx">                                 <!-- Set weight of clients to 1 if this is > 1. Number of clients must match this value if > 1. -->
</span><span class="cx">                                 <key>clientsPerUser</key>
</span><del>-                                <integer>1</integer>
</del><ins>+                                <integer>2</integer>
</ins><span class="cx">                         </dict>
</span><span class="cx">
</span><span class="cx">                 </dict>
</span><span class="lines">@@ -168,7 +168,8 @@
</span><span class="cx">                         <!-- ReportStatistics generates an end-of-run summary of the HTTP requests
</span><span class="cx">                                 made, their timings, and their results. -->
</span><span class="cx">                         <dict>
</span><del>-                                <key>type</key>
</del><ins>+                        <key>type</key>
+
</ins><span class="cx">                                 <string>contrib.performance.loadtest.population.ReportStatistics</string>
</span><span class="cx">                                 <key>params</key>
</span><span class="cx">                                 <dict>
</span></span></pre></div>
<a id="CalendarServertrunkcontribperformanceloadtesticalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/ical.py (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -85,10 +85,14 @@
</span><span class="cx">
</span><span class="cx"> @ivar response: The response which was received
</span><span class="cx"> @type response: L{twisted.web.client.Response}
</span><ins>+
+ @ivar responseBody: The body of the received response
+ @type responseBody: C{str}
</ins><span class="cx"> """
</span><del>- def __init__(self, expected, response):
</del><ins>+ def __init__(self, expected, response, responseBody):
</ins><span class="cx"> self.expected = expected
</span><span class="cx"> self.response = response
</span><ins>+ self.responseBody = responseBody
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -447,6 +451,8 @@
</span><span class="cx">
</span><span class="cx"> _client_type = "Generic"
</span><span class="cx">
</span><ins>+ _managed_attachments_server_url = None
+
</ins><span class="cx"> USER_AGENT = None # Override this for specific clients
</span><span class="cx">
</span><span class="cx"> # The default interval, used if none is specified in external
</span><span class="lines">@@ -618,9 +624,8 @@
</span><span class="cx"> before = self.reactor.seconds()
</span><span class="cx"> response = yield self.agent.request(method, url, headers, body)
</span><span class="cx">
</span><del>- # XXX This is time to receive response headers, not time
- # to receive full response. Should measure the latter, if
- # not both.
</del><ins>+ responseBody = yield readBody(response)
+
</ins><span class="cx"> after = self.reactor.seconds()
</span><span class="cx">
</span><span class="cx"> success = response.code in expectedResponseCodes
</span><span class="lines">@@ -631,6 +636,7 @@
</span><span class="cx"> method=method_label if method_label else method,
</span><span class="cx"> headers=headers,
</span><span class="cx"> body=body,
</span><ins>+ responseBody=responseBody,
</ins><span class="cx"> code=response.code,
</span><span class="cx"> user=self.record.uid,
</span><span class="cx"> client_type=self.title,
</span><span class="lines">@@ -640,22 +646,30 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> if success:
</span><del>- returnValue(response)
</del><ins>+ returnValue((response, responseBody))
</ins><span class="cx">
</span><del>- raise IncorrectResponseCode(expectedResponseCodes, response)
</del><ins>+ raise IncorrectResponseCode(expectedResponseCodes, response, responseBody)
</ins><span class="cx">
</span><span class="cx">
</span><del>- def _parseMultiStatus(self, response, otherTokens=False):
</del><ins>+ def _parseMultiStatus(self, responseBody, otherTokens=False):
</ins><span class="cx"> """
</span><span class="cx"> Parse a <multistatus> - might need to return other top-level elements
</span><span class="cx"> in the response - e.g. DAV:sync-token
</span><span class="cx"> I{PROPFIND} request for the principal URL.
</span><span class="cx">
</span><del>- @type response: C{str}
</del><ins>+ @type responseBody: C{str}
</ins><span class="cx"> @rtype: C{cls}
</span><span class="cx"> """
</span><span class="cx"> parser = PropFindParser()
</span><del>- parser.parseData(response)
</del><ins>+ try:
+ parser.parseData(responseBody)
+ except:
+ print("=" * 80)
+ print("COULD NOT PARSE RESPONSE:")
+ print(responseBody)
+ print("=" * 80)
+ raise
+
</ins><span class="cx"> if otherTokens:
</span><span class="cx"> return (parser.getResults(), parser.getOthers(),)
</span><span class="cx"> else:
</span><span class="lines">@@ -674,7 +688,7 @@
</span><span class="cx"> hdrs = Headers({'content-type': ['text/xml']})
</span><span class="cx"> if depth is not None:
</span><span class="cx"> hdrs.addRawHeader('depth', depth)
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> allowedStatus,
</span><span class="cx"> 'PROPFIND',
</span><span class="cx"> self.server["uri"] + url.encode('utf-8'),
</span><span class="lines">@@ -683,8 +697,7 @@
</span><span class="cx"> method_label=method_label,
</span><span class="cx"> )
</span><span class="cx">
</span><del>- body = yield readBody(response)
- result = self._parseMultiStatus(body) if response.code == MULTI_STATUS else None
</del><ins>+ result = self._parseMultiStatus(responseBody) if response.code == MULTI_STATUS else None
</ins><span class="cx">
</span><span class="cx"> returnValue((response, result,))
</span><span class="cx">
</span><span class="lines">@@ -695,7 +708,7 @@
</span><span class="cx"> Issue a PROPPATCH on the chosen URL
</span><span class="cx"> """
</span><span class="cx"> hdrs = Headers({'content-type': ['text/xml']})
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> (OK, MULTI_STATUS,),
</span><span class="cx"> 'PROPPATCH',
</span><span class="cx"> self.server["uri"] + url.encode('utf-8'),
</span><span class="lines">@@ -704,8 +717,7 @@
</span><span class="cx"> method_label=method_label,
</span><span class="cx"> )
</span><span class="cx"> if response.code == MULTI_STATUS:
</span><del>- body = yield readBody(response)
- result = self._parseMultiStatus(body)
</del><ins>+ result = self._parseMultiStatus(responseBody)
</ins><span class="cx"> returnValue(result)
</span><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span><span class="lines">@@ -716,15 +728,14 @@
</span><span class="cx"> """
</span><span class="cx"> Issue a GET on the chosen URL
</span><span class="cx"> """
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> allowedStatus,
</span><span class="cx"> 'GET',
</span><span class="cx"> url,
</span><span class="cx"> method_label=method_label,
</span><span class="cx"> )
</span><span class="cx">
</span><del>- body = yield readBody(response)
- returnValue(body)
</del><ins>+ returnValue(responseBody)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -735,7 +746,7 @@
</span><span class="cx"> hdrs = Headers({'content-type': ['text/xml']})
</span><span class="cx"> if depth is not None:
</span><span class="cx"> hdrs.addRawHeader('depth', depth)
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> allowedStatus,
</span><span class="cx"> 'REPORT',
</span><span class="cx"> self.server["uri"] + url.encode('utf-8'),
</span><span class="lines">@@ -744,8 +755,7 @@
</span><span class="cx"> method_label=method_label,
</span><span class="cx"> )
</span><span class="cx">
</span><del>- body = yield readBody(response)
- result = self._parseMultiStatus(body, otherTokens) if response.code == MULTI_STATUS else None
</del><ins>+ result = self._parseMultiStatus(responseBody, otherTokens) if response.code == MULTI_STATUS else None
</ins><span class="cx">
</span><span class="cx"> returnValue(result)
</span><span class="cx">
</span><span class="lines">@@ -890,8 +900,20 @@
</span><span class="cx">
</span><span class="cx"> if href == calendarHome:
</span><span class="cx"> text = results[href].getTextProperties()
</span><ins>+ hrefs = results[href].getHrefProperties()
</ins><span class="cx">
</span><ins>+ # Extract managed attachments url
+ self._managed_attachments_server_url = None
</ins><span class="cx"> try:
</span><ins>+ 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:
</ins><span class="cx"> pushkey = text[csxml.pushkey]
</span><span class="cx"> except KeyError:
</span><span class="cx"> pass
</span><span class="lines">@@ -929,10 +951,9 @@
</span><span class="cx"> if isCalendar:
</span><span class="cx"> textProps = results[href].getTextProperties()
</span><span class="cx"> componentTypes = set()
</span><del>- 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())
</del><ins>+ if caldavxml.supported_calendar_component_set in nodes:
+ for comp in nodes[caldavxml.supported_calendar_component_set]:
+ componentTypes.add(comp.get("name").upper())
</ins><span class="cx">
</span><span class="cx"> calendars.append(Calendar(
</span><span class="cx"> resourceType,
</span><span class="lines">@@ -1022,7 +1043,7 @@
</span><span class="cx"> method_label="REPORT{sync}" if calendar.changeToken else "REPORT{sync-init}",
</span><span class="cx"> )
</span><span class="cx"> else:
</span><del>- raise IncorrectResponseCode((MULTI_STATUS,), None)
</del><ins>+ raise IncorrectResponseCode((MULTI_STATUS,), None, None)
</ins><span class="cx">
</span><span class="cx"> result, others = result
</span><span class="cx">
</span><span class="lines">@@ -1208,7 +1229,7 @@
</span><span class="cx"> method_label="REPORT{sync}" if oldToken else "REPORT{sync-init}",
</span><span class="cx"> )
</span><span class="cx"> else:
</span><del>- raise IncorrectResponseCode((MULTI_STATUS,), None)
</del><ins>+ raise IncorrectResponseCode((MULTI_STATUS,), None, None)
</ins><span class="cx">
</span><span class="cx"> result, _ignore_others = result
</span><span class="cx">
</span><span class="lines">@@ -1229,24 +1250,24 @@
</span><span class="cx">
</span><span class="cx"> if result[responseHref].getStatus() / 100 == 2:
</span><span class="cx"> # Get the notification
</span><del>- response = yield self._request(
- OK,
</del><ins>+ response, responseBody = yield self._request(
+ (OK, NOT_FOUND),
</ins><span class="cx"> 'GET',
</span><span class="cx"> self.server["uri"] + responseHref.encode('utf-8'),
</span><span class="cx"> method_label="GET{notification}",
</span><span class="cx"> )
</span><del>- 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
</del><ins>+ 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
+ )
</ins><span class="cx"> )
</span><del>- )
</del><span class="cx">
</span><span class="cx"> # Accept the invites
</span><span class="cx"> for notification in inviteNotifications:
</span><span class="lines">@@ -1287,7 +1308,7 @@
</span><span class="cx">
</span><span class="cx"> # Delete all the notification resources
</span><span class="cx"> for responseHref in toDelete:
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> (NO_CONTENT, NOT_FOUND),
</span><span class="cx"> 'DELETE',
</span><span class="cx"> self.server["uri"] + responseHref.encode('utf-8'),
</span><span class="lines">@@ -1497,8 +1518,8 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def _receivedPush(self, inboundID, dataChangedTimestamp, priority=5):
</span><del>- for href, id in self.ampPushKeys.iteritems():
- if inboundID == id:
</del><ins>+ for href, myId in self.ampPushKeys.iteritems():
+ if inboundID == myId:
</ins><span class="cx"> self._checkCalendarsForEvents(href, push=True)
</span><span class="cx"> break
</span><span class="cx"> else:
</span><span class="lines">@@ -1708,7 +1729,7 @@
</span><span class="cx"> headers.removeHeader('if-match')
</span><span class="cx">
</span><span class="cx"> # At last, upload the new event definition
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> (NO_CONTENT, PRECONDITION_FAILED,),
</span><span class="cx"> 'PUT',
</span><span class="cx"> self.server["uri"] + href.encode('utf-8'),
</span><span class="lines">@@ -1803,7 +1824,7 @@
</span><span class="cx"> if len(attendees) > 75:
</span><span class="cx"> label_suffix = "huge"
</span><span class="cx">
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> okCodes,
</span><span class="cx"> 'PUT',
</span><span class="cx"> self.server["uri"] + href.encode('utf-8'),
</span><span class="lines">@@ -1825,7 +1846,7 @@
</span><span class="cx">
</span><span class="cx"> self._removeEvent(href)
</span><span class="cx">
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> (NO_CONTENT, NOT_FOUND),
</span><span class="cx"> 'DELETE',
</span><span class="cx"> self.server["uri"] + href.encode('utf-8'),
</span><span class="lines">@@ -1835,7 +1856,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><del>- def addEvent(self, href, component, invite=False):
</del><ins>+ def addEvent(self, href, component, invite=False, attachmentSize=0):
</ins><span class="cx"> headers = Headers({
</span><span class="cx"> 'content-type': ['text/calendar'],
</span><span class="cx"> })
</span><span class="lines">@@ -1849,7 +1870,7 @@
</span><span class="cx"> if len(attendees) > 75:
</span><span class="cx"> label_suffix = "huge"
</span><span class="cx">
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> CREATED,
</span><span class="cx"> 'PUT',
</span><span class="cx"> self.server["uri"] + href.encode('utf-8'),
</span><span class="lines">@@ -1862,9 +1883,14 @@
</span><span class="cx"> if not response.headers.hasHeader("etag"):
</span><span class="cx"> response = yield self.updateEvent(href)
</span><span class="cx">
</span><ins>+ # Add optional attachment
+ if attachmentSize:
+ yield self.postAttachment(href, 'x' * attachmentSize)
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> @inlineCallbacks
</span><del>- def addInvite(self, href, component):
</del><ins>+ def addInvite(self, href, component, attachmentSize=0):
</ins><span class="cx"> """
</span><span class="cx"> Add an event that is an invite - i.e., has attendees. We will do attendee lookups and freebusy
</span><span class="cx"> checks on each attendee to simulate what happens when an organizer creates a new invite.
</span><span class="lines">@@ -1878,7 +1904,7 @@
</span><span class="cx"> yield self._attendeeAutoComplete(component, attendee)
</span><span class="cx">
</span><span class="cx"> # Now do a normal PUT
</span><del>- yield self.addEvent(href, component, invite=True)
</del><ins>+ yield self.addEvent(href, component, invite=True, attachmentSize=attachmentSize)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -1894,7 +1920,7 @@
</span><span class="cx"> headers.removeHeader('if-match')
</span><span class="cx">
</span><span class="cx"> # At last, upload the new event definition
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> (NO_CONTENT, PRECONDITION_FAILED,),
</span><span class="cx"> 'PUT',
</span><span class="cx"> self.server["uri"] + href.encode('utf-8'),
</span><span class="lines">@@ -1920,7 +1946,7 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def updateEvent(self, href):
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> OK,
</span><span class="cx"> 'GET',
</span><span class="cx"> self.server["uri"] + href.encode('utf-8'),
</span><span class="lines">@@ -1929,8 +1955,7 @@
</span><span class="cx"> headers = response.headers
</span><span class="cx"> etag = headers.getRawHeaders('etag')[0]
</span><span class="cx"> scheduleTag = headers.getRawHeaders('schedule-tag', [None])[0]
</span><del>- body = yield readBody(response)
- self.eventChanged(href, etag, scheduleTag, body)
</del><ins>+ self.eventChanged(href, etag, scheduleTag, responseBody)
</ins><span class="cx"> returnValue(response)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1988,7 +2013,7 @@
</span><span class="cx"> if len(users) > 75:
</span><span class="cx"> label_suffix = "huge"
</span><span class="cx">
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> OK, 'POST', outbox,
</span><span class="cx"> Headers({
</span><span class="cx"> 'content-type': ['text/calendar'],
</span><span class="lines">@@ -2006,8 +2031,7 @@
</span><span class="cx"> }),
</span><span class="cx"> method_label="POST{fb-%s}" % (label_suffix,),
</span><span class="cx"> )
</span><del>- body = yield readBody(response)
- returnValue(body)
</del><ins>+ returnValue(responseBody)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -2017,15 +2041,14 @@
</span><span class="cx"> headers = Headers({
</span><span class="cx"> 'Content-Disposition': ['attachment; filename="{}"'.format(filename)]
</span><span class="cx"> })
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> CREATED,
</span><span class="cx"> 'POST',
</span><span class="cx"> url,
</span><span class="cx"> headers=headers,
</span><span class="cx"> body=StringProducer(content),
</span><del>- method_label="POST{attach}"
</del><ins>+ method_label="POST{attachment}"
</ins><span class="cx"> )
</span><del>- body = yield readBody(response)
</del><span class="cx">
</span><span class="cx"> # We don't want to download an attachment we uploaded, so look for the
</span><span class="cx"> # Cal-Managed-Id: and Location: headers and remember those
</span><span class="lines">@@ -2034,7 +2057,7 @@
</span><span class="cx"> self._attachments[managedId] = location
</span><span class="cx">
</span><span class="cx"> yield self.updateEvent(href)
</span><del>- returnValue(body)
</del><ins>+ returnValue(responseBody)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -2043,7 +2066,10 @@
</span><span class="cx"> # If we've already downloaded this managedId, skip it.
</span><span class="cx"> if managedId not in self._attachments:
</span><span class="cx"> self._attachments[managedId] = href
</span><del>- yield self._newOperation("download", self._get(href, 200))
</del><ins>+ yield self._newOperation(
+ "download",
+ self._get(href, (OK, FORBIDDEN), method_label="GET{attachment}")
+ )
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -2051,7 +2077,7 @@
</span><span class="cx"> headers = Headers({
</span><span class="cx"> 'content-type': ['text/xml']
</span><span class="cx"> })
</span><del>- response = yield self._request(
</del><ins>+ response, responseBody = yield self._request(
</ins><span class="cx"> (OK, CREATED, MULTI_STATUS),
</span><span class="cx"> 'POST',
</span><span class="cx"> self.server["uri"] + href,
</span><span class="lines">@@ -2059,8 +2085,7 @@
</span><span class="cx"> body=StringProducer(content),
</span><span class="cx"> method_label=label
</span><span class="cx"> )
</span><del>- body = yield readBody(response)
- returnValue(body)
</del><ins>+ returnValue(responseBody)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2496,7 +2521,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class RequestLogger(object):
</span><del>- format = u"%(user)s request %(code)s%(success)s[%(duration)5.2f s] %(method)8s %(url)s"
</del><ins>+ format = u"%(user)s %(client)s request %(code)s%(success)s[%(duration)5.2f s] %(method)8s %(url)s"
</ins><span class="cx"> success = u"\N{CHECK MARK}"
</span><span class="cx"> failure = u"\N{BALLOT X}"
</span><span class="cx">
</span><span class="lines">@@ -2508,6 +2533,7 @@
</span><span class="cx"> url=urlunparse(('', '') + urlparse(event['url'])[2:]),
</span><span class="cx"> code=event['code'],
</span><span class="cx"> duration=event['duration'],
</span><ins>+ client=event['client_type'],
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> if event['success']:
</span><span class="lines">@@ -2516,7 +2542,28 @@
</span><span class="cx"> formatArgs['success'] = self.failure
</span><span class="cx"> print((self.format % formatArgs).encode('utf-8'))
</span><span class="cx">
</span><ins>+ if not event['success']:
+ body = event['body']
+ if isinstance(body, StringProducer):
+ body = body._body
+ if body:
+ body = body[:5000]
</ins><span class="cx">
</span><ins>+ 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)
+
+
+
</ins><span class="cx"> def report(self, output):
</span><span class="cx"> pass
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunkcontribperformanceloadtestpopulationpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/population.py (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -516,7 +516,10 @@
</span><span class="cx"> # Get means for each type of method
</span><span class="cx"> means = {}
</span><span class="cx"> for method, results in self._perMethodTimes.items():
</span><del>- means[method] = mean([duration for success, duration in results if success])
</del><ins>+ try:
+ means[method] = mean([duration for success, duration in results if success])
+ except ZeroDivisionError:
+ pass # Ignore the case where all samples were unsuccessful?
</ins><span class="cx">
</span><span class="cx"> # Determine percentage differences with weighting
</span><span class="cx"> differences = []
</span></span></pre></div>
<a id="CalendarServertrunkcontribperformanceloadtestprofilespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/profiles.py (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -24,6 +24,7 @@
</span><span class="cx"> import json
</span><span class="cx"> import random
</span><span class="cx"> import sys
</span><ins>+from urlparse import urljoin, urlparse
</ins><span class="cx"> from uuid import uuid4
</span><span class="cx">
</span><span class="cx"> from caldavclientlibrary.protocol.caldav.definitions import caldavxml
</span><span class="lines">@@ -260,6 +261,7 @@
</span><span class="cx"> 120 * 60
</span><span class="cx"> ]),
</span><span class="cx"> recurrenceDistribution=RecurrenceDistribution(False),
</span><ins>+ fileSizeDistribution=NormalDistribution(1024, 1),
</ins><span class="cx"> ):
</span><span class="cx"> self.enabled = enabled
</span><span class="cx"> self._sendInvitationDistribution = sendInvitationDistribution
</span><span class="lines">@@ -269,6 +271,7 @@
</span><span class="cx"> self._eventStartDistribution = eventStartDistribution
</span><span class="cx"> self._eventDurationDistribution = eventDurationDistribution
</span><span class="cx"> self._recurrenceDistribution = recurrenceDistribution
</span><ins>+ self._fileSizeDistribution = fileSizeDistribution
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def run(self):
</span><span class="lines">@@ -333,7 +336,7 @@
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><span class="cx"> # Find calendars which are eligible for invites
</span><del>- calendars = self._calendarsOfType(caldavxml.calendar, "VEVENT")
</del><ins>+ calendars = self._calendarsOfType(caldavxml.calendar, "VEVENT", justOwned=True)
</ins><span class="cx">
</span><span class="cx"> while calendars:
</span><span class="cx"> # Pick one at random from which to try to create an event
</span><span class="lines">@@ -368,8 +371,9 @@
</span><span class="cx"> except CannotAddAttendee:
</span><span class="cx"> continue
</span><span class="cx">
</span><ins>+ attachmentSize = int(self._fileSizeDistribution.sample())
</ins><span class="cx"> href = '%s%s.ics' % (calendar.url, uid)
</span><del>- d = self._client.addInvite(href, vcalendar)
</del><ins>+ d = self._client.addInvite(href, vcalendar, attachmentSize=attachmentSize)
</ins><span class="cx"> return self._newOperation("invite", d)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -561,12 +565,17 @@
</span><span class="cx"> for attachment in attachments:
</span><span class="cx"> attachmentHref = attachment.value()
</span><span class="cx"> managedId = attachment.parameterValue('MANAGED-ID')
</span><ins>+ attachmentHref = self._normalizeHref(attachmentHref)
+
</ins><span class="cx"> self._reactor.callLater(
</span><span class="cx"> 0, self._client.getAttachment, attachmentHref, managedId
</span><span class="cx"> )
</span><span class="cx">
</span><ins>+ def _normalizeHref(self, href):
+ return urljoin(self._client._managed_attachments_server_url, urlparse(href).path)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+
</ins><span class="cx"> class Eventer(ProfileBase):
</span><span class="cx"> """
</span><span class="cx"> A Calendar user who creates new events.
</span><span class="lines">@@ -600,12 +609,14 @@
</span><span class="cx"> 120 * 60
</span><span class="cx"> ]),
</span><span class="cx"> recurrenceDistribution=RecurrenceDistribution(False),
</span><ins>+ fileSizeDistribution=NormalDistribution(1024, 1),
</ins><span class="cx"> ):
</span><span class="cx"> self.enabled = enabled
</span><span class="cx"> self._interval = interval
</span><span class="cx"> self._eventStartDistribution = eventStartDistribution
</span><span class="cx"> self._eventDurationDistribution = eventDurationDistribution
</span><span class="cx"> self._recurrenceDistribution = recurrenceDistribution
</span><ins>+ self._fileSizeDistribution = fileSizeDistribution
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def run(self):
</span><span class="lines">@@ -642,8 +653,9 @@
</span><span class="cx"> if rrule is not None:
</span><span class="cx"> vevent.addProperty(Property(None, None, None, pycalendar=rrule))
</span><span class="cx">
</span><ins>+ attachmentSize = int(self._fileSizeDistribution.sample())
</ins><span class="cx"> href = '%s%s.ics' % (calendar.url, uid)
</span><del>- d = self._client.addEvent(href, vcalendar)
</del><ins>+ d = self._client.addEvent(href, vcalendar, attachmentSize=attachmentSize)
</ins><span class="cx"> return self._newOperation("create", d)
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunkcontribperformanceloadtestrequestdataOS_X_10_11poll_calendarhome_depth1_propfindrequest"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/request-data/OS_X_10_11/poll_calendarhome_depth1_propfind.request (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -39,5 +39,6 @@
</span><span class="cx"> <B:supported-calendar-component-sets xmlns:B="urn:ietf:params:xml:ns:caldav"/>
</span><span class="cx"> <A:supported-report-set/>
</span><span class="cx"> <A:sync-token/>
</span><ins>+ <B:managed-attachments-server-URL xmlns:B="urn:ietf:params:xml:ns:caldav"/>
</ins><span class="cx"> </A:prop>
</span><span class="cx"> </A:propfind>
</span></span></pre></div>
<a id="CalendarServertrunkcontribperformanceloadtesttest_icalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/test_ical.py (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1358,7 +1358,7 @@
</span><span class="cx"> response = MemoryResponse(
</span><span class="cx"> ('HTTP', '1', '1'), CREATED, "Created", Headers({"etag": ["foo"]}),
</span><span class="cx"> StringProducer(""))
</span><del>- result.callback(response)
</del><ins>+ result.callback((response, ""))
</ins><span class="cx"> finished.addCallback(requested)
</span><span class="cx">
</span><span class="cx"> return d
</span><span class="lines">@@ -1399,7 +1399,7 @@
</span><span class="cx"> ('HTTP', '1', '1'), MULTI_STATUS, "MultiStatus", Headers({}),
</span><span class="cx"> StringProducer("<?xml version='1.0' encoding='UTF-8'?><multistatus xmlns='DAV:' />"))
</span><span class="cx">
</span><del>- returnValue(response)
</del><ins>+ returnValue((response, "<?xml version='1.0' encoding='UTF-8'?><multistatus xmlns='DAV:' />"))
</ins><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _testPost(*args, **kwargs):
</span><span class="lines">@@ -1418,7 +1418,7 @@
</span><span class="cx"> ('HTTP', '1', '1'), OK, "OK", Headers({}),
</span><span class="cx"> StringProducer(""))
</span><span class="cx">
</span><del>- returnValue(response)
</del><ins>+ returnValue((response, ""))
</ins><span class="cx">
</span><span class="cx"> def _testPost02(*args, **kwargs):
</span><span class="cx"> return _testPost(*args, attendee="ATTENDEE:mailto:user02@example.com", **kwargs)
</span><span class="lines">@@ -1445,7 +1445,7 @@
</span><span class="cx"> ('HTTP', '1', '1'), CREATED, "Created", Headers({}),
</span><span class="cx"> StringProducer(""))
</span><span class="cx">
</span><del>- returnValue(response)
</del><ins>+ returnValue((response, ""))
</ins><span class="cx">
</span><span class="cx"> def _testGet(*args, **kwargs):
</span><span class="cx"> expectedResponseCode, method, url = args
</span><span class="lines">@@ -1458,7 +1458,7 @@
</span><span class="cx"> ('HTTP', '1', '1'), OK, "OK", Headers({"etag": ["foo"]}),
</span><span class="cx"> StringProducer(EVENT_INVITE))
</span><span class="cx">
</span><del>- return succeed(response)
</del><ins>+ return succeed((response, EVENT_INVITE))
</ins><span class="cx">
</span><span class="cx"> requests = [_testReport, _testPost02, _testReport, _testPost03, _testPut, _testGet]
</span><span class="cx">
</span><span class="lines">@@ -1499,7 +1499,7 @@
</span><span class="cx"> response = MemoryResponse(
</span><span class="cx"> ('HTTP', '1', '1'), NO_CONTENT, "No Content", None,
</span><span class="cx"> StringProducer(""))
</span><del>- result.callback(response)
</del><ins>+ result.callback((response, ""))
</ins><span class="cx"> return d
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1935,9 +1935,13 @@
</span><span class="cx"> self.assertEqual((MULTI_STATUS, FORBIDDEN), expectedResponseCode)
</span><span class="cx">
</span><span class="cx"> result.callback(
</span><del>- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
</del><ins>+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)),
+ self._CALENDAR_PROPFIND_RESPONSE_BODY
+ )
+ )
</ins><span class="cx">
</span><span class="cx"> result, req = requests.pop(0)
</span><span class="cx"> expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
</span><span class="lines">@@ -1949,9 +1953,13 @@
</span><span class="cx"> del self.client._events["/something/anotherthing.ics"]
</span><span class="cx">
</span><span class="cx"> result.callback(
</span><del>- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY)))
</del><ins>+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY)),
+ self._CALENDAR_REPORT_RESPONSE_BODY
+ )
+ )
</ins><span class="cx">
</span><span class="cx"> # Verify that processing proceeded to the response after the one with a
</span><span class="cx"> # 404 status.
</span><span class="lines">@@ -1978,9 +1986,13 @@
</span><span class="cx"> self.assertEqual((MULTI_STATUS, FORBIDDEN), expectedResponseCode)
</span><span class="cx">
</span><span class="cx"> result.callback(
</span><del>- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)))
</del><ins>+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_PROPFIND_RESPONSE_BODY)),
+ self._CALENDAR_PROPFIND_RESPONSE_BODY
+ )
+ )
</ins><span class="cx">
</span><span class="cx"> result, req = requests.pop(0)
</span><span class="cx"> expectedResponseCode, method, url, _ignore_headers, _ignore_body = req
</span><span class="lines">@@ -1989,9 +2001,13 @@
</span><span class="cx"> self.assertEqual((MULTI_STATUS,), expectedResponseCode)
</span><span class="cx">
</span><span class="cx"> result.callback(
</span><del>- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_1)))
</del><ins>+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_1)),
+ self._CALENDAR_REPORT_RESPONSE_BODY_1
+ )
+ )
</ins><span class="cx">
</span><span class="cx"> self.assertTrue(self.client._events['/something/anotherthing.ics'].etag is not None)
</span><span class="cx"> self.assertTrue(self.client._events['/something/else.ics'].etag is None)
</span><span class="lines">@@ -2003,9 +2019,13 @@
</span><span class="cx"> self.assertEqual((MULTI_STATUS,), expectedResponseCode)
</span><span class="cx">
</span><span class="cx"> result.callback(
</span><del>- MemoryResponse(
- ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
- StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_2)))
</del><ins>+ (
+ MemoryResponse(
+ ('HTTP', '1', '1'), MULTI_STATUS, "Multi-status", None,
+ StringProducer(self._CALENDAR_REPORT_RESPONSE_BODY_2)),
+ self._CALENDAR_REPORT_RESPONSE_BODY_2
+ )
+ )
</ins><span class="cx">
</span><span class="cx"> self.assertTrue(self.client._events['/something/anotherthing.ics'].etag is not None)
</span><span class="cx"> self.assertTrue(self.client._events['/something/else.ics'].etag is not None)
</span><span class="lines">@@ -2077,7 +2097,7 @@
</span><span class="cx"> response = MemoryResponse(
</span><span class="cx"> ('HTTP', '1', '1'), OK, "Ok", Headers({}),
</span><span class="cx"> StringProducer(""))
</span><del>- result.callback(response)
</del><ins>+ result.callback((response, ""))
</ins><span class="cx"> finished.addCallback(requested)
</span><span class="cx">
</span><span class="cx"> return d
</span></span></pre></div>
<a id="CalendarServertrunkcontribperformanceloadtesttest_profilespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/contrib/performance/loadtest/test_profiles.py (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -31,7 +31,7 @@
</span><span class="cx">
</span><span class="cx"> from twistedcaldav.ical import Component, Property
</span><span class="cx">
</span><del>-from contrib.performance.loadtest.profiles import Eventer, Inviter, Accepter, OperationLogger, AlarmAcknowledger
</del><ins>+from contrib.performance.loadtest.profiles import Eventer, Inviter, Accepter, OperationLogger, AlarmAcknowledger, AttachmentDownloader
</ins><span class="cx"> from contrib.performance.loadtest.population import Populator, CalendarClientSimulator
</span><span class="cx"> from contrib.performance.loadtest.ical import IncorrectResponseCode, Calendar, Event, BaseClient
</span><span class="cx"> from contrib.performance.loadtest.sim import _DirectoryRecord
</span><span class="lines">@@ -258,13 +258,13 @@
</span><span class="cx"> return path
</span><span class="cx">
</span><span class="cx">
</span><del>- def addEvent(self, href, vevent):
</del><ins>+ def addEvent(self, href, vevent, attachmentSize=0):
</ins><span class="cx"> self._events[href] = Event(self.serializePath, href, None, vevent)
</span><span class="cx"> return succeed(None)
</span><span class="cx">
</span><span class="cx">
</span><del>- def addInvite(self, href, vevent):
- return self.addEvent(href, vevent)
</del><ins>+ def addInvite(self, href, vevent, attachmentSize=0):
+ return self.addEvent(href, vevent, attachmentSize=attachmentSize)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def deleteEvent(self, href,):
</span><span class="lines">@@ -291,11 +291,14 @@
</span><span class="cx">
</span><span class="cx"> def changeEventAttendee(self, href, old, new):
</span><span class="cx"> if href in self.rescheduled:
</span><del>- return fail(IncorrectResponseCode(
- NO_CONTENT,
- Response(
- ('HTTP', 1, 1), PRECONDITION_FAILED,
- 'Precondition Failed', None, None))
</del><ins>+ return fail(
+ IncorrectResponseCode(
+ NO_CONTENT,
+ Response(
+ ('HTTP', 1, 1), PRECONDITION_FAILED,
+ 'Precondition Failed', None, None),
+ None
+ ),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> vevent = self._events[href].component
</span><span class="lines">@@ -851,7 +854,7 @@
</span><span class="cx"> NO_CONTENT,
</span><span class="cx"> Response(
</span><span class="cx"> ('HTTP', 1, 1), PRECONDITION_FAILED,
</span><del>- 'Precondition Failed', None, None))
</del><ins>+ 'Precondition Failed', None, None), None)
</ins><span class="cx"> )
</span><span class="cx"> accepter = Accepter(clock, self.sim, client, userNumber)
</span><span class="cx"> accepter.eventChanged(inboxEvent.url)
</span><span class="lines">@@ -1034,7 +1037,25 @@
</span><span class="cx"> # XXX Vary the event period/interval and the uid
</span><span class="cx">
</span><span class="cx">
</span><ins>+class AttachmentDownloaderTests(TestCase):
+ """
+ Tests for L{AttachmentDownloader}.
+ """
</ins><span class="cx">
</span><ins>+ 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"
+ )
+
+
</ins><span class="cx"> class OperationLoggerTests(TestCase):
</span><span class="cx"> """
</span><span class="cx"> Tests for L{OperationLogger}.
</span></span></pre></div>
<a id="CalendarServertrunkrequirementsdevtxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/requirements-dev.txt (15587 => 15588)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -4,5 +4,5 @@
</span><span class="cx"> mockldap
</span><span class="cx"> q
</span><span class="cx"> tl.eggdeps
</span><del>---editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVClientLibrary/trunk@15425#egg=CalDAVClientLibrary
</del><ins>+--editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVClientLibrary/trunk@15578#egg=CalDAVClientLibrary
</ins><span class="cx"> --editable svn+http://svn.calendarserver.org/repository/calendarserver/CalDAVTester/trunk@15551#egg=CalDAVTester
</span></span></pre>
</div>
</div>
</body>
</html>